描述
由Taro、React、Ts开发的一个微信小程序,现在想变成字节小程序,记录一下迁移过程。
环境
"node": "11.13.0"
"@tarojs/taro": "1.3.6"
//有点旧。。。haha
准备工作
https://microapp.bytedance.com 字节开发者中心 注册开发账户,创建一个项目,并认证主体(目前只能企业认证)。
去开发设置里配置服务器域名(直接去小程序后台配置复制过来)。
下载开发者工具。
配置内容
增加project.tt.json
,把appID改上去。
编译
把编译命令的:weapp
改成:tt
。
开发工具导入项目的时候,直接导入dist目录,因为IDE会去找project.config.json
里面的appid。
问题整理
1、编译结果出错
有两个页面的import Base from '@/components/Base'
被编译成了<import src="@/components/Base.wxml" />
导致IDE识别出错,跑不起来。其实在:weapp编译出来也是这样的,但微信小程序的IDE就能正常跑。在:tt编译出来的文件里手动删除<import src="@/components/Base.wxml" />
之后,项目可以正常跑起来。
这两个页面有个共同点,都用到了无状态函数组件,如下:
public render() {
return (
<View>
{this.renderVipDom()}
</View>
)
}
private renderVipDom = () => {
return <View>...</View>
}
测试发现确实是这个问题导致的。以下是尝试修复过程记录:
(1)、尝试升级taro版本到2.2.13(并不能解决上面问题)
只升级当前项目,先把node版本切换到12.10.0,然后执行yarn add -D @tarojs/cli@2.2.13
+yarn taro update project 2.2.13
+yarn add -D @tarojs/mini-runner@2.2.13
。
然后调整config/index
配置文件(参考Taro文档)。
再执行yarn remove @tarojs/async-await
。同时App.tsx文件里删除import '@tarojs/async-await';
。
再执行yarn add -D babel-plugin-transform-runtime
+yarn add -D babel-plugin-transform-runtime
,同时修改项目babel 配置如下:
babel: {
sourceMap: true,
presets: [['env', { modules: false }]],
plugins: [
'transform-decorators-legacy',
'transform-class-properties',
'transform-object-rest-spread',
['transform-runtime', {
"helpers": false,
"polyfill": false,
"regenerator": true,
"moduleName": 'babel-runtime'
}]
]
}
由于使用了sass,还需要yarn add -D @tarojs/plugin-sass
,并在config里配置:
plugins: [
'@tarojs/plugin-sass',
'@tarojs/plugin-less'
],
编译后还有如下一些问题,可自行解决:
①、is_js插件有冲突。
②、所有模块化引入的scss全部没有编译进去,样式全部失效。
(2)、研究编译源码,找编译出错的原因(能解决)
我尝试直接删除dist/文件里的<import src="@/components/Base.wxml" />
,删除后项目是可以跑起来的。所以理论上只要编译不出现这段代码,就没问题。因为同一套代码,:weapp编译是没有问题的,:tt编译就出这个问题,所以重点去看:tt的编译逻辑。
首先,找到node_modules/@tarojs/cli/src/build.ts
,这里面有一个build
方法,是taro打包编译的入口。然后顺藤摸瓜,可以依次找到./mini/index > build()
> ./mini/page > buildPages()
> ./mini/page > buildSinglePage()
> ./mini/native > processNativeWxml()
。其中有一个关键字段REG_WXML_IMPORT
,看他的声明是export const REG_WXML_IMPORT: RegExp = /<import(.*)?src=(?:(?:'([^']*)')|(?:"([^"]*)"))/gi
,看着很眼熟。于是我加了个log测试下,果然有收获。
while ((regResult = constants_1.REG_WXML_IMPORT.exec(wxmlContent)) != null) {
util_1.printLog("reference", '测试', `${regResult[2]}+${regResult[3]}`); //加的log代码
importWxmlPathList.push(regResult[2] || regResult[3]);
}
//下面是编译log出来的内容
生成 页面逻辑 dist/pages/resume/publish/index.js*
引用 测试 undefined+@/components/Base.wxml << 就是他
生成 页面模板 dist/pages/resume/publish/index.ttml
再测试发现前面componentWXMLContent
解析出来就已经包含了<import src="@/components/Base.wxml" />
这段内容。
在./mini/page
里有一段:
const transformResult = wxTransformer({
code: pageJsContent, //整个页面的内容字符串
sourcePath: pageJs, //原始页面路径,如:~/User/git/demo/src/pages/index/index.tsx
sourceDir, //根目录,如:~/User/git/demo/src
outputPath: outputPageJSPath, //输出页面路径,如:~/User/git/demo/dist/pages/index/index.tsx
isRoot: true,
isTyped: constants_1.REG_TYPESCRIPT.test(pageJs),
adapter: buildAdapter, //平台名,如:weapp,tt
env: constantsReplaceList, //环境变量,如:{ ‘process.env.NODE_ENV’: ‘development’, ENV: undefined, ‘process.env.TARO_ENV’: ‘tt’ }
rootProps,
jsxAttributeNameReplace //属性名替换的配置,如:{ cssClass: ‘css-class’, cssPlaceholderClass: ‘css-placeholder-class’ }
});
这里面传入的数据都没问题,但返回的transformResult
就不对了。
然后找到@tarojs/transformer-wx/lib/src/index > transform()
,测试发现里面有一段result = new class_1.Transformer(mainClass, options.sourcePath, componentProperies, options.sourceDir).result;
处理之后就生成了错误结果。
然后重点来了,找到@tarojs/transformer-wx/lib/src/class > CallExpression()
里面有一段:
enter(callPath) {
const callee = callPath.get('callee');
const args = callPath.node.arguments;
if (callee.isMemberExpression()) {
const { object, property } = callee.node;
if (t.isThisExpression(object) && t.isIdentifier(property) && property.name.startsWith('render')) {
const propName = property.name;
if (!self.methods.has(propName)) {
const o = utils_1.getSuperClassPath(self.classPath);
if (o) {
const p = o.resolvePath.endsWith('.js') ? o.resolvePath.slice(0, o.resolvePath.length - 3) : o.resolvePath;
self.importJSXs.add(`<import src="${p + '.wxml'}"/>`); //这是不是很眼熟
}
}
self.renameJSXClassFunc(propName, methodName, callPath, args);
}
}
if (callee.isIdentifier()) {
const nodeName = callee.node.name;
if (nodeName.startsWith('renderClosure')) {
self.renameJSXClassFunc(nodeName, methodName, callPath, args, true);
}
}
},
没搞懂这段代码的目的是什么,我先给它注释掉再说,简单粗暴!
2、自定义头部不支持
字节小程序自定义头部功能需要申请开通才能用。在功能管理 > 页面结构自定义中申请。
3、开发工具默认第一个页面不是"pages/index/index"
开发工具编译后进入的首页不是"pages/index/index",而是进入了"pages/mine/index"。
但是上传代码后用真机进入体验版,首页进入正常。
未完待续。。。
参考资料
Taro 1.x 迁移至 2.x文档:https://taro-docs.jd.com/taro/docs/2.x/migrate-to-2
Taro cli流程及编译代码解析:https://segmentfault.com/a/1190000041515931
字节小程序开发文档:https://microapp.bytedance.com/docs/zh-CN/mini-app/develop/functional-plug-in/custom/