博客 / 詳情

返回

開發自定義Taro插件解決跨平台難題

前言

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

驗證

現在可以像微信小程序那樣正常使用原生頁面了。

user avatar
0 位用戶收藏了這個故事!

發佈 評論

Some HTML is okay.