编译速度一直是困扰开发者的头等问题,现阶段大型 Taro 项目即使在增加了 cache-loader
、thread-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 则无需再对依赖进行编译,从而提升编译速度。
依赖预编译可以分为三步:
- 收集依赖
- 打包依赖
- 打包 Module Federation Remote 应用
Taro 参考 Vite 使用了 esbuild 收集用户使用到的第三方依赖,并分别进行打包。打包后的模块会作为 Webpack 的 entry,最终打包为模块联邦 Remote 应用,供主应用(Host)消费。实现细节请参考 RFC 文档。
Taro 会在小程序环境的 dev 模式下默认开启依赖预编译功能。首次编译时,因为使用了 esbuild 打包第三方依赖,所以会比普通编译稍快。二次编译时,Taro 能直接复用 Remote App,Webpack 只需编译业务代码,因此根据不同项目会有不同的编译提速效果。
依赖预编译的流程图:
3. 提速效果
以 NutUI 组件示例库为例,分别测试 dev 与 prod 环境下编译微信小程序的编译提速效果:
可以看出:
- 在 dev 环境下因为 Taro 默认开启了依赖预编译,因此 Webpack5 首次编译速度比 Webpack4 稍快。而 prod 环境没有默认开启依赖预编译,因此两者速度相当(而且 Webpack5 需要写入缓存,可能会比 Webpack4 稍慢)。
- 无论是 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目前存在两种工作模式:legacy
和 concurrent
。在 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-loader
和 taro-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_modules
、yarn.lock
、package-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 推出了更清晰的参与机制和荣誉激励机制,欢迎更多的同学参与进来~