從 Wepy 到 UniApp 變形記( 三 )


首先我們會處理wepy模板中的特殊標簽< import/ >、< include/ >,主要是將wxml的文件引入模式轉換成Vue模板的組件引入模式,同時還需要收集引入的wxml的文件地址和展示的模板名稱 。由于< include/ >可以引入wxml文件中除了< template/ >和< wxs/ >的所有代碼,為了保證轉換后組件的復用性 , 我們將引入的xx.wxml文件拆成了xx.vue和xx-incl.vue兩個文件,使用< import/ >標簽的會導入xx.vue,而使用< include/ >標簽的會導入xx-incl.vue , 轉換import的核心代碼實現如下:
transformImport() {// 獲取所有import標簽const imports = this.$('import')for (let i = 0; i < imports.length; i++) {const node = imports.eq(i)if (!node.is('import')) returnconst importPath = node.attr('src')// 收集引入的路徑信息this.importPath.push(importPath)// 將文件名統一轉換成短橫線風格let compName = TransformTemplate.toLine(path.basename(importPath, path.extname(importPath)))let template = node.next('template')while (template.is('template')) {const next = template.next('template')if (template.attr('is')) {const children = template.children()// 生成新的組件標簽例如// <import src="https://www.huyubaike.com/biancheng/components/list.wxml" />// <template is="subList" />=> <list is="subList" />const comp = this.$(`<${compName} />`).attr(template.attr()).append(children)comp.attr(TransformTemplate.toLine(this.compName), comp.attr('is'))comp.removeAttr('is')// 將當前標簽替換為新生成的組件標簽template.replaceWith(comp)}template = next}node.remove()}}【從 Wepy 到 UniApp 變形記】具體的WXML文件拆分方案請看WXML轉換部分 。

  • wepy 屬性轉換
上文中已經介紹了,wepy模板中的屬性使用了命名空間+模板字符串風格的動態屬性,我們需要將他們轉換成Vue風格的屬性 。轉換需要操作模板中的節點及其屬性,這里我們使用了cheerio, 快速、靈活、類jQuery核心實現,可以利用jQuery的語法非常方便的對模板字符串進行處理 。
上述流程中一個分支中的轉換函數會處理相應的wepy屬性,以保證后續可以很方便的對轉換模塊進行完善和修改 。由于屬性名稱轉換只是簡單的做一下相應的映射 , 我們重點分析一下動態屬性值的轉換過程 。
WXML中使用雙中括號來標記動態屬性中的變量及WXS表達式,并且如果變量是WXS對象的話還可以省略對象的大括號例如
< view wx:for="{{list}}" > {{item}} < /view >、< template is="objectCombine" data="https://www.huyubaike.com/biancheng/{{for: a, bar: b}}" >< /template >所以當我們取到雙中括號中的值時會有以下兩種情況:
  1. 得到WXS的表達式;
  2. 得到一個沒有中括號包裹的WXS對象 。此時我們可以先對表達式嘗試轉換,如果有報錯的話 , 給表達式包裹一層中括號再進行轉換 ??紤]到WXS的語法類似于Javascript的子集,我們依然使用babel對其進行解析并處理 。
核心代碼實現如下:
/** * * @param value 需要轉換的屬性值 */private transformValue(value: string): string {const exp = value.match(TransformTemplate.dbbraceRe)[1]try {let seq = falsetraverse(parseSync(`(${exp})`), {enter(path) {// 由于WXS支持對象鍵值相等的縮寫{{a,b,c}},故此處需要額外處理if (path.isSequenceExpression()) {seq = true}},})if (!seq) {return exp}return `{${exp}}`} catch (e) {return `{${exp}}`}}到這里 , 我們已經能夠處理wepy模板中絕大部分的動態屬性值的轉換 。但是,上文也提及到了,wepy采用的是類似模板引擎的方式來處理動態屬性的,即WXML支持這種動態屬性< view id="item-{{index}}" >,如果這個 < view / >標簽使用了wx:for指令的話,id屬性會被編譯成item-0、item-1... 這個問題我們也想了多種方案去解決,例如字符串拼接、正則處理等 , 但是都不能很好的覆蓋全部場景,總會有特殊場景的出現導致轉換失敗 。
最終 , 我們還是想到了模板引擎,Javascript中也有類似于模板引擎的元素 , 那就是模板字符串 。使用模板字符串,我們僅僅需要把WXML中用來標記變量的雙括號{{}}轉換成Javascript中的${}即可 。
5.2 Wepy App 轉換5.2.1 差異性梳理wepy 的 App 小程序實例中主要包含小程序生命周期函數、config 配置對象、globalData 全局數據對象,以及其他自定義方法與屬性 。
核心代碼實現如下:
import wepy from 'wepy'// 在 page 中,通過 this.$parent 來訪問 app 實例export default class MyAPP extends wepy.app {customData = https://www.huyubaike.com/biancheng/{}customFunction() {}onLaunch() {}onShow() {}// 對應 app.json 文件// build 編譯時會根據 config 屬性自動生成 app.json 文件config = {}globalData = {}}

推薦閱讀