前言
在Taro项目开发中,难免会遇到需要混合编译的场景,一般来讲Taro
可以使用原生模块、原生项目也可以使用Taro
的模块内容。官方也确实说了Taro 支持使用小程序原生的页面、组件和插件。
比如Taro
使用原生页面,我们只需要在app.config.ts
文件中加上原生页面的路由,然后项目中就可以直接以小程序原生的开发模式开发该页面。
使用原生页面(weapp)
这种模式对于微信小程序确实是可以,比如:
新增路由
// app.config.ts
export default defineAppConfig({
pages: [
// ...
"pages/wxTest/index",
// ...
]
})
原生模式开发
然后在Taro项目中直接以原生模式进行开发
模版文件
<view>微信原生页面: {{ name }}</view>
启动项目
完成这两步后,就可以直接来启动项目了,由于这个路由是直接写在Taro
项目中,那么这个路由对应的文件也必然会被Taro
进行编译。
从这张图我们可以看到,这个原生开发的页面确实可以正常渲染,并且它的js
文件确实也经过了Taro编译
,但是这个编译非常简单,只是做了一下模块的导出。
并且它的模版文件更直接,啥也没处理!
所以这就是Taro
项目能够直接使用原生模式开发的原因!
使用原生页面(kwai)
但是!!!这种模式貌似只适用于微信小程序,对于其它小程序好像行不通,我这边在快手小程序中尝试以这种模式来让Taro
使用原生页面,发现以下几个问题:
- 对于
ksml
后缀,Taro编译会报错
意思就是webpack
处理不了这种文件,需要我们安装对应的loader
...
这咋还和微信小程序区别对待了呢??
- 如果将
ksml
改为wxml
,虽然不会报错,但它生成的ksml
文件是经过Taro
编译的,使用了Taro
模版
可以看到,此时的页面是渲染不了的,这明明是和微信小程序一样的操作,为啥这样?
原因可能是Taro
底层并没有对ksml
文件的编译做适配,官方不靠谱,那我们只能自己动手来解决了
插件开发
解决这个问题的关键就是不要让Taro
编译我们的原生页面
但是我们想要使用原生页面,那么就得将这个页面配置在Taro
的路由列表中,但如果在路由列表中加上了这个页面那么它势必又会被Taro
编译。这看样子像死循环了?
换种思路:在编译之前不要将该页面加入Taro
的路由列表中,在编译完成后找到生成的app.json
文件,这时候再往里面添加原生页面。然后再将原生页面复制到编译产物中去就可以了。
Taro插件介绍
Taro 的插件都具有固定的代码结构,通常由一个函数组成,示例如下:
export default (ctx, options) => {
// plugin 主体
ctx.onBuildStart(() => {
console.log('编译开始!')
})
ctx.onBuildFinish(() => {
console.log('Webpack 编译结束!')
})
ctx.onBuildComplete(() => {
console.log('Taro 构建完成!')
})
}
插件函数可以接受两个参数:
- ctx:插件当前的运行环境信息,包含插件相关的 API、当前运行参数、辅助方法等等
- options:为插件调用时传入的参数
Taro插件除了提供了上面三个钩子函数以外,还提供了许多 API 来对编译过程进行修改:
ctx.modifyWebpackChain(args: { chain: any }) => void)
,编译中修改 webpack 配置,在这个钩子中,你可以对 webpackChain 作出想要的调整,等同于配置webpackChain
ctx.modifyBuildAssets(args: { assets: any }) => void)
,修改编译后的结果ctx.onBuildFinish(() => void)
,编译结束,接收一个回调函数。在每次 Webpack 编译后都会被触发。如果是在 watch 模式下,那么每当有文件改变触发 Webpack 编译时,都会触发onBuildFinish
钩子。
taro-plugin-kwai-noparse
我们可以选用modifyWebpackChain
这个钩子函数来进行处理:
// taro-plugin-kwai-noparse.js
const path = require("path");
const CopyWebpackPlugin = require("copy-webpack-plugin");
const webpack = require("webpack");
class HandleAppJsonPlugin {
options = {};
pluginName = "HandleAppJsonPlugin";
constructor(options) {
this.options = options;
}
apply(compiler) {
// 绑定 插件
compiler.hooks.compilation.tap(this.pluginName, (compilation) => {
// 监听 webpack 的 processAssets
compilation.hooks.processAssets.tapAsync(
{
name: this.pluginName,
stage: webpack.Compilation.PROCESS_ASSETS_STAGE_OPTIMIZE,
},
async (assets, callback) => {
// 找到 app.json 文件,添加不需要编译的页面路由
if (assets["app.json"] && assets["app.json"].source) {
const json = JSON.parse(assets["app.json"].source());
// console.log("------", this.options);
json.pages.push(...this.options.pages);
compilation.assets["app.json"] = {
source: () => JSON.stringify(json),
size: () => JSON.stringify(json).length,
};
}
callback();
},
);
});
}
}
module.exports = (ctx, pluginOpts) => {
ctx.onBuildStart(() => {
console.log("编译开始!");
});
ctx.modifyWebpackChain(({ chain }) => {
// 处理 编译后生成的 app.json 文件,添加不需要taro编译的页面路由。
chain.plugin("HandleAppJsonPlugin").use(HandleAppJsonPlugin, [pluginOpts]);
// 复制未编译的文件到输出目录
const patterns = pluginOpts.pages.map((page) => {
const pagePath = page?.split("/")?.slice(0, 2)?.join("/");
return {
from: path.resolve(ctx.paths.sourcePath, pagePath),
to: path.resolve(ctx.paths.outputPath, pagePath),
};
});
chain.plugin("CopyWebpackPlugin").use(CopyWebpackPlugin, [
{
patterns,
options: {},
},
]);
});
};
使用
let plugins = [];
if (TARO_ENV === "kwai") {
plugins = [
[
path.resolve(__dirname, "../../../", `plugins/taro-plugin-kwai-noparse`),
{
pages: ["pages/ksTest/index"],
},
],
];
}
module.exports = {
plugins,
}
验证
现在可以像微信小程序那样正常使用原生页面了。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。