可以申请 App 启动时预下载首包,建议拆包后申请,可以大幅度降低包下载时间。
预渲染提前渲染页面相当于从第一个阶段创建容器便开始优化。
模块拆分:可以拆分首包,可大幅提升包下载更新和加载性能。
懒加载:首屏不相关模块实现懒加载,减少加载时间。
Inline Requires 采用
inline require
方式打包,可以把实际代码中没有使用的模块去除,减少 size。 加载模块的时机在实际真正使用到这个模块的时候,实现懒加载效果。 模块赋值时采用get
属性的方式进行赋值,可以在inline require
打包方式下实现懒加载。
开启 inline require 配置
// metro.config.js
module.exports = {transformer: {getTransformOptions: async () => ({transform: {experimentalImportSupport: true,inlineRequires: true,},}),},
};
V8 or Hermes 引擎性能对比
CodeCache:JS 预编译后的字节码产物,会缓存至磁盘,下次页面加载时可直接复用。目前 Hermes 已开启。
可减少一些 Call Native 的操作,同时将一些 ab 和静态数据增量提供批量获取的接口减少通信次数,以及减少 log 埋点的次数。
预加载 Bundle Load:Native 提供 loadBusinessScript。 只加载 JS,不触发生命周期。
引擎复用 + 深度预加载:提前渲染备用,可自动生成 CodeCache,实际进入页面时可提升性能。
前页信息复用:前页信息复用+后页骨架屏,前页跳转至后页时,可以采用前页信息提前渲染后页的部分内容。适合列表页->详情页等场景。
服务预取:框架已实现服务预取Prefetch
,可以在前页提前发服务预取后页信息。适用查询页 → 列表页、列表页 → 详情页等场景。
还有一类『在途服务预取』,是在进入下个页面时提前发下个页面的主服务,可以节省 200ms 左右的页面切换时间,在途服务仅在此次进入页面时有效。
Prefetch 不要滥用,因为会引起后端服务请求量上升,针对场景适当选用。
服务预取的缓存模式也支持长期过期时间:目前唐图已采用。场景是将之前的网络请求相应报文存储到本地 cache(磁盘文件缓存和内存缓存均支持)。页面加载时直接读取缓存,如果有缓存直接渲染 UI,同时发服务获取最新数据后再 diff 刷新 UI,同时更新缓存。
BFF 接口聚合:尽可能在 BFF 层聚合首屏所需服务,需要 case by case 制定方案。
接口设计和性能优化:梳理 BFF 接口的契约合理性,以及提出具体的性能要求。
即使有接口聚合,和复用前页信息并不冲突。
对于第三方或者外部直接跳转至详情页场景, 接口聚合可以直接发挥作用;
在列表页 → 详情页场景先使用信息复用,再一次性加载好完整页面,只要控制好 CLS(至少保证页面上半部分组件位置保持不变),对用户体验仍有大量提升。
竞品 - PDD:头图复用前页图片,页面下半部分一次性服务完成渲染。
分屏渲染:CRN 与 Native 通信性能不佳,导致多次渲染引发“动画片”效果,Dom 量大的情况下效果更差。建议改用 Promise.all 来同步渲染性能接近的异步请求。
页面设计约束:Layout 变化会导致页面抖动,直接影响 CLS 指数(见附录参考 Web 性能核心指标之一),对用户体验影响很大。
避免导致页面 Layout 变化的设计,至少页面的上半部分 Layout 不要有大幅变化的可能性;也可以利用前页信息,自动调整当前页面的骨架屏,避免页面 Layout 大幅变化,尤其尽可能避免页面上半部分的 Layout 变化。
竞品 - 美团:复用前页信息(通常是列表页到详情页),页面上半部分不会大幅变化,即骨架屏相对固定,不会存在额外加载新元素导致页面抖动的情况。
DomTree 预渲染,相当于客户端实现预加载直出
以上是本人在开发公司出海App使用rn方面所总结的一些经验,项目规模不大,如有错误,欢迎各路大佬批评指教,对 DomTree 预渲染
感兴趣的话,下一篇我会单独介绍一下