0x1 讀完后希望你會
- 了解Weex中,we腳本的各個標(biāo)簽<template><script><style>解析實現(xiàn)方式,如何生成可以運(yùn)行的js腳本文件。
- 基于標(biāo)簽tag,自己實現(xiàn)一套你喜歡的類似we或者微信小程序的腳本語言。
- 可以包含表達(dá)式計算
- 流程控制
0x2 背景
目前線上App接入的weex版本為0.8.X,距目前最新的0.10.X系列跨度相差較大,但是在保證目前業(yè)務(wù)穩(wěn)定的前提下,不能直接升級Weex SDK到最新版。
在升級前期,不得不面對的幾個問題:
- 目前已開發(fā)的大量.we頁面是否可以在新版Weex運(yùn)行。
- 繼續(xù)使用已被廢棄的we腳本開發(fā)新需求(Weex已經(jīng)推薦用vue來寫頁面)
- Vue與Weex兩大生態(tài)的聯(lián)手,Weex官方也在推進(jìn)vue來編寫界面.
- we腳本將來很有可能面臨不再被擴(kuò)展維護(hù),也就是weex很多的新特性在we中不一定會被支持,除非we與vue的feature更新節(jié)奏保持一致。
- 據(jù)說手淘與貓客已不再使用we編寫weex,而是全部采用vue(求證)。
- Vue自身生態(tài)龐大,對vue更好的支持,反而更利于weex的推廣。
所以在升級之前,以weex的transform流程原理為開端,展開一些前期的調(diào)研工作,順便學(xué)習(xí)一下weex的toolkit實現(xiàn)原理。
據(jù)weex的同學(xué)說新版的weex是可以跑老we的,不過要保證we腳本代碼的嚴(yán)謹(jǐn)性,因為新版的jsf runtime校驗機(jī)制,更加嚴(yán)格,某些頁面可能白屏(What??。。?。
老話常談,“Talk is cheap,Show me the code”。
0x3 驗證思路
一個we頁面被渲染執(zhí)行,可以大致拆分為兩部分。
【.we腳本的打包與編譯】(weex/vue loader,生成標(biāo)準(zhǔn)的js腳本)
[.we] -> [build] -> output [*.js]
【W(wǎng)eex JS 運(yùn)行時環(huán)境(JSF)】(負(fù)責(zé)解析運(yùn)行編譯好的js腳本)
[*.js] -> [weex runtime] -> render [view:page]

從圖中可以看出 Weex 整體的工作流程。
首先開發(fā)者編寫 .we 文件。
通過 weex-toolkit 提供的工具將 .we 文件轉(zhuǎn)為js。
JS Framework 接收并執(zhí)行 js 的代碼,執(zhí)行數(shù)據(jù)綁定、模板編譯等操作,然后輸出 json 格式的 Virtual DOM 傳遞給移動端。
這篇文章主要分析第一層的transformer的實現(xiàn)原理。
0x4 we/vue 腳本是如何被解析的
不可不說的parse5:
weex-loader中,對we文件的解析主要依賴第三方開源npm組件,html標(biāo)簽解析的parse5,對輸入的html標(biāo)簽類文本,解析后輸出json對象。
關(guān)于輸出的json格式,可以參考官方的在線playground,http://astexplorer.net/#/1CHlCXc4n4
weex-loader首先通過parse5 得到we文本的json 結(jié)構(gòu)的樹結(jié)構(gòu),
然后Weex-loader的處理流程中,針對不同標(biāo)簽(<template/> <script/><style/>),分別有對應(yīng)的解析處理模塊,

大致流程如上圖,從左向右依次為:
1.輸入為原始we腳本文件。
2.通過parse5組件解析出對應(yīng)的jsonobject
3.根據(jù)json object中描述的各部分tag交給對應(yīng)的處理模塊。
例如一個template標(biāo)簽的json對象,在loader中是這樣被處理的,

源碼地址: weex-loader/lib/loader.js
不同的標(biāo)簽類型文本會分配給專門的parse組件.

源碼地址:weex-loader/lib/parser.js
標(biāo)簽對應(yīng)的處理模塊如下:
<script> 標(biāo)簽 ————> weex-templater
<style > 標(biāo)簽 ————> weex-styler
<script> 標(biāo)簽 ————> weex-scripter
0x5 weex-styler 簡介以及CSS預(yù)備知識
weex-styler負(fù)責(zé)處理weex所支持的css樣式,以及驗證開發(fā)者所寫的css樣式是否正確。
其中實現(xiàn)原理是使用開源css https://www.npmjs.com/package/css 組件,根據(jù)css代碼的文本段,生成json object類型的 ast節(jié)點信息。要了解weex-styler的處理流程,勢必要了解一下ast以及css的基本概念。
一張圖看懂CSS構(gòu)成.
A CSS rule-set consists of a selector and a declaration block:

rule-set:包含一個selector 以及多個declaration。
selector:樣式選擇器,主要用來定位元素
declaration:定義樣式屬性以及值。
了解這三個基本概念之后,看scripter也是輕車熟路了,快上車。
scripter當(dāng)中,通過css parser得到AST json object,讀取ast.stylesheet.rules獲得到當(dāng)前樣式的rule-set,然后遍歷所有的declaration,校驗樣式是否被weex所支持的(因為weex中支持css的樣式有限),以及簡單的value字段合法性校驗。
![Uploading Paste_Image_374004.png . . .]
源碼:/weex-styler/index.js
weex-styler新老版之差異。
對Pseudo class樣式進(jìn)行了支持。

新版weex中,支持shorthand writing寫法的transition樣式。
// shorthand writing
div {
transition: width 2s linear 1s;
}
div {
transition-property: width;
transition-duration: 2s;
transition-timing-function: linear;
transition-delay: 1s;
}
0x6 weex-templater 簡介
解析we腳本中的<template/>標(biāo)簽內(nèi)容,其核心節(jié)點數(shù)據(jù)結(jié)構(gòu)也是基于parse5解析出的json object,同時也會做數(shù)據(jù)綁定,標(biāo)簽驗證,自動修復(fù)common錯誤的處理。
<template>
<div if={{x}}>
<text onclick="toggle">Toggle: {{result}}</text>
</div>
</template>
- 數(shù)據(jù)綁定:
<text>標(biāo)簽內(nèi)的value,”Toggle: {{result}}”,
<div>標(biāo)簽內(nèi)的attribute if 中的 “{{x}}”,
也就這種用戶可以編輯的文本段,需要對包含的表達(dá)式以及變量進(jìn)行處理,這里的實現(xiàn)就在var exp = require('./exp’)這個模塊當(dāng)中,
該exp函數(shù)有2個參數(shù),需要轉(zhuǎn)換的字符串文本,以及是否需要轉(zhuǎn)換成function對象。
![Uploading Paste_Image_402398.png . . .]
/weex-templater/lib/exp.js
將雙引號“替換為單引號’,去除所有\(zhòng)n換行控制字符。
判斷文本中是否包含表達(dá)式,如果不包含,直接返回原始文本。
根據(jù)生成的token列表,
文本會在兩端加入單引號’
表達(dá)式在兩端(),顯式的加入運(yùn)算優(yōu)先級。
比如Toggle: {{result}} => [‘\’Toggle:\’’,’(result)’]
這樣在最后,只需要把結(jié)果列表中的所有元素做一次’+’.join操作,就可以構(gòu)成了一個合法的js語句。
- 事件綁定:
text中的onclick屬性的值,templater會自動生成可以調(diào)用toggle函數(shù)的js代碼。當(dāng)標(biāo)簽的屬性名包含on前綴時,將進(jìn)行事件綁定。

weex-templater/index.js
在checkEvent方法中,通過把封裝好的function描述字符串eval對象賦值給value,達(dá)到事件發(fā)生時觸發(fā)函數(shù)的機(jī)制。
- 對template的內(nèi)容,對元素標(biāo)簽(如<div/><a/><img/等>),控制語句(if,else,repeat等)做validation,針對不同標(biāo)簽,會有額外特殊的驗證操作。
下面舉幾個典型例子,
- 【If】 validation: <if={{x == 1}}> 驗證if中包含的value,以及value是否是一個合法的表達(dá)式。

- 【else】 validation:當(dāng)遍歷到一個節(jié)點中包含else時,就會驗證前一個節(jié)點中是否包含了if。
- Tag(標(biāo)簽)validation:
Tag的驗證相對多一點,因為不同的tag會有其特殊的規(guī)則,
例如最外層的<template/>只能包含一個root子節(jié)點。
container類型的標(biāo)簽可以嵌套標(biāo)簽,非container則不可。
<cell>標(biāo)簽可以自動補(bǔ)全tree屬性。 - <text>標(biāo)簽比較特殊,因為text里面可以包含任意的字符串,變量,表達(dá)式,
例如下面的腳本。
- 修復(fù)一些簡單的common問題。
舉個例子,比如圖像內(nèi)容,既可以寫成<img>也可以寫成<Image>,在templater中是有處理的。

源碼地址:weex-templater/lib/validator.js