从 Wepy 到 UniApp 变形记( 七 )

5.5.1 差异性梳理从上面的代码可以看出,一个WXML文件中支持多个不同name属性的< template/ >标签,并且支持通过在引入设置data来传入属性 。从上面的示例模板中我们可以分析出,除了需要将wepy使用的WXML语法转换成vue模板语法外(这里的转换交给了template模块来处理) , 我们还需要处理以下的问题 。

  • 确定引入组件时的传参格式
  • 确定组件中传入对象的属性有哪些
  • 处理< import/ >和< include/ >引入的文件时的情况
5.5.2 核心转换设计1.确定引入组件时的传入属性方式
首先需要将wepy组件引入形式改成Vue的组件引入方式 。以上面的代码为例 , 即将< import/ > 、< script/ >对的引入形式改写成< component-name / > 引入方式 。我们会在转换开始前对代码进行扫描,收集模板中的引入文件信息,传递给wepy-page-transform模块处理,在转换后的Vue组件的< script/ >中进行引入 。并且将< script is="foo" data="https://www.huyubaike.com/biancheng/{{item, pic}}" / > 转换为< FooBar is="foo" :data=https://www.huyubaike.com/biancheng/(待定) / >。这里就需要确定属性传递的方式 。
从上面的代码中可以看到,在WXML文件的< template/ >会自动使用传入的data属性作为隐式的命名空间,从而不需要使用data.item来获取item属性 。这里很自然的就会想到原来的< script is="foo" data="https://www.huyubaike.com/biancheng/{{item, pic}}" / >可以转换成< FooBar compName="foo" :key1="val1" :key2="val2" ... / > 。
其中,key1,val1,key2,val2等为原data属性对象中的键值对 , compName用来指定展示的部分 。这样处理的好处是,引入的WXML文件中使用相应的传入的属性就不需要做额外的修改,并且比较符合我们一般引入Vue组件时传入属性的方式 。
虽然这种方案可以较少的改动WXML文件中的模板 , 但是由于传入的对象可能会在运行期间进行修改,我们在编译期间比较难以确定传入的data对象中的键值对 。考虑到实现的时间成本及难易程度,我们没有选择这种方案 。
目前我们所采用的方案是不去改变原有的属性传入方式,即将组件引入标签转换为< FooBar compName="foo" :data="https://www.huyubaike.com/biancheng/{item, pic}" / > 。从而省去分析传入对象在运行时的变动 。这里就引出了第二个问题,如何确定组件中传入的参数有哪些 。
2.确定组件中的传入的对象属性
由于Vue的模板中不会自动使用传入的对象作为命名空间,我们需要手动的找到当前待转换的模板中所使用到的所有的变量 。相应的代码如下:
searchVars() {const self = thisconst domList = this.$('template *')// 获取wxml文件中template节点下的所有text节点const text = domList.text()const dbbraceRe = new RegExp(TransformTemplate.dbbraceRe, 'g')let ivar// 拿到所有被{{}}包裹的动态表达式while ((ivar = dbbraceRe.exec(text))) {addVar(ivar[1])}// 遍历所有节点的属性,获取所有的动态属性for (let i = 0; i < domList.length; i++) {const dom = domList.eq(i)const attrs = Object.keys(dom.attr())for (let attr of attrs) {const value = https://www.huyubaike.com/biancheng/dom.attr(attr)if (!TransformTemplate.dbbraceRe.test(value)) continueconst exp = value.match(TransformTemplate.dbbraceRe)[1]try {addVar(exp)} catch (e) {addVar(`{${exp}}`)}}}function addVar(exp: string) {traverse(parseSync(`(${exp})`), { // 利用babel分析表达式中的所有变量Identifier(path) {if (path.parentPath.isMemberExpression() &&!path.parentPath.node.computed &&path.parentPath.node.property === path.node)returnself.vars.add(path.node.name) // 收集变量},})}}收集到所有的变量信息后,模板中的所有变量前面需要加上传入的对象名称 , 例如item.hp_title需要转换成data.item.hp_title 。考虑到模板的简洁性和后续的易维护性,我们把转换统一放到< script/ >的computed字段中统一处理即可:
<template><!--...--></template><script>export default {props: ['data', 'compName'],computed: {item() {return data.item},pic() {return data.pic}}}</script>3.处理 < import/ >和< include/ >两种引入方式
wepy模板有两种引入组件的方式,一种是使用< import/ >< script/ >标签对进行引入,还有一种是使用< include/ > 进行引入,< include/ > 会引入WXML文件中除了< template/ >和< wxs/ >的其他标签 。这里的处理方式就比较简单 , 我们把< include/ > 会引入的部分单独抽取出来,生成TItem-incl.vue文件,这样即保证了生成代码的可复用性,也降低< import/ >标签引入的部分生成的TItem.vue文件中的逻辑复杂度 。生成的两个文件的结构如下:

推荐阅读