公共离线包方案的落地过程
摘要:本文将从what、why、how详细介绍公共离线包的落地过程。
背景
为了提高端内页面的首屏性能,在团队内落地了公共离线包方案。在落地公共离线包的过程中跟离线包接触比较多,所以本次从做h5活动这个角度分享一下离线包的知识,然后也分享一下我们做的公共离线包到底是啥,并且是如何在我们项目中落地的。
一、离线包
1.1. 离线包概述
1.1.1. 介绍
离线包是指提前将页面所需的静态资源打包到一个压缩包内,当用户打开App时会预先下载该压缩包到本地,当用户在客户端内打开H5页面时,浏览器直接从本地加载静态资源。从而最大程度地避免网络环境对H5页面的打开速度产生影响。
1.1.2. 离线包的作用
-
优化用户首次进入页面的体验。
-
优化带宽成本,将大型活动的集中流量,通过提前预下载的方式,减轻高峰时段CDN服务的带宽压力。
二、公共离线包概述
2.1. 什么是公共离线包
公共离线包就是多个业务可以公用的离线包。多个业务可以共用的比如公共的第三方依赖、公共的字体资源、公共的组件等。
2.2. 为什么要用公共离线包
为了性能:
-
公共离线包能申请高优和独立带宽,能极大提高离线包的覆盖率和覆盖速度,且能供多个项目使用。成本原因不可能所有包都能享用高优下载和独立带宽。
-
让没有业务离线包的活动也能获得离线包的收益。
-
新项目能直接享受到公共离线包现有的覆盖率。公共离线包发布之后一般不会频繁改动,所以能让后续的新项目直接命中该公共离线包,而新项目或者迭代的项目的业务离线包还得慢慢进行覆盖。
-
目前在新回用户多 和 冷启动占比高的场景下 离线包命中率偏低。原因是在冷启动场景下,有的用户可能有一段时间没有打开过app,所以没有最新的离线包。但通过公共离线包,只要用户以前打开过app下载了公共离线包,则本次打开页面能命中公共离线包。
为了跨工程的资源复用:
- 多个项目公用的东西,比如暑期每个会场都会用的主会场挂件。cny的方式是项目间都物理上放一起,所以可以打公共的离线包。现在可以让cny等类似活动的各会场代码物理上隔离开。
2.3. 适用范围
-
本身离线包命中率低(新回用户多,冷启多、新项目、经常迭代的项目)
-
目前没有业务离线包
-
多个项目公用的东西,比如暑期每个会场都会用的主会场挂件。cny的方式是项目间都物理上放一起,所以可以打公共的离线包。现在可以让cny等类似活动的各会场代码物理上隔离开。
- 还有如下迭代的场景,但迭代的优化效果跟打包方式有关。
打包方式 迭代内容 | vendor单独包 | vender和业务糅合为一个包 |
业务代码 | 用不用公共离线包都一样。(都是同一个文件缺失了离线包) | 公共离线包版本✅ (毕竟提了几个依赖出来,主包小一点即缺失离线包的那个文件体积会小一点。) |
业务代码和加依赖 | 公共离线包版本✅ (因为公共离线包版本的vendor包会小一些。) | 公共离线包版本✅ (同上) |
结论 | 根本原因在于公共离线包版本的依赖包会小一点。所以只要涉及依赖包的迭代都会有正向效果。 | |
2.4. 接了公共离线包后的线上工作原理:
2.5. 目前团队内公共离线包里的资源
三、如何落地公共离线包
3.1. 整体架构
3.2. 维护一份json数据(暂时放kconf)
作用:
-
用于存储提取的所有依赖信息。一是提供给脚本使用,二是提供给业务插件使用。
-
用于维护公共离线包的资源。以后想对公共离线包里的内容做修改在这改即可。
配置示例:
"kwai-ecosystems-vue3-legacy": {
"pluginConfig": [
{
"name": "#radar",
"originalName": "@ks-radar/radar",
"var": "Radar",
"path": ""
},
],
"legacy": "vue3",
"modules": [
{
"version": "3.10.38",
"name": "@ks/weblogger",
"url": "node_modules/@ks/weblogger/dist/umd/log.hybrid.js",
"validRange": ">=3.0.0 <=3.10.38"
}
]
},
3.3. 更新公共离线包的流水线
- 为什么有的依赖需要自定义打包,有的直接用官方产物?
官方包不一定满足我们的需求:
-
如vue官方的global包进行babel降级时会有问题。
-
希望vue-apollo-model的包把rxjs也打进去,因为rxjs一般只被vue-apollo-model使用。
- 一些官方包遇到的坑:
-
vue-demi: globalThis在低端机中会报
。 解决方式:提PR。
-
vue: vue官方的global包通过babel语法降级的时候会出问题。
解决方式: 不用官方提供的包,而是自己打。
- vue、vue-router: 通过var定义变量企图往windows上挂属性,但是当使用type="module"的时候var定义的变量不会挂到windows上。解决方式: 不用官方包,自己打。
3.4. 给业务提供一个插件
ESM转全局变量大致效果:
import {createApp} from 'vue';
createApp();
--转换成-->
Vue.createApp();
html里会被注入以下链接:
<!doctype html>
<html lang="zh-CN">
<head>
......
<script type="module" crossorigin src="https://s1.kskwai.com/kos/nlav111422/public-offline-pkg/polyfills/assets/polyfills-DcMOsudF.js"></script>
<script defer fetchpriority="high" crossorigin src="https://s1.kskwai.com/kos/nlav111422/public-offline-pkg/legacy/vue-ecosystems-3-4-31-3188fe8f.legacy.js"></script>
<script defer fetchpriority="high" crossorigin src="https://s1.kskwai.com/kos/nlav111422/public-offline-pkg/legacy/kwai-ecosystems-ea84b777.legacy.js"></script>
<script defer fetchpriority="high" crossorigin src="https://s1.kskwai.com/kos/nlav111422/public-offline-pkg/legacy/common-public-0d55a1ac.legacy.js"></script>
</head>
<body>
......
<script nomodule crossorigin src="https://s1.kskwai.com/kos/nlav111422/public-offline-pkg/polyfills/assets/polyfills-legacy-C8pawOBk.js"></script>
</body>
</html>
打包产物:公共离线包里的内容不会被打包。如下在打包产物中搜索vue-router则搜不到对应产物。
页面请求的时候会直接请求公共离线包的资源:
四、性能度量
实验室实验
方案设计阶段通过实验室数据去验证我们的一些想法,发现可以会有性能的劣化。
- 优化
采用公共离线包之后,命中离线包与未命中离线包比是fmp是正向的。大概快50%。
- 劣化
采用公共离线包之后,未命中离线包时跟之前没采用公共离线包的时候相比是负向的。因为包体积变大了,公共离线包对比项目自己打包依赖最大的缺点就在于没法tree-shaking。用了公共离线包未命中离线包时性能比之前未命中离线包时劣化30%。
-
两者均衡之后是正向
-
将命中率提高就能将整体结果往正向引。
线上实验
渠道投放5天数据:fmp优化: 5.3%。tti优化:-4.7%
C2C 5天数据:fmp优化: 6.3%。 tti 优化:3.3%根据C2C线上数据命中率x与TTI增量y的关系为:y = \frac{850 - 1390x}{7976} 原本没有离线包的项目,当公共离线包的命中率达到60%以上则项目整体的TTI的优化效果是正向的。
五、规划
流水线TODO
kconf里json的校验。公共离线包里放其它资源比如 各会场都用的组件、字体资源等。如何检查产物的可靠性。
插件TODO
处理非首屏的内容
开发环境和生产环境的统一方案。
webpack支持
工程规范TODO
如何维护依赖的版本。制定离线包的发版策略,多久发一次版,依据什么决定发一次版