原生项目使用 Taro

3.0.25 后开始支持在原生项目中部分使用 Taro 进行开发。

示例项目:

taro-blended

基础混合用法

App 入口

1. 以混合模式进行打包

编译项目时使用 --blended 参数以输出混合模式的代码。

  1. taro build --type weapp --blended
  2. taro build --type weapp --watch --blended

2. 移动 Taro 项目的输出目录到原生项目内

可以编写一个 Taro 插件自动移动,可以参考 plugin-mv

3. 原生项目的 app.js 中引用 Taro 入口文件

app.config.js

  1. // 必须引用 Taro 项目的入口文件
  2. const taroApp = require('./taro/app.js').taroApp
  3. App({
  4. onShow () {
  5. // 可选,调用 Taro 项目 app 的 onShow 生命周期
  6. taroApp.onShow()
  7. },
  8. onHide () {
  9. // 可选,调用 Taro 项目 app 的 onHide 生命周期
  10. taroApp.onHide()
  11. }
  12. })

引用原生项目的 JS 文件

有时我们需要在 Taro 项目中引用原生项目中的公共 js 模块,如上报 sdk。但是 Webpack 会把这些公共模块一并打包,导致公共模块存在两份(Taro 产物中一份,原生项目中一份)。

为了优化包体积大小,我们希望不要打包到 Taro 产物中,而是直接引用原生项目中的代码,可以使用 Webpack 的 externals 配置去实现。

例子

假设以下项目结构:

  1. ├── miniapp 原生项目
  2. | └── utils
  3. | └── util.js
  4. └── taro-project Taro 项目
  5. └── src
  6. └── pages
  7. └── index 此页面中希望引用 miniapp/utils/util.js
  1. 配置 alias,目的是让 externals 更方便地筛选出不需要打包的依赖。
  2. 配置 Webpack externals,选择出不需要打包的依赖,并计算好相对路径。
  3. 设置环境变量 process.env.NODE_ENVproduction 时,externals 生效。(当没有手动设置环境变量时,默认在 watch 模式时环境变量为 development,否则为 production

config/index.js

  1. const config = {
  2. alias: {
  3. // 开发环境直接引用原生项目下的依赖,方便开发
  4. '@/utils': process.env.NODE_ENV === 'production' ? path.resolve(__dirname, '../utils') : path.resolve(__dirname, '../../miniapp/utils')
  5. },
  6. mini: {
  7. webpackChain (chain) {
  8. chain.merge({
  9. externals: [
  10. (context, request, callback) => {
  11. const externalDirs = ['@/utils']
  12. const externalDir = externalDirs.find(dir => request.startsWith(dir))
  13. if (process.env.NODE_ENV === 'production' && externalDir) {
  14. const externalDirPath = config.alias[externalDir]
  15. const res = request.replace('@/utils', path.relative(context, externalDirPath))
  16. return callback(null, `commonjs ${res}`)
  17. }
  18. callback()
  19. },
  20. ]
  21. })
  22. }
  23. }
  24. }

taro-project/src/pages/index/index.js

  1. import { logSomething } from '@/utils/util'
  2. logSomething()

webpack 打包结果

  1. {
  2. "./src/pages/index/index.jsx": (function(m, e, __webpack_require__) {
  3. var _utils_util = __webpack_require__("@/utils/util");
  4. // ...
  5. }),
  6. "@/utils/util": (function(module, exports) {
  7. module.exports = require("../../../utils/util");
  8. })
  9. }

引用原生项目的原生组件

有时我们需要在 Taro 项目中引用原生项目中的公共自定义组件。

引用原生项目的 js 文件一样,我们希望在开发环境能正确解析组件路径,在生产环境则直接引用原生项目的组件而不是重复打包,可以使用 Taro 的 alias 配置去实现。

例子

假设以下项目结构:

  1. ├── miniapp 原生项目
  2. | └── components
  3. | └── title
  4. | ├── index.js
  5. | ├── index.wxml
  6. | ├── index.wxss
  7. | └── index.json
  8. └── taro-project Taro 项目
  9. └── src
  10. ├── components 把原生项目的组件拷贝过来,让开发环境能正确解析组件
  11. | └── title
  12. └── pages
  13. └── index 此页面中希望引用 miniapp/components/title
  1. 把原生项目的组件拷贝到 Taro 项目,让开发环境能正确解析组件。
  2. 根据开发环境和生产环境,正确配置 alias

config/index.js

  1. const config = {
  2. alias: {
  3. '@/components': process.env.NODE_ENV === 'production' ? path.resolve(__dirname, '../components') : path.resolve(__dirname, '../../miniapp/components')
  4. }
  5. }

taro-project/src/pages/index/index.config.js

  1. export default {
  2. usingComponents: {
  3. title: '@/components/title/index'
  4. }
  5. }

对 Taro 项目的部分页面分包

对 Taro 项目中部分页面进行分包。

依赖细分

Taro 默认会把页面共用的普通依赖打包到 common.js,node_modules 依赖打包到 vendor.js

但是在分包时,我们会希望把只有在分包中共用的依赖打包到分包中,而不是打在主包的 common.jsvendor.js 中。这就需要我们对依赖进行细分,可以借助 Webpack 的 splitChunk 和 Taro 的 addChunkPages 配置去实现。

例子

假设以下项目结构:

  1. ├── dist
  2. | |── common.js 公共依赖
  3. | |── vendors.js node_modules 依赖
  4. | └── subPackages
  5. | ├── foo
  6. | ├── bar
  7. | └── common.js 只有 subPackages 子包中使用的公共依赖
  8. └── src
  9. └── subPackages
  10. ├── foo
  11. └── bar
  1. 使用 Webpack splitChunks 把只有 subpackages 子包独有的依赖打包到 subpackages/common.js 中。
  2. 使用 Taro addChunkPages 配置,在子包中所有页面的头部添加对 subpackages/common.js 的引用。

config/index.js

  1. const config = {
  2. mini: {
  3. addChunkPages (pages) {
  4. pages.set('subpackages/bar/index', ['subpackages/common']),
  5. pages.set('subpackages/foo/index', ['subpackages/common'])
  6. },
  7. webpackChain (chain) {
  8. chain.merge({
  9. optimization: {
  10. splitChunks: {
  11. cacheGroups: {
  12. subpackagesCommon: {
  13. name: 'subpackages/common',
  14. minChunks: 2,
  15. test: (module, chunks) => {
  16. const isNoOnlySubpackRequired = chunks.find(chunk => !(/\bsubpackages\b/.test(chunk.name)))
  17. return !isNoOnlySubpackRequired
  18. },
  19. priority: 200
  20. }
  21. }
  22. }
  23. }
  24. })
  25. }
  26. }
  27. }

把 Taro 项目作为一个完整分包

使用方法

1. 安装使用插件

安装插件 @tarojs/plugin-indie

  1. npm i @tarojs/plugin-indie --save-dev

使用插件

config/index.js

  1. const config = {
  2. plugins: [
  3. '@tarojs/plugin-indie'
  4. ]
  5. }

2. 以混合模式进行打包

编译项目时使用 --blended 参数以输出混合模式的代码。

  1. taro build --type weapp --blended
  2. taro build --type weapp --watch --blended

3. 移动 Taro 项目的输出目录到原生项目内

可以编写一个 Taro 插件自动移动,可以参考 plugin-mv

4. 设置原生项目的分包配置

把 Taro 项目拆分到多个分包

假设有一个 Taro 项目,它含有页面 A 和页面 B。我们需要把页面 A 加入原生项目的其中一个分包 M,把页面 B 加入到另一个分包 N。

为此,和普通打出一个分包不同的是,首先需要配置 Webpack 的 output.jsonpFunction 配置,避免 chunkid 的冲突。

config/index.js

  1. config = {
  2. // ...
  3. mini: {
  4. webpackChain (chain) {
  5. chain.merge({
  6. output: {
  7. // 可以配合 npm script 和环境变量来动态修改
  8. jsonpFunction: process.env.JSONP_NAME || "webpackJsonp"
  9. }
  10. })
  11. }
  12. }
  13. }

然后分别对 A、B 页面使用混合模式打包,步骤和把 Taro 项目作为一个完整分包一致。

把 Taro 组件编译为原生自定义组件

v3.1.2+,暂时只支持 React

Taro 支持把组件编译为原生小程序自定义组件,供原生项目使用。

使用方法

1. 配置组件路径

修改 app.config.js,增加 components 配置,指向组件入口文件的路径:

app.config.js

  1. export default {
  2. // ...
  3. components: [
  4. 'pages/index/index',
  5. 'components/picker/index'
  6. ]
  7. }

2. 开始编译

使用 taro build native-components 命令,配合参数 type,即可编译出对应平台的自定义组件。

  1. taro build native-components --type [platform] [--watch]

props 传递

传递 props 给 Taro 编译出来的原生自定义组件时,需要统一通过 props 参数来传递:

page/index/index.js

  1. Page({
  2. data: {
  3. pickerProps: {
  4. mode: 'format',
  5. value: [0, 0, 0],
  6. onInitial (value, index) {
  7. console.log('onInitial')
  8. }
  9. }
  10. }
  11. })

page/index/index.wxml

  1. <!--index.wxml-->
  2. <view>
  3. <picker props="{{pickerProps}}"></picker>
  4. </view>

Taro 组件 - Picker

  1. function Picker ({ mode, value, onInitial }) {
  2. return (
  3. // ...
  4. )
  5. }