2
头图

编译速度一直是困扰开发者的头等问题,现阶段大型 Taro 项目即使在增加了 cache-loaderthread-loader 等优化手段后,编译耗时仍高居不下。因此在 v3.5 版本中 Taro 重点对编译系统进行了重构,引入对 Webpack5 的支持,改善小程序 & H5 编译时的性能与体验。(除此之外,Taro 也正在落地对于 Vite 的支持,届时开发者将可以自由地选择编译工具。)

同时,Taro v3.5 还带来了兼容 React 18、H5 MPA 等新特性,欢迎各位同学升级试用~

一、编译提速

为了改善编译性能,Taro 主要做了以下事情:

  • 支持 Webpack5
  • 基于模块联邦的依赖预编译
  • 支持使用 esbuild 压缩 JS,使用 esbuild@parcel/css 压缩 CSS
  • 使用 @swc/register 代替 @babel/register

接下来将简单聊聊 Webpack5 与依赖预编译,关于编译提速的完整实现细节请参阅 RFC 文档

1. Webpack5

Webpack5 发布已有两年时间,功能足够稳定,同时其持久化缓存、模块联绑、更优的 Tree Shaking 等特性都为项目的编译流程提供了更好的解决方案。

其中的持久化缓存功能是最重要的特性之一,能极大提升再次编译时的速度。但同时也引入了如何使缓存失效的问题。

Taro 遵循 Webpack “编译安全比编译速度重要” 的理念,默认不开启持久化缓存。当开发者设计好缓存策略后,强烈建议开启持久化缓存。详细配置请参考 mini.cache

2. 依赖预编译

Webpack5 另一个重要的特性要数模块联邦(Module Federation)。受 UmiJS mfsu 特性的启发,可以预先把项目的 node_modules 依赖打包为一个模块联邦 remote 应用,再次编译时 Webpack 则无需再对依赖进行编译,从而提升编译速度。

依赖预编译可以分为三步:

  1. 收集依赖
  2. 打包依赖
  3. 打包 Module Federation Remote 应用

Taro 参考 Vite 使用了 esbuild 收集用户使用到的第三方依赖,并分别进行打包。打包后的模块会作为 Webpack 的 entry,最终打包为模块联邦 Remote 应用,供主应用(Host)消费。实现细节请参考 RFC 文档

Taro 会在小程序环境的 dev 模式下默认开启依赖预编译功能。首次编译时,因为使用了 esbuild 打包第三方依赖,所以会比普通编译稍快。二次编译时,Taro 能直接复用 Remote App,Webpack 只需编译业务代码,因此根据不同项目会有不同的编译提速效果。

依赖预编译的流程图:

https://storage.360buyimg.com/cjj-pub-images/prebundle.png

3. 提速效果

NutUI 组件示例库为例,分别测试 dev 与 prod 环境下编译微信小程序的编译提速效果:

https://storage.jd.com/cjj-pub-images/compile-speed_dev.png

https://storage.jd.com/cjj-pub-images/compile-speed_prod.png

可以看出:

  1. 在 dev 环境下因为 Taro 默认开启了依赖预编译,因此 Webpack5 首次编译速度比 Webpack4 稍快。而 prod 环境没有默认开启依赖预编译,因此两者速度相当(而且 Webpack5 需要写入缓存,可能会比 Webpack4 稍慢)。
  2. 无论是 dev 还是 prod 环境,在完全命中缓存的最优情况下,Webpack5 的编译速度都能得到极大提升。即使是修改源码导致了部分缓存失效时,编译速度仍然比首次编译快得多。

4. 使用

旧项目升级后需要安装 Webpack5 的相关依赖才能正常编译,详情请参考下文的【升级指南】部分。

简单修改 Taro 的编译配置即可开启 Webpack5、持久化缓存、依赖预编译等功能:

/** config/index.js */
const config = {  
    // 自定义编译工具,可选 'Webpack4' 或 'Webpack5' 
    compiler: {       
        type: 'webpack5',   
        // 依赖预编译配置     
        prebundle: {
            enable: true    
        }  
    },  
    // 持久化缓存配置  
    cache: {
        enable: true  
    }
}

二、兼容 React18

React 官方正式发布了 react v18版本,带来了 Automatic Batching、Transitions 和 Concurrent 等诸多新特性,提升了React性能。Taro 也在第一时间完成了对 React18 的兼容。

React目前存在两种工作模式:legacyconcurrent 。在 concurrent 模式下,会使用 New Client API 来渲染组件。

默认情况下,Taro react 仍会在 legacy 模式下运行。简单修改 @tarojs/plugin-framework-react 插件的配置即可启用 concurrent 模式:

/** config/index.js */
const config = {
  plugins: [
    ['@tarojs/plugin-framework-react', { reactMode: 'concurrent' }]
  ]
}
不要忘记将项目的 react 版本升级到 v18 哦

详细内容请参考 discussions

三、支持服务端渲染(SSR)

1. 动机

与 SPA(单页应用程序,Single-Page Application)相比,服务器端渲染(SSR)能带来带来更快的首屏渲染速度更好的 SEO,因此 SSR 功能是大家期望 Taro 支持的特性之一。

2. 实现原理

Taro 在 3.1 版本提出了开放式架构的思想,让开发者可以通过编写插件来让 Taro 支持一个新的平台。

通过 Taro 插件,将 React 生态中知名的 Next.js 框架作为 Taro 的一个新的目标平台,以此让 Taro 能够支持服务端渲染(SSR)。

目前这个插件项目的地址位于:SyMind/tarojs-plugin-platform-nextjs

⚠️  插件目前处于早期建设中,不建议用于生产环境!

3. 安装与使用

安装插件和 Next.js 框架。

# 使用 npm 安装插件与 next.js
npm install tarojs-plugin-platform-nextjs next

# 使用 pnpm 安装插件与 next.js
pnpm install tarojs-plugin-platform-nextjs next

在 Taro 项目的编译配置中添加本插件。

const config = {
  plugins: [
    'tarojs-plugin-platform-nextjs'
  ]
}

开始尝试它吧!

npx taro build --type nextjs --watch

四、支持多页应用(MPA)

很多同学通过阅读 Taro 的源码发现,Taro 曾在 1.x 支持过多页面应用,通过配置 multi 模式就可以开启该特性,但是由于并没有很好支持,也没有相应的业务场景测试,最终并没有在文档中呈现。

在 2.x 发布时,由于各种原因我们回滚了该特性,尽管开发者们依旧可以通过插件或者在项目内自定义 webpack 配置实现类似的需求,不过这依旧会在一些细节上难以处理得尽善尽美。比如需要额外配置文件拆分规则避免冗余的代码,对于 MPA 也并不需要taro-router提供对于路由相关的事件方法的支持……

MPA 是不少社区开发者所追求的重要特性之一,为此我们改写了 taro-loadertaro-router 以适应该模式的个性化需求,应用该模式也只需要如下配置:

module.exports = {
  // ...
  h5: {
    // ...
    router: {
      mode: 'multi'
    }
  }
}

需要注意的是,有很多小程序事件和方法都是基于 SPA 模式设计使用的,在 MPA 模式并不适用,所以会存在一些问题,比如由于多页面导致 TabBar 会重复加载,App 级的生命周期会重复触发,不支持路由动画,生产环也需要额外配置路由映射等等,开启该模式前需要认真考量适用场景。

五、RN 相关依赖库由 unimodules 升级至 expo

Expo 是 React Native 生态中的重要角色,提供了非常多优秀的模块,在 Taro 中有较为广泛的使用,如 expo-av、expo-camera 等,将来我们还会持续接入新的模块。Expo 的模块系统,由 unimodules 变更为 expo 已有一段时日,其架构变更原因可参考文章: What’s new in Expo modules infrastructure

Taro v3.5 及以后将使用新的模块系统,可以通过 taro init 选择 react-native 模板体验。如果你使用的是 Taro 壳工程,可切换到 0.67.0-expo 分支体验。

新老版本的 Taro 及壳工程之间混用的话,将存在不兼容情况,主要原因是存在多个版本原生依赖导致,可通过 resolution 进行版本锁定解决,相应版本参考此处

后续壳的工程将不再包含 unimodules 版本。旧版本升级可参考此PR

注意:升级为 expo 将不再支持 iOS 11,详细内容请参考 discussions

六、升级指南

1. 安装 v3.5.0-beta 的 CLI 工具:

npm i -g @tarojs/cli@beta

2. 更新项目依赖

如果安装失败或打开项目失败,可以删除 node_modules、yarn.lock、package-lock.json 后重新安装依赖再尝试。

2.1 把 package.json 文件中 Taro 相关依赖的版本修改为 3.5.0@beta

2.2 Breakings

  • Vue2 项目需要添加 devDenpendencies:"@vue/babel-preset-jsx": "^1.2.4”
  • Vue3 项目需要添加 devDenpendencies:"@vue/babel-plugin-jsx": "^1.0.6"
  • React 项目需要添加 devDenpendencies:

    "@pmmmwh/react-refresh-webpack-plugin": "0.5.4",
    "react-refresh": "0.11.0"
  • PReact 项目需要添加 devDenpendencies:

    "@prefresh/webpack": "^3.2.3",
    "@prefresh/babel-plugin": "^0.4.1"

2.3 重新安装依赖

3. 使用 Webpack5

新项目在创建项目时,选择编译工具为 "webpack5" 即可。

旧项目升级后需要更新依赖:

  • 建议首先删除 node_modulesyarn.lockpackage-lock.json
  • package.json 中删除 @tarojs/mini-runner@tarojs/webpack-runner 依赖,添加 @tarojs/webpack5-runner 依赖。
  • 重新安装依赖。
  • Taro 编译配置添加 compiler: 'webpack5',最后开启编译。

最后

接下来我们会继续对 v3.5 版本进行迭代,包括实现 H5 的依赖预编译等。而在 v3.6 版本则会落地对 Vite 的支持,同时优化运行时的性能。

最后的最后,衷心各位感谢参与 Taro 开源共建的同学!为了建立更加完善、更加可持续的 Taro 开源生态,突出贡献者价值,Taro 推出了更清晰的参与机制和荣誉激励机制,欢迎更多的同学参与进来~


凹凸实验室
2.3k 声望5.5k 粉丝

凹凸实验室(Aotu.io,英文简称O2) 始建于2015年10月,是一个年轻基情的技术团队。