58RN 页面秒开方案与实践
如果无法正常显示,请先停止浏览器的去广告插件。
1. 58RN ⻚面秒开方案与实践
蒋宏伟
58同城 资深前端工程师
2.
3. • 蒋宏伟
• 2015 年入职58同城
• 58RN、Hybrid 框架的负责人
• 目前专注于大前端监控平台的搭建
4. 回归预测:耗时减少 1s,流失率降低 6.9%
流失率 %
20
y = 6.9308x - 3.4752
R² = 0.9203
15
10
5
0
0
0.5
1
1.5
2
2.5
3
首屏时间 s
5. 实际效果:耗时越⻓,性能优化收益越理想
Δ流失率 %
Δ预计流失率
12
Δ实际流失率
9
6
3
0
0
0.5
1
1.5
2
优化后首屏时间 s
6. 收益驱动的秒开模型
方案
提升
选择
指标
流失率
首屏时间
秒开收益
驱动
业务
7. 首屏时间采集方案
性能优化方案
总结与展望
8. 首屏时间的定义
Start
0 ms
FCP
410 ms
Biz DidUpdate LCP Visible Loaded
668 ms 784 ms 928 ms
9. 首屏时间 = LCP
FCP FMP Biz Update LCP Visible Loaded
首次内容绘制 ❌ 首次有效绘制 业务组件渲染完成 最大内容绘制 可视区加载完成
非侵入式采集 非侵入式采集 侵入式采集 ❌ 非侵入式采集 非侵入式采集
W3C Paint Timing 启发式是推测值 ❌ React Lifecycle W3C Draft LCP 没有相关标准 ❌
10. LCP = 你看得到的最大元素,它渲染出来的时间
Start
0 ms
FCP
410 ms
Biz DidUpdate LCP Visible Loaded
668 ms 784 ms 928 ms
11. LCP 在 RN 中的实现
React Native
Native
Start Time
Inject Context
JavaScript
Add Listener
Text.onLayout
Calculator
End Time
Viewport
Visible?
loaded
上报 LCP = End - Start
Image.onLoadEnd
loaded
Size = W * H
loaded
Video.onLoadEnd
Largest Size?
loaded
Wrapper.onTouch
LCP LayoutTime
Se
Bottom Loaded
lec
te
d
End Time
12. LCP 指标收益
• 非侵入式收集
• RN/Web 指标统一
13. 首屏时间采集方案
性能优化方案
• 动态更新优化
• 框架性能优化
• 业务层性能优化
• metro-code-split
总结与展望
14. 性能结构 -> 性能瓶颈 -> 优化方案
React Native
Native
Version
Request
200
JavaScript
Bundle Request Init Init
470 350 380
29% 动态更新
32% 框架初始化
Biz Request
Render
460
420
39%
业务耗时
15. 瓶颈一:动态更新
vs
迭代快
性能好
16. 内置 + 静默更新
Build In
首次进入:使用内置资源,同时后台静默更新
• 再次进入:使用缓存资源,同时后台静默更新
• 强制更新:针对有 BUG 的缓存版本进行升级
Render
JS Thread
Native Thread
•
applyUpdate?
Async Notify
Cache?
Pre Cache
Execute
latest version?
Bundle Request
Update Cache
17. 内置 + 静默更新的缺点
• 增加 App 体积
• 新版本覆盖率低
• 版本碎片化严重
100%
75%
新版本覆盖率
50%
25%
0%
0
24
48
72
单位:h
18. 资源预加载 + 静默更新
• 关键应用: 在上个⻚面,进行资源预加载
• 缓存应用:在 App 启动时,进行版本预请求
Render
JS Thread
applyUpdate?
Async Notify
Native Thread Cache?
Prev Page Pre Cache
Execute
latest version?
Bundle Request
Update Cache
19. 方案一:资源预加载 + 静默更新
未优化
动态更新优化后
2280 ms
1610 ms
-29%
20. 瓶颈二:框架初始化
• JS Native 互相不知道彼此存在
• Native 要初始化所有 NativeModule
JS Thread
Execute(
Native Thread
Bundle
)
Bridge
NativeModule
21. 拆包内置 + 框架预执行
• App 启动后,执行 React 内置包
• 进入 RN 时,执行 Biz 业务包
优化前
优化后
Execute( Bundle )
拆包
Execute( React )
+
Execute(
Biz
Bundle = React + Biz
进入 RN
App 启动后
进入 RN
)
22. 踩坑:diff-match-patch 拆包
业务包
React
Biz
• patch 生成的是“文本补丁”
• “文本补丁”不能单独执行
动态更新包
内置包
patch
React
@@
-1,5
+1,9 @@
React
+,Biz
23. 改造 metro 实现拆包
Modules
React
Biz
组成
src/xxx
…
Bundles
Biz
比对
过滤
动态更新包
node_modules
/react
/xxx
记录内置 IDs
React
内置包
• 内置包用 Path ID 保证不会变,Random ID 会变
• 内置包 require(InitializeCore) 可多节约 90ms
24. 方案二:拆包内置 + 框架预执行
未优化
2280 ms
动态更新优化后
框架初始化优化后
1610 ms
1300 ms
-43%
25. 瓶颈三:业务请求
• 请求不适合缓存
• 请求不适合在上个⻚面预加载
React Native
JavaScript
Native
Init Init Biz Request Render
60 360 420 460
26. 业务数据并行请求
JS Thread
Registry
PreFetch(cb)
Invoke cb
Biz
Native Thread
Execute(
Biz
)
并行
58.com/api?user=${user}
58.com/api?user=GMTC
cb?
27. 方案三:业务数据并行请求
未优化
2280 ms
动态更新优化后
1610 ms
框架初始化优化后
业务请求优化后
1300 ms
985 ms
实现秒开后,还有优化空间吗?
-57%
28. 瓶颈四:代码执行
•
需要执行完整的 bundle 包
React Native
JavaScript
Native
Init
60
Init
360
Request Render
150 460
29. Dynamic Import ?
• Tab 切换⻚面
• 纯 RN 路由⻚面
30. Dynamic import not working
31. Dynamic Import in RN
32. Import ( "./Foo" )
Runtime
Import ( Path )
Installed?
Path to URL
Download Chunk
Run In Context
Render Component
33. Compile Time
Project
Graph
4 basic ( total : 9 )
Modules
Bundles
Dynamic Map
Path URL
Foo Md5.js
CDN
Runtime
Path to URL
34. 基础
基础1 基础2
静态依赖 动态依赖
35. 解决依赖冲突
无依赖冲突
着色
着色 1 动
1 -> 1 动 + 2 静
有依赖冲突
着色
1 -> 2 动 -> 1 静
复制 1 静 + 着色 2 次 ❌
着色 2 动 ✅
36. 整体思路
基础2
基础1
Biz
解构
着色
打包
基础3
Chunks
基础4
37. metro-code-split(已开源)
https://github.com/wuba/metro-code-split
38. 首屏时间采集方案
性能优化方案
总结与展望
39. 性能优化方案
优化分类 方案 (预期)收益 生效范围 生效场景 状态
动态更新 缓存优先,静默更新 203ms 业务 有缓存 已支持
动态更新 常用应用定时更新版本 203ms 所有 使用过或重要业务 已支持
动态更新 资源预加载 470ms 业务 进入概率高 已支持
框架初始化 通用资源内置 143ms 所有 所有 已支持
框架初始化 框架提前初始化 370ms 所有 所有 已支持
业务耗时 业务数据预加载 580ms 业务 所有但需要配置 已支持
业务耗时 Dynamic Import / 业务 非首屏 已开源
RN 新架构 JSI 同步获取数据 46ms 业务 异步获取数据 规划中
RN 新架构 inlineRequire 20% 业务 非首屏 规划中
RN 新架构 Hermes JSVM 80% 所有 所有 规划中
40. 秒开方案 + 收益驱动的实践
方案
动态更新优化
提升
指标
流失率
框架耗时优化 首屏时间
metro-code-split 秒开收益
选择 驱动
业务
9 个 App
337 个业务
41. 欢迎找我交流
42.
43. 在此键入姓名
在此键入Tittle