taro-convert 是一个命令行工具,其作用是将原生微信小程序转换成 taro 标准代码。其主要是通过src/convertor/index.ts 中的 Convertor类来实现。
初始化
在生成Convertor对象时,除了一些必要的初始化工作外,比如初始化项目根路径,生成输出文件夹等等。其还会去解析微信小程序工程。主要包括如下几方面:
读取入口文件
app.js路径。-
读取配置文件
app.json,获取如下信息:-
pages,获取路由信息,存放到页面路由集合中。 -
sitemapLocaiton,若存在,则将sitemap.json拷贝到转换输出目录。 -
subpackages/subPackages,获取subpackages信息,添加到路由页面集合中。
-
读取样式文件
app.wxss。
转换过程
转换流程分为以下三个步骤:
- 入口文件的转换
- 页面文件的转换,逐个遍历页面文件,进行转换。即在
app.json中定义的pages。 - 根据模板生成工程配置文件
js 代码转换
主要分为入口文件App.js和导入文件的转换。
入口文件转换
主要是在 generateEntry 方法中,其内部调用了generateScriptFiles 处理导入的文件。
主要步骤如下:
- 调用
taroize生成ast及wxml中导入的模板信息。taroize的内部处理下面会有说明。 - 调用
parseAst遍历并修改ast,并且统计import的js文件,作为结果返回。 - 调用
generateMinimalEscapeCode,根据ast生成代码,并写入文件到输出目录。 - 若入口有样式文件,则调用
traverseStyle进行处理。 - 若入口文件有
import其他js文件,则继续调用generateScriptFiles进行处理。内部逻辑在下面会讲到。 - 若入口配置文件中有定义
tabBar,则调用generateTabBarIcon进行处理。将Icon/selectedIcon复制到输出目录。
导入文件转换
导入文件指的是通过 require/import 引入的文件。其转换逻辑主要在 generateScriptFiles 方法,入参为 js 文件路径集合。
主要步骤如下:
- 遍历每个
js文件。 - 调用
wxTransformer生成ast。其内部逻辑很复杂,但是对于微信小程序来说,只是简单返回其ast,无其他处理。 - 调用
parseAst遍历并修改ast,并返回import的js文件。其逻辑下面会有说明。 - 调用
generateMinimalEscapeCode生成代码,写入文件。 - 因为当前处理的
js文件可能也import了其他js文件,所以递归调用generateScriptFiles进行处理。
taroize 方法
taroize 是比较重要的一个方法,会进行一系列的预处理。其参数中包含了配置信息 xx.json,js 文件内容,wxml 模板信息。具体处理如下:
-
parseXML如果传入了
xml,则进行解析,包括标签的解析。 -
parseJSON将
json加上括号包装成表达式(json),调用babel的template方法返回对象表达式的节点信息ObjectExpression。会用在后续的parsePage中(下面会提到),主要用于填充新生成类中的config的值。比如
json为{'title': 'hello'},则会返回下图中红框部分。

-
parseScript设置一系列
babel插件参数,将代码转换为ast。然后做如下处理:-
自定义
vistior进行访问。-
遇到
BlockStatement,即方法具体实现{}, 将 作用域scope的值从wx修改为Taro。也就是说如果在
xx.js最外层定义了function,会走到这里。如下://获取应用实例 const app = Taro.getApp() function test() { console.log('test') } 遇到
Identifier标识符,且节点名为wx,则替换为Taro。-
遇到
CallExpression函数调用,进行标识符、属性调用的替换以及类的生成。-
标识符替换
如果
Identifier为getApp/getCurrentPages,替换成Taro.getApp/Taro.getCurrentPages调用。 -
属性调用替换
如果为
wx.xxx的调用,则替换为Taro.xxx。 -
Page/Component/App方法调用,类的生成如果是
Page/Component/App方法,则说明其为微信小程序·。调用parsePage返回一个ClassDecleration结构,插入到body中。这样生成的文件中就会包含如下信息:@withWeapp({ xx }) class _C extends Taro.Component { config = { navigationBarTitleText: '查看启动日志' } render () { } }其中
parsePage中包括config,render 方法,装饰器withWeapp的生成。config为配置文件json中的信息。{ "navigationBarTitleText": "查看启动日志", "usingComponents": {} }
-
-
如果不为微信小程序,则在
js文件末尾默认加上;Component({}),再进行以上处理。-
导入使用的组件和依赖文件,结果如下所示:
``` import { Block, View, Button, Image, Text } from '@tarojs/components' import Taro from '@tarojs/taro' import withWeapp from '@tarojs/with-weapp' ```其中使用的组件是通过解析
wxml中的标签来计算,比如以下xml代码:``` <view class="container log-list"> <block wx:for="{{logs}}" wx:for-item="log"> <text class="log-item">{{index + 1}}. {{log}}</text> </block> </view> ```则会自动引入
Block、View、Text组件,其中Block是默认就会引入的,无论在xml中是否使用。
-
parseAst 方法
可使用 https://astexplorer.net/ 查看语法树。
主要用来遍历 ast,对其进行修改,并统计 import 的 js 文件信息。其中语法树的修改包括头部导入 Taro 框架,调用对象的替换为Taro 等等。
首先使用 traverse 进行 ast 遍历,分为 enter 和 exit两个过程。
enter 处理
在 enter 时会处理如下几种类型:
-
ClassDeclaration,类的声明。内部会继续遍历
ClassMethod,类中定义的方法。如果遇到render,继续遍历,如果存在JSXElment,标记为taro组件,并记录类名。一个
JSXElement如下,相当于一个组件的声明。<Image src={require('../kkk')}></Image> -
ClassExpression,这里主要用于类被赋值给一个变量的情况,=右边为ClassExpression。内部处理方式同步骤1。f = class NamedFoo { constructor() {} whoIsThere() { return NamedFoo.name; } } -
ExportDefaultDeclaration,处理export default的情况。同样包括两种情况。内部处理方式同步骤1。export default class Component {}export default a = class Component {}
-
ImportDeclaration,处理import的js/ts文件。通过调用
analyzeImportUrl分析引入的js/ts文件。 -
CallExpression,处理函数调用。- 若调用了
require,则调用analyzeImportUrl分析出import的js/ts文件。 - 若调用了微信小程序的
['getApp', 'getCurrentPages', 'requirePlugin']这几个方法,则会替换成Taro调用,并标记需要引入Taro框架。
- 若调用了
-
MemberExpression,处理对象属性调用。
如果调用对象为wx,则替换成Taro。同时标记需要导入Taro框架。wx.setStorageSync('logs', logs) => Taro.setStorageSync('logs', logs)
exit 处理
在 exit 中做如下处理。
-
处理
body节点,如果需要导入@tarojs/taro,则加入importDeclaration节点。import Taro from '@tarojs/taro' -
遍历
StringLiteral,主要处理图片路径引用。计算其绝对路径, 并将图片复制到输出目录。重新计算源文件与图片的相对路径
relativePath。如果其父节点是变量声明(值为图片路径),则使用
require(relativePath)进行替换。-
如果其父节点是
JSX属性,则使用jSXExpressionContainer进行替换,即{require(relativePath)}表达式的方式进行替换。在下例中,
src就是JSX属性。<Image src={require('../kkk')}></Image>
-
导入依赖文件,包括引入样式和其他依赖
导入样式:
import './app.scss'导入依赖组件和文件:
import xx from 'xx'其中依赖的组件是从页面配置文件的
usingComponent中解析出来的。 -
如果判断
isApp为true,则会在最后插入一段代码。Taro.render(<App />, document.getElementById('app'))但只有在解析入口文件时,
isApp才为true。
样式转换
通过 postcss,传入自己写的postcss-taro-unit-transform插件来进行转换的。其内部实现很简单,主要做了两件事情:
- 把
10px转换为20px,即将数值 *2。 - 把
rpx替换为px。
页面转换
包括js 文件转换 + 样式转换 + 依赖组件递归转换。与入口文件的转换类似,多了页面模板 wxml + 依赖组件的转换。
其会从页面配置信息中拿到出依赖的组件信息。
组件转换
同页面转换。
根据模板生成配置文件
根据对应文件的模板生成相应的内容。
配置文件
-
index.js:通用配置 -
dev.js:开发配置,会合并index.js -
pro.js:发布配置,会合并index.js .gitignore-
.editorconfig:编辑器相关配置 -
.eslintrc:eslint相关配置 -
project.config.json:工程配置 package.jsonsrc/index.html