1
头图

前言

在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,
}

验证

现在可以像微信小程序那样正常使用原生页面了。


南玖
1.1k 声望109 粉丝