從 Wepy 到 UniApp 變形記( 四 )

uniapp的 App.vue 可以定義小程序生命周期方法,globalData全局數據對象 , 以及一些自定義方法,核心代碼實現如下:
<script>export default {globalData: {text: 'text'}onLaunch: function() {console.log('App Launch,app啟動')},onShow: function() {console.log('App Show,app展現在前臺')},onHide: function() {console.log('App Hide,app不再展現在前臺')},methods: {// .....}}<script>5.2.2 核心轉換設計

從 Wepy 到 UniApp 變形記

文章插圖
如圖,核心轉換設計流程:
  1. 對 app.py 進行 parse,拆分出script和style部分,對script部分使用babel進行parse生成AST 。
  2. 通過對 AST 分析出,小程序的生命周期方法,globalData全局數據,自定義方法等 。
  3. 對于AST進行uniapp轉換,生命周期方法和全局數據轉成對象的方法和屬性,對自定義方法轉換到method內 。
  4. 其中對 globalData 的訪問,要進行替換通過 getApp()進行訪問 。
  5. 抽取 ast 中的 config 字段,輸出到 app.json 配置文件 。
  6. 抽取 wepy.config.js 中的 config 字段 , 傳入 wepy 的 app 實例 。
核心代碼實現:
let APP_EVENT = ['onLaunch', 'onShow', 'onHide', 'onError', 'onPageNotFound']//....// 實現wepy app到uniapp App.vue的轉換t.program([...body.filter((node: t.Node) => !t.isExportDeclaration(node)),// 插入appClass...appClass,...body.filter((node: t.Node) => t.isExportDeclaration(node)).map((node: object) => {// 對導出的app進行處理if (t.isExportDeclaration(node)) {// 提前config屬性const { appEvents, methods, props } = this.clzProperty// 重新導出vue style的對象return t.exportDefaultDeclaration(t.objectExpression([// mixins...mixins,// props...Object.keys(props).filter((elem) => elem !== 'config').map((elem) =>this.transformClassPropertyToObjectProperty(props[elem])),// app events...appEvents.map((elem) =>this.transformClassMethodToObjectMethod(elem)),// methodst.objectProperty(t.identifier('methods'),t.objectExpression([...methods.map((elem) =>this.transformClassMethodToObjectMethod(elem)),])),]))}return node}), ])// ..... 5.2.3 痛點難點在運行期,app.wpy 會繼承 wepy.App 類 , 這樣就會在運行期和 wepy.App 產生依賴關系,怎么最小化弱化這種關系 。抽取wepy的最小化以來的polyfill,隨著業務中代碼剔除對wepy的api調用,最終去除對polyfill的依賴 。
5.3 wepy component 轉換對于wepy component 的轉換主要可以細化到對 component 中 template、script、style 三部分代碼塊的轉換 。
其中,style 部分由于已經兼容 Vue 的規范 , 所以我們無需做額外處理 。而 template 模塊主要是需要對 wepy template 中特殊的標簽、屬性、事件等內容進行處理 , 轉化為適配 uni的template,上文做了詳細的說明 。
我們只需要專注于處理 script 模塊的代碼轉換即可 。從架構設計的思路來看,component script 的轉換主要是是做以下兩件事:
  1. 編譯期可確定代碼塊的轉換 。
  2. 運行期動態注入代碼的兼容 。
wepy-component-transform 就是基于以上這兩個標準設計出來的實現轉換邏輯的模塊 。
5.3.1 差異性梳理首先先解釋一下什么是“編譯期可確定代碼塊”,我們來看一個 wepy 和 Vue 語法對比示例:
從 Wepy 到 UniApp 變形記

文章插圖
從直觀上來說,這個 script 的模板的語法大致和 Vue 語法類似,這意味著我們解析出來的 AST 結構和 Vue 文件對應的 AST 結構上類似 , 基于這一點來看編譯轉換的工作量大致有底了 。
從細節來看,wpy 文件script 模塊中的 API 語法和 Vue 中有聲明及使用上的不同,其中包含:
  1. wepy 自身的包依賴注入及運行時依賴
  2. props/data/methods 聲明方式不同
  3. 生命周期鉤子不同
  4. 事件發布/訂閱的注冊和監聽機制不同 。
  5. ....等等
為了確定這個第5點等等還存在哪些使用場景,我們需要對 wepy 自身的邏輯和玩法有一個詳盡的了解和熟悉,通過在團隊內組織的 wepy 源碼走讀,再結合wepy 實際生產項目中的代碼相互印鑒 , 我們最終才將 wepy 語法邏輯與 uni-app Vue 語法邏輯的異同梳理清楚 。
5.3.2 核心轉換設計我們簡單梳理一下 wepy-component-transform 這個模塊的結構,可以分為以下三個部分:
  • 預處理 wepy component script 代碼 AST 節點部分
  • 構建 Vue AST
  • 通過 generate 吐出代碼
1.預處理 AST
基于前文轉換設計這一節我們知道, wepy 變色龍的轉換器中對代碼的 AST 解析主要依賴 babel AST 三板斧(traverse、types、generate)來實現 , 通過分析各個差異點代碼語句轉換后的 AST 節點,就可以通過 traverse 中的鉤子來進行節點的前置處理,這里安利一下 https://astexplorer.net/,我們可以通過它快速分析代碼塊 AST 節點、模擬場景及驗證轉換邏輯:

推薦閱讀