背景

  • 微信小程序限制单个分包/主包大小不能超过2M
  • 整个小程序所有分包不超过20M
  • Taro中开启智能提取分包依赖导致小程序分包中存在大量重复代码

Taro 异步化能力

Taro官方文档
Taro官方示例
微信分包异步化

Taro原生混合 new-blended 模式

照片把 Taro 项目作为原生项目中的一个分包单独使用,并且把特定的组件单独打包为原生组件

模式介绍

新的混合模式在项目打包过程中会同时处理 Taro组件编译为原生自定义组件 的任务,该模式适用于同时需要用到 页面混合模式 以及 Taro组件编译为原生自定义组件 的场景。
以往使用taro build native-components 命令编译的自定义组件会额外包含一套Taro运行时,而这个特性可以帮助你在编译原生自定义组件的同时与taro页面共享同一套运行时代码,避免了两套运行时导致包体增大的问题。通过这种优化,我们能够更加高效地使用混合模式,并减少包体积的增长。

组件异步化实现

异步工程改造

将组件迁移至异步分包化工程中并在 app.config.ts 文件中全局注册

照片

// app.config.ts
  components:[
    'components/Picker/index',
  ],

异步化组件需要配置index.config.ts处理css样式生效

//src/components/Picker/index.config.ts
export default {
  styleIsolation: 'shared',
  virtualHost: true,
}

照片
config配置

 //config/prod.ts
  mini: {
    webpackChain(chain){
      chain.merge({
        output:{
          // 防止不同工程中 chunkid 的冲突
         chunkLoadingGlobal: 'asynclibjsonp',
        }
      })
    }
  },
//package.json
"scripts": {
    "build:weapp": "taro build --type weapp --new-blended",
    "dev:weapp": "taro build --type weapp --new-blended --watch"
},

主工程改造

新建 pages-subpackage/lib-component 分包

//src/pages-subpackage/lib-components/pages/index/index.tsx
export default () => ''

//src/app.config.ts
// 注册异步分包口子
subpackages:[
    {
      name: 'lib-components',
      root: 'pages-subpackage/lib-components',
      pages: [
        'pages/index/index',
      ],
    }
]


app.config.ts注册异步组件路径

//src/app.config.ts
  usingComponents: ({
    weapp: {
      // 异步分包中组件
      'native-async-picker': './components/Picker/wechat',
    },
  })[process.env.TARO_ENV] || {}

components新增Picker文件夹 index.weapp.tsx 和 wechat 文件夹

//src/components/Picker/index.weapp.tsx
export interface IAsynPicker {
  title: string;
  list: any[];
  onButtonClick: () => void;
}
function AsyncPicker<T>(props: IAsynPicker) {
  return (
     //TODO:这里可能ts报错  typings/component.d.ts中添加类型
    <native-async-picker props={props} />
  )
}
export default AsyncPicker


//typings/component.d.ts
import React from 'react';

declare global {
    namespace JSX {
        interface IntrinsicElements {
            'native-async-picker': React.DetailedHTMLProps<any, any>;
        }
    }
}
export {};

//src/components/Picker/wechat/index.json
{
  "component": true,
  "usingComponents": {
    // 这里的路径是你异步工程产物移动到主工程产物下对应的路径
    "picker": "../../../pages-subpackage/lib-components/components/Picker/index"
  },
  "componentPlaceholder": {
    "picker": "view"
  }
}



// src/components/Picker/wechat/index.wxml
//异步化组件无法采取React中组件传递方式
<picker props="{{props}}" />


// src/components/Picker/wechat/index.ts
//原生组件只能用props接收
Component({
  properties: {
    props: {
      type: Object,
      value: {},
    },
  },
})

主工程脚本改造

//scripts/index.js
export default (ctx) => {
  ctx.onBuildFinish(() => {
    buildSubpackage({
        libName:'',
        outputPath:'',
        srcPath:'',
    })
  })
}
//scripts/buildPackage.js
const { execSync } = require('child_process')
const path = require('path')
const fs = require('fs')
const chalk = require('chalk')

function buildSubpackage(item) {
  const startAt = Date.now()
  log('building', chalk.green(item.libName))
  try {
    // 迁移至异步化工程路径下运行
    execSync(`cd ./packages/${item.libName} && npm run build:${process.env.TARO_ENV}`)
    const output = path.join(projectRoot, item.outputPath)
    if (fs.existsSync(output)) {
      fs.rmSync(output, { recursive: true })
    }
    // 迁移异步化工程构建产物至主工程构建产物目录下
    mergeDir(path.join(item.srcPath, './dist'), output)
  } catch (e) {
    log(`${item.libName} Subpackage 打包失败`, e)
    throw new Error(`${item.libName} Subpackage 打包失败(${e.message})`)
  }
}

config配置改造

// config/index.ts
 plugins: [
    //注册自定义插件
   [path.resolve(__dirname, '../scripts/index.js')],
 ]

主工程页面上使用

//src/pages/index/index.tsx
import { View, Text } from '@tarojs/components'
import { useLoad } from '@tarojs/taro'
import Picker from '../../components/Picker/index.weapp'
import './index.scss'

export default function Index() {

  useLoad(() => {
    console.log('Page loaded.')
  })

  return (
    <View className='index'>
      <Text>Hello world!</Text>
      <Picker title='异步组件测试' list={[1,2,3]} onButtonClick={()=>console.log("success")}></Picker>
    </View>
  )
}

微信开发者工具效果

照片

最后

  • Taro版本4.0.1+React18
  • 支付宝小程序、抖音小程序未测试过
  • 原生组件只能支持对应平台将不再具备多端转换的能力