anu小程序

由於小程序長得像vue(指它的template指令風格),因此早期的轉譯器都是vue系的。而wept與mpvue帶來的問題不比它們解決的問題少。於是社區繼續造輪子。人們發現小程序的虛擬DOM機制其實是來自react,setData, forceUpdate API也是很好的證據。因此react風格的轉譯器興起了,最出名的代表是taro,但taro是個早產兒,BUG非常多。因此我們公司幾經考慮後,自己開發自己的小程序工具。像之前的文章所言,這麼多難點,社區上已經有對應的解決方案,剩下的問題是如何整合成一個順手的框架。

anu小程序背靠anujs這個實現精良的迷你框架,依仗群裡的已有代碼,經過著兩個月的努力,披荊斬棘,臻於完美。下面向大家展示這偉大成果。

小程序的最大問題是沒有組件繼承機制,它所謂的自定義組件就是一個Component,不能指定父類。換言之,就是20年前的面向配置編程。因此大家都是設法引用vue/react這樣廣為流行的組件機制。

其次,小程序的模板機制也非常弱,限制很多,雖然有點像vue,但它的數據都會經過JSON.stringify過濾,因此不能傳函數,如何突破這個問題是難點。

我們看第一個問題,小程序的JS文件基本上分為4類,app.js,pages目錄下的JS文件,components目錄下的JS文件,及其他目錄下的JS文件。為了方便區分,anu依次識別為App類,Page類與Componet類,它們都是轉譯器處理的對象。其他目錄下的JS只會做拷貝,不轉譯。

App類,Page類與Componet類都應該用React定義組件的方式實現的。

App類就是app.js這一個文件,用於抽取依賴,構建app.json。下面是用戶編寫的app.js,及轉譯後的app.js與app.json。app.json用戶不需要編寫。事實上,小程序的各種配置用的json,我們的轉譯器全包了。這樣更符合我們react的開發方式。

anu小程序

anu小程序

anu小程序

Page類是定義在pages目錄下的React組件,anu轉義器會將它的es6 類風格變成React.miniCreateClass定義的類,因為es6 類轉換成es5形式時,babel會添加這麼多輔助方法,導到頁面體積過大,這是對體積拮据的小程序來說是不可容忍的。

下面是pages/index/index.js轉譯的例子:

anu小程序

轉譯後,生成三個文件

anu小程序

anu小程序

anu小程序

Componet類是components目錄下的React組件。小程序有兩種定義組件的方式,template組件與自定義組件。anu轉譯器會將Componet類轉譯成template組件,並且在組件更新時,收集它的props, state, context到Page類組件的props中。最後由Page類進行統一的setData,從而減少頁面的刷新頻率。

anu小程序

Animal組件轉換成兩個文件,留意它的wxml都是包在一個template標籤中。

anu小程序

anu小程序

通過以React組件開發方式,我們把本來應該寫兩三個文件的繁鎖開發方式,改成一個文件。當然,如果你引入了xxx.less, xxx.sass,anu小程序還會在對應目錄下當成wxss文件。此外帶來的好處還有,你不增加插件與配置的情況下,在原開發工具上(vscode, webstrom)享受語法高亮與提示。

第二個問題是模板數據的突破,之所以不用小程序後來推出的自定義組件機制,而是使用template標籤,是因為template能全量接受上方傳下來的所有數據。而自定義組件需要在properties對象定義了才能接受。這與React差距太多了。

小程序的Page類的wxml不是能使用函數來操作數據的,當然你可以用wxs,但wxs太雞肋。因此我們的方案是在React的虛擬DOM中diff好,統一提交到Page類的props中。這就是anu小程序的雙模板機制。它同時擁有被編譯了並加料的JSX模板,及經過優化的wxml。用戶在編寫組組件的JSX時,與寫普通的React程序沒什麼兩樣,但為了突破小程序的限制,我們在這個JSX上加入這麼多隨機數,它們是各種UUID。比如說,我們會在用戶定義了onTap事件的標籤上添加三個UUID。

anu小程序

轉譯後的第一個模板:

anu小程序

data-class-code對應的類的UUID, data-insance-code對應的是實例的UUID,data-tap-fn對應的事件UUID。onTap在JSX中還是原來的方法。

anu小程序

這是轉譯後的第二個模板,wxml用的模板。當我們跑小程序時,會先把所有react組件跑一次,理所當然,JSX(第一個模板)也跑起來。當它們跑起來了,事件回調就會存到實例上,實例就會存到類.

然後我們點擊wxml, 它運行的是dispatchEvent這個統一的委託者,它會從事件對象中找到target,然後再找到dataset,裡面包含了{tapFn: '89136908', classCode: 'c5552349481165', instanceCode: "234324324" }。然後我們就能通過類的UUID找到類,再通過實例的UUID從類中拿到實例,再通過事件的UUID從實例中拿到事件,最後觸發事件。由於事件在JSX中已經bind了this與參數,因此就解決傳參問題,不管這參數是簡單類型還是複雜類型。像taro,它只能傳簡單類型,並且只能bind一次。而anu小程序,隨著JSX的diff,會同步最新的數據。

對於樣式的單位處理與複雜的數據計算,涉及到函數問題,我們也是這麼幹。因此說anu小程序的雙模板機制是一個聰明的實現,輕鬆繞開了小程序鱉足的設計。

對於mpvue最為詬病的問題,因為setData過於頻繁導致性能底下的問題,anu是將所有數據更新上交到Page類進行setData。

其他方面,我們實現對無狀態組件的支持,支持並行構建用戶代碼,支持動態添加wx:key,支持智能省略wx:if分支(因為if分支已經在JSX中進行過濾一次,數據都已經剪枝),支持複雜if, for語法的轉譯,比市面上其他React轉小程序更加強悍。

歡迎試用

RubyLouvre/anu

轉載來自知乎 https://zhuanlan.zhihu.com/p/42788287


分享到:


相關文章: