前言
项目使用Taro框架搭建,兼容H5钉钉端以及微信小程序端。代码随着迭代开发,小程序的代码体积逐渐变大,微信为了用户体验方面考虑,对代码包的体积有一定要求,如果过大则不允许上架。微信推荐的是使用分包方案来处理代码包过大的情况,即代码按照页面区分,分为主包和一个或者多个分包,其中总体积不超过20MB,主包的体积不超过2MB,就允许发布。此文记录了分包主要优化的方面以及遇到的问题。
分包配置
首先,需要改动路由配置,项目原有的路由配置文件如下:
pages: [
'pages/home/index', // 首页
'pages/login/index', // 登录页面 用于token过期,重新登录
'pages/consult/index', // 资讯
'pages/consult_detail/index', // 资讯详情
'pages/my-questionnaire/index', // 我的问卷
// ....其他页面
],
我们将Tab下的四个页面放进主包,其他的页面放入分包(也可以使用多个分包,这里只分了一个包),分包后的路由配置:
pages: [
'pages/login/index', // 登录页面 用于token过期,重新登录
'pages/home/index', // 首页
'pages/my-library/index', // 学习中心
'pages/study-calendar/index', // 学习日历
'pages/person/index' // 我的
],
subpackages: [
{
root: 'pages/module-common/',
pages: [
'consult/index', // 资讯
'consult_detail/index', // 资讯详情
'my-questionnaire/index', // 我的问卷
'my-qa/index', // 我的提问
// .... 其他页面
]
}
],
可以看到,增加了subpackages配置,除了Tab的页面,全部放进来。同时,代码的文件目录也要做出改动

其中,module-common是我们自己定的分包命名,命名什么都是可以的。文件结构不允许全部放在pages下面,必须按照包名来分,否则微信会报错。
体积压缩
分包配置完成,但是我们还需要对代码体积进行分析,减少打包体积,目标是主包2MB以下。可以通过微信开发者工具,查看代码依赖分析,来看具体占体积较大的模块(注:应使用npm run build:weapp,生产会进行压缩)

上图是已经优化过后的代码包大小,可以看到,主包是1.87MB,分包是1.30MB,能达到微信的发布要求。接下来介绍下主要的优化工作,我们研究发现,代码包体积大的原因主要有以下几个方面:
静态资源
一般在assets目录下存放前端静态资源,以图片为主,图片是占据代码包大小的最大元凶,一张图片可能有700KB左右,所以我们首先的优化对象就是处理图片,推荐以下处理方法:
- 将体积稍大的图片上传到OSS,改成HTTP URL 引用资源,避免打包进来。
- 如果没有OSS,可以将图片进行压缩,前提是图片不要失真过度。
第三方库
我们还发现,某个业务页面的体积居然有500KB。要说明的是,我们的业务代码占总体代码是很少一部分的,代码体积的大头还是第三方库的使用。于是,当我们发现这个500KB的页面时,首先想到了这个页面是否引用了某个超大的第三方库,并将这个库的代码全部打进来了。
答案是cryto-js这个库,用于加密。当使用最新版本时,会将其他无用的代码也打进来,压缩后的体积有400KB左右(例如,nodejs版的代码也打进来了),解决方案是回退到3.1.9-1的版本,重新打包后,页面从500KB变成50KB,可以接受。
另外,代码中如果使用了moment,建议使用dayjs来替换,dayjs的体积较小,api一致。如果一个项目既用moment,又用dayjs,在我看来是不可接受的。
微信的代码依赖分析图,可能并不是很明确,例如vendor.js文件,无法展示它具体包含了哪些第三库,这个时候可以考虑使用webpack-bundle-analyzer来分析是哪些库占用了较大的体积,这个插件要在配置文件的webpackchain加上。
打包了H5的代码
我们使用的是Taro来实现h5和小程序的多端统一。当然,有些功能是没可能做到一份代码,到处运行的。无法兼容多端的功能,我们会单独开发,h5一套代码,微信小程序一套代码。
例如,我们在h5中,有个pdf文件预览的功能,基于pdf.js。但是,pdf.js在微信小程序的环境是跑不了的,所以我们微信小程序的文件预览的功能是Webview内嵌h5页面。
分析打包时,发现微信小程序居然把pdf.js也打了进来,pdf.js本身就是一个很大的库,打包进无用的代码,是必须要进行优化的。
import { lazy, Suspense } from 'react';
let FileViewer;
if (process.env.TARO_ENV === 'h5') {
FileViewer = lazy(() => import('./components/FileViewerH5'));
} else {
FileViewer = lazy(() => import('./components/FileViewerTaro'));
}
const FilePreview = () => {
return (
<Suspense fallback={<span />}>
<FileViewer />
</Suspense>
);
};
export default FilePreview;
解决上述问题的方法就是使用代码分割,这里使用的是react的Suspens、lazy以及异步import的方案,识别到伊布import后,webpack会自动帮我们按需加载,就不会将h5的代码打进来了。
还有一个例子是,我们的项目有视频播放的功能,在h5上使用的是video.js,在微信小程序上使用的是video标签,这部分也需要代码分割,按需引入,避免在微信上打包进了video.js。
import { forwardRef, lazy, Suspense } from 'react';
let Video;
if (process.env.TARO_ENV === 'h5') {
Video = lazy(() => import('./VideoH5'));
} else {
Video = lazy(() => import('./VideoTaro'));
}
function VideoWrapper(props) {
return (
<Suspense fallback={<span />}>
<Video {...props} />
</Suspense>
);
}
export default forwardRef(VideoWrapper);
路由问题
我们进行分包的时候,业务代码基本写完了,页面的路由也都定好了。但是进行分包后,微信小程序和h5的路由都会发生变化,原有路由根本不能使用,例如:
Taro.navigateTo({ url: `/pages/my-qa/index` });
分包后的路由:
Taro.navigateTo({ url: `/pages/module-common/my-qa/index` });
会发现,分包内的页面,要在路由上加载前缀,这个改动无疑是巨大的。我们不可能把代码里面的每一个url都手动去加上前缀,部分路由url,在后端也有存储,还要去改后端数据库里的url。考虑再三后,我们希望把改动降到最小,不改动原有的 navigateTo中的url,h5的路由保持不变,能接受改动微信小程序的路由。
h5的路由不变
h5路由保持不变使用的是 config.js中h5的router属性,来配置router的别名。即真实的路由是/pages/module-common/consult/index,但是/pages/consult/index相当于一个别名,也能找到页面。
单独处理小程序的路由
小程序没有像h5,可以配置“别名”,来映射路由。所以,我们期望的效果是在调用 Taro.navigateTo({ url: /pages/my-qa/index }); 时,能自动加上分包前缀,跳转到真正的页面。
Taro.navigateTo({ url: /pages/module-common/my-qa/index });
实现以上效果的做法是,在Taro的实例上定义一个navigateToPage方法,核心就是使用Object.defineProperties方法,所有的navigateTo都替换成navigateToPage方法,navigateToPage方法为跳转的url新增分包的前缀。
实现如下:
import Taro from '@tarojs/taro';
// 主包内的路由
const mainPkgRoutes = [
'pages/home/index', // 首页
'pages/login/index', // 登录页面 用于token过期,重新登录
'pages/my-library/index', // 学习中心
'pages/study-calendar/index', // 学习日历
'pages/person/index' // 我的
];
// 分包的路由加上前缀
const beforeRoute = options => {
if (process.env.TARO_ENV === 'weapp') {
const { url } = options;
const isMainPkgRoute = mainPkgRoutes.find(route => url.includes(route));
if (isMainPkgRoute) {
return options;
}
return {
...options,
url: url.replace('/pages', '/pages/module-common')
};
}
return options;
};
/**
* @function 自定义navigaTo等路由跳转方法(用于处理微信分包)
*/
const initCustomTaro = () => {
Object.defineProperties(Taro, {
navigateToPage: {
configurable: true,
enumerable: true,
get() {
return options => Taro.navigateTo(beforeRoute(options));
}
},
redirectToPage: {
configurable: true,
enumerable: true,
get() {
return options => Taro.redirectTo(beforeRoute(options));
}
},
reLaunchPage: {
configurable: true,
enumerable: true,
get() {
return options => Taro.reLaunch(beforeRoute(options));
}
}
});
};
export default initCustomTaro;
最后,在全局app.ts执行initCustomTaro进行初始化即可。再全局替换navigateTo为navigateToPage即可,就能自动带上分包前缀,避免改错改漏的情况发生。当然,如果在项目开始前,就定好分包策略,路由便不需要进行如此困难的改动了。
总结
本文记录了Taro框架下微信小程序进行分包的一些经验,包括如何进行页面拆分和配置、如何进行体积压缩、以及遇到的路由问题。
体积压缩:
- 处理图片,上传到OSS或者进行压缩。
- 使用第三方库时,要考虑其体积。如果仅仅是使用部分功能,可以按需加载,或者考虑用其他库来替代。
- 避免微信端打入了h5的代码。
路由问题:
- 建议在开发前,就定好微信的分包。
- 可以在Taro的实例上重新定义个路由跳转方法,用来增加分包前缀。
前言
项目使用Taro框架搭建,兼容H5钉钉端以及微信小程序端。代码随着迭代开发,小程序的代码体积逐渐变大,微信为了用户体验方面考虑,对代码包的体积有一定要求,如果过大则不允许上架。微信推荐的是使用分包方案来处理代码包过大的情况,即代码按照页面区分,分为主包和一个或者多个分包,其中总体积不超过20MB,主包的体积不超过2MB,就允许发布。此文记录了分包主要优化的方面以及遇到的问题。
分包配置
首先,需要改动路由配置,项目原有的路由配置文件如下:
我们将Tab下的四个页面放进主包,其他的页面放入分包(也可以使用多个分包,这里只分了一个包),分包后的路由配置:
可以看到,增加了subpackages配置,除了Tab的页面,全部放进来。同时,代码的文件目录也要做出改动

其中,module-common是我们自己定的分包命名,命名什么都是可以的。文件结构不允许全部放在pages下面,必须按照包名来分,否则微信会报错。
体积压缩
分包配置完成,但是我们还需要对代码体积进行分析,减少打包体积,目标是主包2MB以下。可以通过微信开发者工具,查看代码依赖分析,来看具体占体积较大的模块(注:应使用npm run build:weapp,生产会进行压缩)

上图是已经优化过后的代码包大小,可以看到,主包是1.87MB,分包是1.30MB,能达到微信的发布要求。接下来介绍下主要的优化工作,我们研究发现,代码包体积大的原因主要有以下几个方面:
静态资源
一般在assets目录下存放前端静态资源,以图片为主,图片是占据代码包大小的最大元凶,一张图片可能有700KB左右,所以我们首先的优化对象就是处理图片,推荐以下处理方法:
第三方库
我们还发现,某个业务页面的体积居然有500KB。要说明的是,我们的业务代码占总体代码是很少一部分的,代码体积的大头还是第三方库的使用。于是,当我们发现这个500KB的页面时,首先想到了这个页面是否引用了某个超大的第三方库,并将这个库的代码全部打进来了。
答案是cryto-js这个库,用于加密。当使用最新版本时,会将其他无用的代码也打进来,压缩后的体积有400KB左右(例如,nodejs版的代码也打进来了),解决方案是回退到3.1.9-1的版本,重新打包后,页面从500KB变成50KB,可以接受。
另外,代码中如果使用了moment,建议使用dayjs来替换,dayjs的体积较小,api一致。如果一个项目既用moment,又用dayjs,在我看来是不可接受的。
微信的代码依赖分析图,可能并不是很明确,例如vendor.js文件,无法展示它具体包含了哪些第三库,这个时候可以考虑使用webpack-bundle-analyzer来分析是哪些库占用了较大的体积,这个插件要在配置文件的webpackchain加上。
打包了H5的代码
我们使用的是Taro来实现h5和小程序的多端统一。当然,有些功能是没可能做到一份代码,到处运行的。无法兼容多端的功能,我们会单独开发,h5一套代码,微信小程序一套代码。
例如,我们在h5中,有个pdf文件预览的功能,基于pdf.js。但是,pdf.js在微信小程序的环境是跑不了的,所以我们微信小程序的文件预览的功能是Webview内嵌h5页面。
分析打包时,发现微信小程序居然把pdf.js也打了进来,pdf.js本身就是一个很大的库,打包进无用的代码,是必须要进行优化的。
解决上述问题的方法就是使用代码分割,这里使用的是react的Suspens、lazy以及异步import的方案,识别到伊布import后,webpack会自动帮我们按需加载,就不会将h5的代码打进来了。
还有一个例子是,我们的项目有视频播放的功能,在h5上使用的是video.js,在微信小程序上使用的是video标签,这部分也需要代码分割,按需引入,避免在微信上打包进了video.js。
路由问题
我们进行分包的时候,业务代码基本写完了,页面的路由也都定好了。但是进行分包后,微信小程序和h5的路由都会发生变化,原有路由根本不能使用,例如:
分包后的路由:
会发现,分包内的页面,要在路由上加载前缀,这个改动无疑是巨大的。我们不可能把代码里面的每一个url都手动去加上前缀,部分路由url,在后端也有存储,还要去改后端数据库里的url。考虑再三后,我们希望把改动降到最小,不改动原有的 navigateTo中的url,h5的路由保持不变,能接受改动微信小程序的路由。
h5的路由不变
单独处理小程序的路由
小程序没有像h5,可以配置“别名”,来映射路由。所以,我们期望的效果是在调用 Taro.navigateTo({ url:
/pages/my-qa/index}); 时,能自动加上分包前缀,跳转到真正的页面。Taro.navigateTo({ url:
/pages/module-common/my-qa/index});实现以上效果的做法是,在Taro的实例上定义一个navigateToPage方法,核心就是使用Object.defineProperties方法,所有的navigateTo都替换成navigateToPage方法,navigateToPage方法为跳转的url新增分包的前缀。
实现如下:
最后,在全局app.ts执行initCustomTaro进行初始化即可。再全局替换navigateTo为navigateToPage即可,就能自动带上分包前缀,避免改错改漏的情况发生。当然,如果在项目开始前,就定好分包策略,路由便不需要进行如此困难的改动了。
总结
本文记录了Taro框架下微信小程序进行分包的一些经验,包括如何进行页面拆分和配置、如何进行体积压缩、以及遇到的路由问题。
体积压缩:
路由问题: