本文由cy帘靡,wxy 共同完成面褐,排名不分先后攻柠。
0 前言
在網(wǎng)易內(nèi)部恨诱,很多部門(mén)都是以NEJ+Regular作為基礎(chǔ)庫(kù)來(lái)進(jìn)行前端開(kāi)發(fā)翻默,因此腰根,各個(gè)部門(mén)也基于此積累了很多庫(kù)新翎,組件缓醋。這也產(chǎn)生了一個(gè)問(wèn)題署鸡,一個(gè)新的項(xiàng)目如果啟動(dòng)案糙,想去使用以前的積累,就必須和以前采用相同的技術(shù)方案以及選型靴庆,那么时捌,有沒(méi)有一種方法,在新項(xiàng)目(業(yè)務(wù))啟動(dòng)時(shí)炉抒,既能兼容以往部門(mén)所沉淀的技術(shù)組件奢讨,又能夠使用新技術(shù),新方案給開(kāi)發(fā)帶來(lái)全新的感受焰薄,我們?cè)凇究ù钚@】這個(gè)產(chǎn)品做了以下的嘗試拿诸。
1 NEJ篇
在網(wǎng)易內(nèi)部,大家一定對(duì)NEJ不陌生塞茅,早期乃至現(xiàn)在很多業(yè)務(wù)都基于NEJ框架進(jìn)行開(kāi)發(fā)亩码,NEJ也包攬了模塊依賴(lài)、umi調(diào)度系統(tǒng)野瘦、platform適配描沟、構(gòu)建打包等多個(gè)功能,可以說(shuō)涵蓋了從開(kāi)發(fā)到構(gòu)建上線(xiàn)的所有生命周期,足見(jiàn)之強(qiáng)大吏廉〈缆纾可也正因?yàn)榇耍琋EJ也顯示的很重迟蜜,無(wú)法像webpack,postcss刹孔,babel等通過(guò)插件/loader,來(lái)進(jìn)行二次開(kāi)發(fā),有時(shí)候娜睛,一些新的想法和點(diǎn)子只能通過(guò)小聰明去解決髓霞。基于這樣的考慮畦戒,我們首先做了一件事方库,解耦NEJ,即NEJ代碼不需要通過(guò)自有的打包構(gòu)建工具障斋,而能被任意第三方框架/倉(cāng)庫(kù)使用纵潦。
1.1 模塊依賴(lài)
眾所周知,NEJ有屬于自己的模塊依賴(lài)系統(tǒng)垃环,即NEJ.define(),一個(gè)標(biāo)準(zhǔn)的NEJ模塊系統(tǒng)會(huì)有這樣的代碼
NEJ.define( [
'pro/{mode}/base'
'./component.js',
'text!./web/component.html',
'css!./web/component.css'
],function(Base,Button,html,css,p,o,f,r){
// ...具體邏輯
})
顯然邀层,NEJ的模塊依賴(lài)系統(tǒng)屬于AMD類(lèi)型,但是其語(yǔ)法與標(biāo)準(zhǔn)的AMD模塊相比遂庄,又更具靈活性:
- 文件依賴(lài)url支持參數(shù)寥院,可通過(guò)define.js配置:pro/{mode}/base
- 支持text,css,regular等拓展關(guān)鍵字來(lái)引入不同類(lèi)型的文件:text!./web/component.html
- 模塊中會(huì)自動(dòng)注入4個(gè)參數(shù):p,輸出結(jié)果集空間涛目;o秸谢,一個(gè)空對(duì)象;f霹肝,一個(gè)return false的函數(shù)估蹄;r,一個(gè)空數(shù)組沫换;
1.2 模塊轉(zhuǎn)化:babel插件
如果能夠找出NEJ的私有模塊依賴(lài)系統(tǒng)與標(biāo)準(zhǔn)的模塊系統(tǒng)之間的不同之處臭蚁,將NEJ的私有模塊依賴(lài)系統(tǒng)轉(zhuǎn)化為標(biāo)準(zhǔn)的AMD/CMD/CommonJS/ES6 module系統(tǒng),是不是也就意味著NEJ模塊可以被其它類(lèi)型的模塊所用苗沧?
基于此刊棕,我們決定采取編寫(xiě)Babel插件的方式來(lái)進(jìn)行NEJ module -> CommonJS module的轉(zhuǎn)化炭晒。
Babel 是 JavaScript 編譯器待逞,更確切地說(shuō)是源碼到源碼的編譯器,通常也叫做“轉(zhuǎn)換編譯器(transpiler)”网严。 意思是說(shuō)你為 Babel 提供一些 JavaScript 代碼识樱,Babel 更改這些代碼,然后返回給你新生成的代碼。
1.2.1 使用babel轉(zhuǎn)化NEJ需要解決的問(wèn)題
除了以上三點(diǎn)怜庸,babel轉(zhuǎn)化NEJ的過(guò)程中当犯,我們還需要解決如下幾個(gè)問(wèn)題:
- NEJ return && 輸出結(jié)果集空間的處理
- 依賴(lài)系統(tǒng)中循環(huán)依賴(lài)的處理
- NEJ依賴(lài)中,默認(rèn)this = window
- NEJ很多文件在嚴(yán)格模式下會(huì)報(bào)錯(cuò)割疾,因?yàn)榇嬖诤芏喾菄?yán)格寫(xiě)法嚎卫,(如 xxx.bind(undefine),非嚴(yán)格模式情況下, this指向window宏榕;而嚴(yán)格模式下this指向undefined)
在不斷的分析NEJ源碼以及嘗試下拓诸,我們小組完成了babel插件babel-plugin-transform-nej-module
來(lái)做這件事情。
1.2.2 插件效果示例
對(duì)一個(gè)常見(jiàn)的NEJ模塊文件:
// NEJ模塊
NEJ.define( [
'../component.js',
'text!./component.html',
'text!./component.css'
],function(
Component,
html,
css,
pro,
o, f, r
) {
return uxModal;
});
babel-plugin-transform-nej-module
會(huì)將其轉(zhuǎn)化為:
// CommonJS模塊
(function nejModule() {
var Component = require('../component');
var html = require('./component.html');
var css = require('./component.css');
var css = "";
var pro = exports;
var o = {};
var f = function () {};
var r = [];
module.exports = uxModal;
return;
}).call(window);
1.2.3 插件運(yùn)行剖析
插件的運(yùn)行流程圖及每一步的解釋如下:
從文件的根開(kāi)始:為什么要從根開(kāi)始麻昼?在babel插件的不同的visitor中奠支,難免會(huì)需要共享變量。這些共享變量的初始化抚芦,應(yīng)該在每個(gè)文件第一次進(jìn)入時(shí)進(jìn)行倍谜。因此選擇從根開(kāi)始處理文件,以便在文件開(kāi)始運(yùn)行前做一些初始化處理叉抡。(Babel插件處理文件的順序是并行的還是串行的尔崔,這一點(diǎn)尚有待驗(yàn)證。如果是串行的褥民,在pre階段進(jìn)行初始化更好您旁。)
取第一層節(jié)點(diǎn):JS文件被Babylon parse成一棵AST樹(shù),取該樹(shù)的第一層節(jié)點(diǎn)開(kāi)始后續(xù)處理轴捎。
為什么取第一層鹤盒?
- 在以NEJ為基礎(chǔ)的前端工程中,所有的業(yè)務(wù)代碼都包含在
NEJ.define
/define
語(yǔ)句中侦副,形成模塊侦锯。
如果NEJ模塊被包含在其他代碼塊中,那它不一定能被執(zhí)行到秦驯,則該模塊是無(wú)效的尺碰。
即使一定能執(zhí)行到,在NEJ打包時(shí)译隘,也不會(huì)將模塊外部的代碼打包亲桥。 - 如果從任意一條語(yǔ)句開(kāi)始,可能會(huì)遇到其他文件中的define函數(shù)被識(shí)別為nej模塊情況固耘。
因此题篷,我們只考慮一個(gè)文件就是一個(gè)nej模塊的情況
判斷NEJ模塊:
nej.define()
或者define()
函數(shù)被識(shí)別為NEJ模塊獲取依賴(lài)列表和回調(diào)函數(shù):處理了無(wú)依賴(lài)列表、依賴(lài)列表為空厅目、回調(diào)函數(shù)為變量的情況番枚。
進(jìn)一步尋找回調(diào)函數(shù)(當(dāng)回調(diào)函數(shù)為變量的時(shí)候):訪問(wèn)模塊內(nèi)代碼的所有賦值語(yǔ)句法严,找到對(duì)回調(diào)函數(shù)變量賦值的語(yǔ)句。
將回調(diào)函數(shù)命名為nejModule:這一步很重要葫笼。用來(lái)定位回調(diào)函數(shù)的return和輸出結(jié)果集空間的變動(dòng)深啤,并進(jìn)行跟蹤修改。
處理return:回調(diào)函數(shù)對(duì)應(yīng)的
return xxx
為module.exports = xxx; return;
處理依賴(lài):
- 初始化注入?yún)?shù)路星;
- 標(biāo)準(zhǔn)化依賴(lài)路徑溯街;
- 初始化css文件為空字符串,以兼容NEJ對(duì)css的處理洋丐,盡管這些處理在不使用define函數(shù)的時(shí)候是無(wú)效的苫幢。
處理輸出結(jié)果集空間(設(shè)為pro):將所有使用到
pro
的代碼替換為exports
,(不直接module.exports = pro垫挨,因?yàn)樾枰幚硌h(huán)依賴(lài))保證模塊內(nèi)this指向window:使用自執(zhí)行函數(shù)將全部語(yǔ)句包裝起來(lái)韩肝。
1.2.4 方案不足之處
- 尚存在兩種罕見(jiàn)的異常未做處理:
-
text!
方式引入的css文件為空字符串,實(shí)際應(yīng)該是css文件的內(nèi)容; - 輸出結(jié)果集空間被直接修改為其它對(duì)象;
- NEJ對(duì)于不同平臺(tái)的適配處理:
-
{platform}/element.js
被轉(zhuǎn)化為./platform/element.js
九榔,實(shí)際應(yīng)當(dāng)同時(shí)引入./platform/element.patch.js
如果你發(fā)現(xiàn)了任何其它異常情況哀峻,請(qǐng)一定要提出issue或直接聯(lián)系我們,我們將會(huì)在最快時(shí)間內(nèi)解決
1.3 與webpack的結(jié)合
NEJ模塊轉(zhuǎn)化為標(biāo)準(zhǔn)Common JS模塊后哲泊,還需要再兩項(xiàng)額外配置剩蟀,來(lái)保證與webpack的結(jié)合使用:
- 將NEJ的路徑參數(shù)配置為weppack別名,以下是已知的NEJ路徑參數(shù)配置:
module.exports = {
...
resolve: {
alias: {
'base': resolve('lib/nej/src/base'),
'lib': resolve('lib/nej/src'),
'ui': resolve('lib/nej/src/ui'),
'util': resolve('lib/nej/src/util')
}
}
...
}
- 在插件中配置非前綴的路徑參數(shù)切威、以及嚴(yán)格模式的去除:
"plugins": [
...
"transform-remove-strict-mode",
[
"transform-nej-module",
{
"mode": "web"
}
],
///
],
2 Regular篇
說(shuō)完如何處理NEJ育特,現(xiàn)在我們來(lái)分析一下,如何處理部門(mén)所沉淀的regular組件庫(kù)先朦?
我們所在的部門(mén)從16年起缰冤,開(kāi)始搭建了一套用regular編寫(xiě)的組件庫(kù),大大小小封裝了上百個(gè)模塊及組件喳魏,為各條產(chǎn)品線(xiàn)提供支持棉浸。
眾所周知,vue template和regular template是不同DSL語(yǔ)法的模板技術(shù)刺彩,兩個(gè)框架擁有各自的語(yǔ)法迷郑,
在框架層面,如何能最小程度的兼容regular組件成了我們需要優(yōu)先考慮的問(wèn)題创倔。
2.1 原理探究
在regularjs中嗡害,組件被拆分為了 模板template + 數(shù)據(jù)data + 業(yè)務(wù)邏輯(實(shí)例函數(shù))的組合。也就是說(shuō)畦攘,只要滿(mǎn)足上述三個(gè)條件霸妹,就可以實(shí)例出一個(gè)regular組件。
regular組件有兩種使用方式念搬,一種是直接實(shí)例化抑堡,一種是標(biāo)簽式。
1.對(duì)于直接使用new方法實(shí)例出來(lái)的組件朗徊,將所需要數(shù)據(jù)傳入即可首妖,注意要及時(shí)銷(xiāo)毀。
2.對(duì)于標(biāo)簽式組件爷恳,就必須要有一個(gè)地方來(lái)承載regular模版有缆,于是想到可以通過(guò)注冊(cè)一個(gè)通用的vue組件,拿到regularjs模版温亲,傳入數(shù)據(jù)與邏輯棚壁,在該組件中進(jìn)行實(shí)例化,從而達(dá)到承載regular組件的效果栈虚。
2.2 開(kāi)始設(shè)計(jì)
我們的目的就是設(shè)計(jì)并實(shí)現(xiàn)這樣一個(gè)RegularComponent(簡(jiǎn)稱(chēng)RC)組件袖外。
首先遇到的問(wèn)題是如何獲取標(biāo)簽內(nèi)的模版?
我們可以利用vue插槽
業(yè)務(wù)組件
// 業(yè)務(wù)組件模版
<rc ref="rc" :revent="REvent" :rdata="RData" :rfilter="RFilter">
<ux-button value={name} on-click={this.clickBtn($event)}></ux-button>
</rc>
RC組件
// RC組件模版
<div ref="rc">
<slot/>
</div>
這樣就可以在RC組件中,拿到
-
this.$slots
模版 -
this.rdata
數(shù)據(jù) -
this.revent
業(yè)務(wù)邏輯 -
this.rfilter
過(guò)濾器
這里需要注意魂务,this.$slots
直接獲取到的是vue規(guī)范的AST曼验,下面稱(chēng)為VNode,我們可以封裝一個(gè)方法將VNode轉(zhuǎn)為模版字符串粘姜,再去執(zhí)行實(shí)例化regular的行為鬓照。
let attrStringify = obj => {
let ret = '';
for (let i in obj) {
if (obj[i]) {
ret += ' ' + i + '="' + obj[i] + '"';
} else {
ret += ' ' + i;
}
}
return ret;
};
// 拼裝模版
let formatTpl = arr => {
let tpl = '';
arr.forEach(item => {
if (item.text) {
tpl += item.text;
} else if (item.tag) {
tpl += '<' + item.tag;
// 組裝attr
if (item.data) {
tpl += attrStringify(item.data.attrs) + '>';
} else {
tpl += '>';
}
// 組裝子節(jié)點(diǎn)
if (item.children) {
tpl += formatTpl(item.children);
}
// 閉合組件標(biāo)簽
tpl += '</' + item.tag + '>';
} else if (item.children) {
tpl += formatTpl(item.children);
}
});
return tpl;
};
拿到這些數(shù)據(jù),就可以生成reuglar組件了孤紧。
到了這一步豺裆,還沒(méi)有完全實(shí)現(xiàn)我們想要的效果,因?yàn)関ue插槽會(huì)將內(nèi)容分發(fā)至子組件号显,查看源碼可以看到模版代碼直接展示在頁(yè)面上臭猜;而且模版中的標(biāo)簽式組件是在regular中注冊(cè)的,直接使用會(huì)報(bào)錯(cuò)押蚤,這是不希望看到的获讳。
[Vue warn]: Unknown custom element: <ux-button> - did you register the component correctly? For recursive components, make sure to provide the "name" option.
不過(guò)修復(fù)這個(gè)問(wèn)題并不困難,只需隱藏這個(gè)插槽即可活喊。
<div ref="rc">
<slot v-if="false" />
</div>
現(xiàn)在組件實(shí)例化好了丐膝,假設(shè)需要異步獲取數(shù)據(jù),同步至regular組件钾菊,要如何更新呢帅矗?
在regular中,我們一般在接口請(qǐng)求回調(diào)中煞烫,將返回的數(shù)據(jù)賦值到this.data上浑此,然后調(diào)用regular的update方法來(lái)觸發(fā)一輪臟檢查來(lái)同步數(shù)據(jù)。
在使用RC時(shí)滞详,當(dāng)vue組件中RData發(fā)生變化時(shí)凛俱,會(huì)自動(dòng)同步至RC組件紊馏,但regular組件實(shí)例化完成之后,不會(huì)繼續(xù)更新數(shù)據(jù)蒲犬。我們只要在RC組件內(nèi)部監(jiān)聽(tīng)這個(gè)對(duì)象朱监,再去更新regular組件內(nèi)的數(shù)據(jù)即可,這個(gè)流程可以用下圖表示:
2.3 注意事項(xiàng)
1.目前已知regular模版中雙大括號(hào)會(huì)被編譯原叮,導(dǎo)致返回錯(cuò)誤的AST赫编,可以在最外層加上v-pre
,若有使用r-class奋隶、r-style就必須加上擂送,其他場(chǎng)景最好也加上
示例
<rc ref="rc" :revent="REvent" :rdata="RData" :rfilter="RFilter">
<div v-pre>
<ux-button value={name} on-click={this.clickBtn($event)}></ux-button2>
</div>
</rc>
2.需要注意函數(shù)中this的指向,vue事件指向vue唯欣,regular事件指向regular
3.在regular模版中嘹吨,若在逗號(hào)后面包含空格,如 on-click={this.xxx(item, $event)}
境氢,會(huì)導(dǎo)致AST格式出錯(cuò)躺苦,解決方案是把空格去掉,或者大括號(hào)外加上引號(hào)
2.4 小結(jié)
為了在vue中支持regular产还,我們可以注冊(cè)一個(gè)RC組件來(lái)承載匹厘,它是一個(gè)vue組件,通過(guò)vue slot(插槽)獲取模版脐区,綁定data愈诚、event、filter牛隅,動(dòng)態(tài)生成regular組件插入到視圖層炕柔,從而支持了在vue組件中直接編寫(xiě)regular代碼的功能。
2.5 該方案的不足
因?yàn)槲覀兊捻?xiàng)目是采取weex控制組件的各個(gè)state媒佣,即單向數(shù)據(jù)流來(lái)控制組件中的數(shù)據(jù)匕累,但是在目前已實(shí)現(xiàn)的組件池基本已被regular雙向綁定給限制了,雖然也可以在watch中觸發(fā)commit,dispatch,可這也無(wú)疑會(huì)將邏輯打亂默伍,因此欢嘿,針對(duì)這部分代碼,我們大多沒(méi)有采用weex管理狀態(tài)也糊,而交由組件自身去維護(hù)炼蹦,有時(shí)候享受不了單向數(shù)據(jù)流帶來(lái)的便利性。
3 構(gòu)建部署篇
我們可以看到狸剃,我們通過(guò)Babel將完成了對(duì)NEJ模塊依賴(lài)的解耦掐隐,通過(guò)Vue組件代理,完成了regular組件上的兼容钞馁,最后就是去解決如何上線(xiàn)部署了
3.1構(gòu)建
3.1.1問(wèn)題
1.【卡搭校園】這個(gè)項(xiàng)目虑省,后端還是按照以往的邏輯匿刮,將一些后端數(shù)據(jù)直接寫(xiě)到ftl模板之中。也就是說(shuō)探颈,最后上線(xiàn)熟丸,我們?nèi)匀灰蕾?lài)freemarker去解析數(shù)據(jù)。但是膝擂,我們?cè)谑褂脀ebpack構(gòu)建開(kāi)發(fā)環(huán)境之時(shí)虑啤,其本質(zhì)是不支持渲染java模板的隙弛。因此架馋,在構(gòu)建環(huán)境,我們要區(qū)分開(kāi)發(fā)狀態(tài)以及上線(xiàn)打包狀態(tài)全闷,提供不同的代碼用來(lái)兼容不同平臺(tái)(node or html/ java)
- 靜態(tài)資源路徑叉寂,NEJ-toolkit已經(jīng)幫忙實(shí)現(xiàn)了類(lèi)似靜態(tài)資源地址前綴添加,切換到webpack后总珠,不交給webpack打包的js屏鳍,css,img等資源文件默認(rèn)是不會(huì)自動(dòng)加前綴的,這塊也要想辦法進(jìn)行兼容局服。
3.1.2 問(wèn)題解決
先來(lái)看看第一個(gè)問(wèn)題钓瞭,在webpack-server中,默認(rèn)是通過(guò)HtmlWebpackPlugin插件生成自動(dòng)生成html淫奔,同時(shí)山涡,該插件可以兼容ejs語(yǔ)法對(duì)各種環(huán)境進(jìn)行定制。那么唆迁,我們也可以通過(guò)他自動(dòng)生成ftl模板鸭丛。webpack配置如下
let filename = filePath.substring(filePath.lastIndexOf('\/') + 1, filePath.lastIndexOf('.'));
let conf = {
// 模板來(lái)源
template: '!!ejs-compiled-loader!' + filePath,
// 文件名稱(chēng)
filename: filename + '.html',
// 頁(yè)面模板需要加對(duì)應(yīng)的js腳本,如果不加這行則每個(gè)頁(yè)面都會(huì)引入所有的js腳本
chunks: ['manifest', 'vendor', filename],
inject: true
};
// 生產(chǎn)環(huán)境轉(zhuǎn)為ftl
if (process.env.NODE_ENV === 'production') {
conf = merge(conf, {
filename: filename + '.ftl',
env: 'production',
minify: {
removeComments: true,
collapseWhitespace: true,
removeAttributeQuotes: true
},
chunksSortMode: 'dependency'
});
}
對(duì)應(yīng)的html模板如下所示
<% if(htmlWebpackPlugin.options.env === 'production'){ %>
<!--生產(chǎn)環(huán)境-->
<script>
<#if webUser?exists>
window.webUser = {
<#if webUser.id?exists>id: "${webUser.id?html}",</#if>
end_key: "end_value"
};
</#if>
</script>
<%} else{%>
<!--開(kāi)發(fā)環(huán)境-->
<script>
window.webUser = {
"id": 123456
};
</script>
<%} %>
因?yàn)樵趙ebpack插件中唐责,我們指定了template通過(guò)ejs-loader來(lái)渲染鳞溉,因此我們通過(guò)參數(shù)傳遞到html中,通過(guò)不同的環(huán)境模式鼠哥,來(lái)切換開(kāi)發(fā)環(huán)境以及部署到服務(wù)器與后端渲染的ftl模板熟菲,后期如果有新的后端數(shù)據(jù)需要渲染到ftl上,只需要維護(hù)自己的template模板即可朴恳。這樣處理也帶來(lái)了另外一個(gè)好處科盛,我們?cè)趆tml模板會(huì)使用類(lèi)似如下的語(yǔ)法
<body>
<div id="app"></div>
<% include src/pages/common/footer.html %>
</body>
插件最后生成ftl時(shí)會(huì)將所有片段組裝到一起,避免了ftl語(yǔ)法 <#include> 部署后路徑菜皂,資源以及模板被緩存等問(wèn)題
第二個(gè)問(wèn)題即靜態(tài)資源服務(wù)器替換問(wèn)題贞绵,正常情況下,webpack打包的js||css等都可以通過(guò)配置output添加對(duì)應(yīng)的靜態(tài)資源路徑恍飘,但是如果出現(xiàn)在HtmlWebpackPlugin模板中的靜態(tài)資源默認(rèn)是不會(huì)添加榨崩,我們不可能在開(kāi)發(fā)或者部署的時(shí)候手動(dòng)添加對(duì)應(yīng)的前綴來(lái)滿(mǎn)足業(yè)務(wù)上的需求谴垫,因?yàn)闆](méi)有找到合適的工具,因此母蛛,在自動(dòng)切換層翩剪,我們手動(dòng)編寫(xiě)了webpack插件來(lái)進(jìn)行解決。
解決思路如下
通過(guò)監(jiān)聽(tīng) html-webpack-plugin-before-html-processing 對(duì)輸出的html進(jìn)行處理彩郊,如果發(fā)現(xiàn)他沒(méi)有帶靜態(tài)資源路徑名前弯,則給其增加前綴,示例代碼如下
function AddStaticServer (options) {
let option = options || {
serverPath: '//kc.stu.126.net'
};
this.serverPath = option.serverPath;
}
AddStaticServer.prototype.apply = function (compiler) {
compiler.plugin('compilation', (compilation) => {
compilation.plugin(
'html-webpack-plugin-before-html-processing',
(data, cb) => {
data.html = generatehtml(data,html)
cb(null, data);
}
);
});
};
webpack對(duì)應(yīng)配置
plugins:[
.....,
.....,
new AddStaticServerPlugin({
serverPath: config.build.assetsPublicPath
})])
]
3.1.3 該方案的不足
在前文解決方案中提到秫逝,我們?yōu)榱藚^(qū)分線(xiàn)上ftl文件以及前端html文件恕出,需要針對(duì)性的編寫(xiě)模板,并且通過(guò)配置违帆,當(dāng)遇到通用ftl數(shù)據(jù)過(guò)多的情況下浙巫,需要做很多的額外配置以及編寫(xiě),項(xiàng)目到后期的話(huà)也可能會(huì)越來(lái)越大刷后,因此的畴,在我們的項(xiàng)目中,除了約定必須的model數(shù)據(jù)外尝胆,其他都通過(guò)異步接口進(jìn)行獲取丧裁,這樣也是為了減少后期可能SSR的成本
3.2開(kāi)發(fā)數(shù)據(jù)mock
原有項(xiàng)目中,我們可以通過(guò)網(wǎng)易內(nèi)部的NEI平臺(tái)來(lái)進(jìn)行接口mock甚至是ftl的渲染含衔,而在新的工程中煎娇,因?yàn)殚_(kāi)發(fā)環(huán)境的切換,不在使用ftl作為承載頁(yè)面進(jìn)行抱慌,因此逊桦,需要尋找其他方式進(jìn)行Mock,
好在webpack-dev-server能夠提供這樣的能力我們可以配置proxy來(lái)對(duì)接口進(jìn)行代理抑进,示例代碼如下所示
devServer: {
proxy: {
'/api': 'http://localhost:8002/',
'/j':'http://localhost:8002/',
}
}
nei默認(rèn)會(huì)在8002下啟動(dòng)mock服務(wù)器强经,因此,在開(kāi)發(fā)環(huán)境中寺渗,我們可以像以往一樣匿情,使用nei平臺(tái)對(duì)接口和數(shù)據(jù)模型進(jìn)行管理,最后我們只要將webpack-dev-server的接口代理到nei下即可信殊。同樣炬称,我們也可以在開(kāi)發(fā)環(huán)境中隨時(shí)更換成線(xiàn)上服務(wù)器(需要解決csrf以及cookie的問(wèn)題),用本地訪問(wèn)真實(shí)的數(shù)據(jù)也成為了可能涡拘。
總結(jié)
目前玲躯,該套方案已經(jīng)成功運(yùn)行上線(xiàn)了一段時(shí)間,也得到了組內(nèi)同學(xué)的認(rèn)可,不過(guò)跷车,依舊還是有許多地方可以?xún)?yōu)化棘利,比如
- ftl是否可以去掉,因?yàn)槟壳霸摲桨敢槍?duì)本地開(kāi)發(fā)環(huán)境與線(xiàn)上部署環(huán)境生成不同的代碼朽缴,會(huì)有一定的開(kāi)發(fā)開(kāi)銷(xiāo)善玫,如果去掉類(lèi)似登錄狀態(tài)該如何判斷。
- webpack所構(gòu)建的sourcemap斷點(diǎn)會(huì)出現(xiàn)一到兩行偏差密强,沒(méi)有純?cè)创a調(diào)試那么方便
- 目前babel插件處理了 css,text,regular等NEJ插件茅郎,如果未來(lái)NEJ又有新的關(guān)鍵字插件,需要一起更新或渤,防止轉(zhuǎn)化失敗系冗。
- VUE代理的Regular組件,需要不斷的監(jiān)聽(tīng)事件劳坑,才可以兼容以前的代碼并完成單向數(shù)據(jù)流操作毕谴。
如果大家對(duì)這方案有興趣成畦,并且有更好的建議距芬,歡迎隨時(shí)聯(lián)系我哦~