从 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 = {}}

推荐阅读