Taro 3 與原生小程式混合開發實踐

語言: CN / TW / HK

前言

最近接到一個專案需求,是要做出一套頁面,不僅應用在小程式的環境中,也要應用在 H5 裡面,並同時相容兩端原有的邏輯不變。最終經過我們的調研梳理,決定使用當前最新版本的 Taro 3,來實現多端統一開發的訴求。

那這裡我們為什麼選擇 Taro 呢,我認為有以下幾點好處

  • 京東凹凸實驗室開發, 框架穩定性較高
  • 與 Taro 1、2 相比,Taro 3 開發體驗幸福度更高,與使用 React 或者 Vue 開 web 體驗無差
  • 相比 UniApp 框架,Taro 靈活度高可配置性強,可選用的開發框架也更多
  • Taro 社群活躍度較高、周邊物料庫豐富

專案痛點

1.通過 Taro3 進行多端開發,程式碼怎樣做到相容,可以適配到 H5 和小程式,並保證在多端順利啟動。

2.由於原生小程式端有一套自己的程式碼邏輯,H5 端有一套自己的程式碼邏輯,並且兩端邏輯毫不相關。怎樣做到一套程式碼開發出來的頁面,同時插入 H5 或者小程式中,可以順利相容到兩端的原有邏輯流程。

3.打造一套通用框架,可以快速高效的接入其他多端頁面,實現降本提效。

Taro架構的演變

在動手開發前,我覺得還是非常有必要先了解下 Taro 的架構演進的心路歷程。做到一個基本的瞭解。

Taro 1/2(重編譯)

在 Taro 1 與 Taro 2 階段,編譯階段起到了重要的部分。簡單來講,主要是將 Taro 程式碼通過程式碼編譯,轉換為各個端(各類小程式、RN、H5 等)可以執行的程式碼。再通過輕量的執行時進行適配,用 Taro 的元件庫、執行時的框架以及 API 進行差異磨平,來解決多端差異性。

Taro3(重執行)

這一次的架構全新升級,可以理解為重新站在瀏覽器的角度來思考前端的本質:無論開發用的是什麼框架,React ,Vue 也好,jQuery 也罷,最終程式碼經過執行之後都是呼叫了瀏覽器的那幾個 BOM/DOM 的 API ,如:createElementappendChildremoveChild 等,那麼在小程式端我們也可以模擬實現 DOM、BOM API 來讓前端框架直接執行在小程式環境中。

那麼對於生命週期、元件庫、API、路由等差異,我們依然可以通過定義統一標準(如標準組件庫、標準 API 等),各端負責各自實現的方式來進行抹平。

Taro v3.1 開放式架構

Taro 核心維護的平臺只有 6 個(微信、支付寶、百度、頭條、QQ、京東小程式),那麼常常會有使用者對於其他平臺也需要進行及時的支援和維護。因為對於單一平臺的相容性程式碼,涉及編譯和執行時的部分,改動起來複雜度高,且社群難以參與貢獻。於是便打造了一個開放式的框架,通過外掛的形式擴充套件 Taro 的端平臺支援能力:

  • 外掛開發者無需修改 Taro 核心庫程式碼,即可編寫出一個端平臺外掛。
  • 外掛使用者只需安裝、配置端平臺外掛,即可把程式碼編譯到指定平臺。
  • 開發者可以繼承現有的端平臺外掛,然後對平臺的適配邏輯進行自定義。

那麼基於此,Taro 也是根據開放式架構,進行了調整,把內建支援的 6 個平臺封裝成了外掛,CLI 預設會全部載入這些外掛。封裝的過程中,也是檢索了各小程式最新的元件、API,並進行更新與支援,對齊各小程式最新的能力。

專案開發搭建過程

架構的演變簡單瞭解了下,下面讓我們開始專案開發搭建~

1.Taro3 專案安裝

CLI 工具安裝

yarn global add @tarojs/cli

專案初始化

2.原生小程式和 Taro 的目錄對比

首先我們先對比下兩者目錄結構的差異,其實兩者結構並沒有太大的不同,也很容易看懂和上手。

3.H5和小程式的打包執行分析

1)scripts 指令碼配置

我們先看一下專案初始化後的 scripts 指令碼:

使用 Taro 的 build 命令可以把 Taro 程式碼編譯成不同端的程式碼:

Taro 編譯分為 devbuild 模式:

  • dev 模式(增加 --watch 引數) 將會監聽檔案修改。
  • build 模式(去掉 --watch 引數) 將不會監聽檔案修改,並會對程式碼進行壓縮打包。
  • dev 模式生成的檔案較大,設定環境變數 NODE_ENVproduction 可以開啟壓縮,方便預覽,但編譯速度會下降。
"build:weapp""taro build --type weapp"// 微信小程式
"build:swan""taro build --type swan",  // 百度小程式
"build:alipay""taro build --type alipay"// 支付寶小程式
"build:tt""taro build --type tt"// 位元組跳動小程式
"build:h5""taro build --type h5"// H5
"build:rn""taro build --type rn"// React Native
"build:qq""taro build --type qq"// qq小程式
"build:jd""taro build --type jd"// 京東小程式
"build:quickapp""taro build --type quickapp"// 快應用端

2)如何支援編譯其他小程式呢

前面也講過,Taro 為了支援編譯其他端的小程式更加的方便,進行了架構調整為開放式架構,並通過外掛的形式擴充套件 Taro 的端平臺支援能力,這裡以企業微信小程式舉例:

安裝外掛
yarn add @tarojs/plugin-platform-weapp-qy
配置外掛
// config/index.js
config = {
  // ...
  plugins: [
    "@tarojs/plugin-platform-weapp-qy"
  ]
}
打包編譯
npm run build:qywx
注意:

Taro v3.1 + 開始支援

3)小程式 Taro 打出來的包怎麼進行預覽呢

h5 打包預覽和我們平常的方式沒有任何區別,那麼小程式又該如何操作呢?這裡以微信小程式舉例:


這個時候下載並開啟微信開發者工具,然後匯入專案根目錄的 dist 包即可,即可看到介面:

4.Taro 3 開發過程中一些細節點

這裡還是推薦大家先將官方文件簡單梳理一遍,瞭解下 Taro 的一些官方 api,元件庫的使用,這裡就不再一一列舉。下面筆者會直接講一些開發過程中的遇到的一些踩坑點和我覺得比較重要的部分,避免大家走一些彎路。

1)使用路由

在 Taro3 中使用的路由的方式,與之前的版本會有所不同,無論是獲取專案傳入引數還是頁面入參,都是通過 getCurrentInstance().router 來獲取的,具體使用如下。

import Taro, { getCurrentInstance } from "@tarojs/taro"
import React, { Component } from "react"

export default class Index extends Component {
  componentDidMount () {
    console.log(getCurrentInstance().router.params)
  }
}

2)打包新增版本號

在h5編譯打包的時候,如果想要增加版本號,就需要在 config 目錄下 prod.js 檔案進行配置,這裡會暴露一個專屬於 H5 的配置供大家使用。這裡以設定輸出解析檔案的目錄和拓展 Webpack 的 output 選項舉例:

var config = require("../package.json")
var path = require("path")
module.exports = {
  env: {
    NODE_ENV"production"
  },
  defineConstants: {
  },
  mini: {},
  h5: {
    publicPath:config.publicPath+config.version,
    output: {
      path: path.resolve(__dirname, `../dist/${config.version}`), 
    },
    /**
     * 如果h5端編譯後體積過大,可以使用 webpack-bundle-analyzer 外掛對打包體積進行分析。
     * 參考程式碼如下:
     * webpackChain (chain) {
     *   chain.plugin('analyzer')
     *     .use(require('webpack-bundle-analyzer').BundleAnalyzerPlugin, [])
     * }
     */

  }
}

3)介面請求

對於網路請求,Taro 封裝了一套自己的 API,比如說我這邊用的是 async/await 用法:

const options = {
  method: requestType, // "GET"或者"POST"
  url,
  data: param,
  credentials"include" as credentialsType,
  timeout1000 * 30,
  header: h,
};
const res = await Taro.request(options)

注意:

小程式的環境下,介面 url 的請求一定是 https

專案難點攻克

對於這次需求來說,最需要做的事情,就是如何藉助 Taro3 開發一套頁面,同時去相容兩端(微信小程式端和 H5 端)的流程。並且將微信小程式環境下打包出來的 dist 目錄,插入到原生的小程式中,實現混合開發。

1.相容兩端流程搭建

這裡我們首先需要封裝函式來區分不同環境,封裝在 utils 目錄中。

const Utils = {
  isH5() {
    return process.env.TARO_ENV === "h5"
  },
  isWeapp() {
  return process.env.TARO_ENV === "weapp"
 },
}
export default Utils

這樣我們在開發過程中,比如點選提交按鈕,區分出是 h5 環境下,便處理 h5 的邏輯。區分出是微信小程式的邏輯,便處理微信小程式的邏輯。

2.混合開發

現在我們面臨這樣一個場景,就是在此 Taro 專案框架,區分出是微信小程式的環境下了,現在我們需要跳轉到原生小程式的某個頁面,比如說喚起原生的登陸打通,那麼我們又該如何實現呢?

讓我們思考下,即使只開發了一個頁面,小程式打出的包 dist,也是一整個小程式。那我們現在想要將打出的包插入到原生小程式中去實現,那麼我們需要打包成一個單獨的分包。

1)以混合模式進行打包

非常幸運的一點是,Taro 3 官方提供了混合開發的功能,可以讓原生小程式專案和打包出來的專案進行混合開發使用,通過 --blended 命令。

taro build --type weapp --blended

這個時候,在打包出來的 app.js 中會暴露出 taroApp,供我們在原生小程式的 app.js 頁面下去呼叫其生命週期。

但是存在這樣一個問題,在執行我們的原生小程式專案時,我們通過引用在原生專案下的 app.js 裡引入 Taro 專案的入口檔案,來提前初始化一些執行時的邏輯,因此要保證 Taro 專案下的 app.js 檔案裡的邏輯能優先執行。所以說只是 --blended 命令這種,只適合主包的頁面,分包的話,沒法優先執行。

2)解決分包開發,引入 @tarojs/plugin-indie 外掛

先進行安裝

yarn add --dev @tarojs/plugin-indie

然後引入外掛配置

// config/index.js
const config = {
  // ...
  plugins: [
    "@tarojs/plugin-indie"
  ] 
  // ...
}

簡單來說,就是在編譯程式碼時,對頁面下的 js chunk 檔案進行 require 引入的目錄調整,增加對應的 module 的 sourceMap 對映。這樣在訪問到我們 Taro 下的分包頁面時,就可以做到優先執行了。

3)手寫 @tarojs/plugin-mv 外掛,自動化挪動打包後的檔案

在處理好分包的打包流程後,我們在原生小程式的 pages 頁面下建立一個頁面資料夾 taroMini,為了匯入我們 Taro 專案下打包出的分包頁面。然後在原生小程式的 app.json 中,引入分包的路徑配置。

{
  "subPackages": [
    {
      "root""pages/taroMini/pages/riskControlAnswer",
      "pages": [
        "index"
      ]
    }
  ]
}

現在就是我們手動把 Taro 打包出來的資料夾,拖動到原生小程式的 taroMini 目錄下就可以執行我們的頁面了。如圖:

這個時候,我們又要思考下了,每次打包出來的 dist 資料夾,都要手動拖入到原生小程式的專案中麼,有沒有一種自動化的方式,讓其自己挪動呢。這裡我們就要開始手寫一個外掛了:

// plugin/index.js
const fs = require("fs-extra")
const path = require("path")

export default (ctx, options) => {
  ctx.onBuildFinish(() => {
    const blended = ctx.runOpts.blended || ctx.runOpts.options.blended
    
    if (!blended) return

    console.log("編譯結束!")

    const rootPath = path.resolve(__dirname, "../..")
    const miniappPath = path.join(rootPath, "jdc_wx_ecard")
    const outputPath = path.resolve(__dirname, "../dist")

    // taroMini是你在京東有禮小程式專案下的路由資料夾
    const destPath = path.join(miniappPath, `./pages/taroMini`)

    console.log("outputPath", outputPath)
    console.log("destPath", destPath)

    if (fs.existsSync(destPath)) {
      fs.removeSync(destPath)
    }
    fs.copySync(outputPath, destPath)

    console.log("拷貝結束!")
  })
}

其實程式碼仔細看一下,實現思想就是進行一個資料夾的自動挪動指令碼。然後我們把我們手寫好的這個外掛,引入到我們的 Taro 專案中

// config/index.js
const config = {
  // ...
  plugins: [
    "@tarojs/plugin-indie",
    path.join(process.cwd(), "/plugin/index.js")
  ] 
  // ...
}

4)應用

那麼上面所提到的,在 Taro 專案中跳轉到原生小程式的某個頁面中,就可以在我們的本地 Taro 專案中(區分好是微信小程式的環境下)正常書寫了。

const { orderId } = param
const fromPageType = ""
const returnPage = encodeURIComponent(`/pages/taroMini/pages/riskControlAnswer/index?orderId=${orderId}`)
const url = `/pages/login/index/index?returnPage=${returnPage}&pageType=${fromPageType}&noWXinfo=1`
Taro.redirectTo({ url })

其他呼叫原生小程式的功能也可以正常開發了。

專案總結

這樣我們就實現了最開始我們提到的一些需求點,實現了原生小程式和 Taro 專案的混合開發,也實現了 Taro 專案端和原生小程式端兩個專案之間的自動化打通。其實對於 Taro 3 進行深挖,還有很多可以值得探索梳理的地方,後續有機會再和大家分享下~

參考資料

本文分享自微信公眾號 - 凹凸實驗室(AOTULabs)。
如有侵權,請聯絡 [email protected] 刪除。
本文參與“OSC源創計劃”,歡迎正在閱讀的你也加入,一起分享。