Professional Documents
Culture Documents
伊不
CONTENTS 1 原理介绍 走进支付宝小程序底层架构
目录 2 更快启动 为首屏渲染争分夺秒
3 更快呈现 打开即可见的视觉体验
4 更快交互 富交互场景能力增强
Part 1 原理介绍
走进支付宝小程序底层架构
H5 代码是如何渲染出视图的?
class Component extends React.Component {
constructor() {
super();
this.state = { year: 0 }; 数据变更
}
render() {
return ( 组件树维护 DOM视图刷新
<div onClick={this.handleTap.bind(this)}>
{this.props.name}{this.state.year}
</div>
);
}
事件回调
handleTap() {
window.alert('Welcome to SEE Conf 2021');
}
生命周期
componentDidMount() {
this.setState({ year: 2021 });
}
}
React 组件
数据变更
WebView 内部 data 或 外部 props 发生改变
组件树维护
数据变更 调度渲染逻辑,收集组件创建/更新/卸载信息
组件树维护
生命周期调度
界面交互 触发组件的生命周期方法
事件回调
事件回调
界面发生交互事件时,触发组件绑定的事件回调
支付宝小程序代码是如何渲染出视图的?
class Component extends React.Component { <view onTap="handleTap">
constructor() { {{name}}{{year}}
组件树维护 DOM视图刷新
super(); </view>
this.state = { year: 0 };
}
render() { Component({
data: { year: 0 }, 数据变更
return (
<div onClick={this.handleTap.bind(this)}> props: { name: '' },
{this.props.name}{this.state.year} methods: {
</div> handleTap() { 事件回调
); my.alert({
} message: 'Welcome to SEE Conf 2021',
handleTap() { });
window.alert('Welcome to SEE Conf 2021'); },
} },
生命周期
componentDidMount() { didMount() {
this.setState({ year: 2021 }); this.setData({ year: 2021 });
} },
} });
数据变更
数据变更
组件树维护
组件树维护
DOM视图刷新
DOM视图刷新
生命周期
生命周期
界面交互
界面交互
事件回调
事件回调
H5 单线程模型 支付宝小程序极简双线程模型
WebView 和 Worker 如何交互?
WebView Worker
const { serviceWorker } = window.navigator;
await serviceWorker.register( self.addEventListener(
启动 'message',
'./worker.js',
{ scope: './' }, ({ data, ports }) => {
); 启动 switch (data.type) {
case 'WebViewInit':
const { active } = await serviceWorker.ready; 启动成功回调 const port2 = ports[0];
const { port1, port2 } = new MessageChannel(); }
active.postMessage({ },
建立通道 );
type: 'WebViewInit',
pagePath, 建连成功
queryString,
}, [port2]);
数据变更
组件树维护
// 接收来⾃ WebView 的消息的回调
// 接收来⾃ Worker 的消息的回调 DOM视图刷新
port1.onmessage = (event) => { port2.onmessage = (event) => {
// ... // ...
};
生命周期 };
// 向 Worker 发送消息的⽅法 // 向 WebView 发送消息的⽅法
port1.postMessage; 界面交互 port2.postMessage;
事件回调
Part 2 更快启动
为首屏渲染争分夺秒
性能优化前后对比视频
1 2 3 4 5
830ms 1250ms 1320ms 2090ms 2750ms
3 4 5
白屏等待
1 2 3 首次渲染耗时较长
830ms 1250ms 1320ms
700ms 页面抖动
1秒内页面抖动3次
3
1 Worker 启动
依赖 WebView 拉起
1 2 3
830ms 1250ms 1320ms
2 页面的初始数据
依赖 WebView 建连
组 组 组
建 视 视 视
件 件 件
启 立 图 图 图 生命周期调度
1 2 树 3 树 3 树 3
动 通 刷 刷 刷
维 维 维 依赖 WebView 渲染
道 新 新 新
护 护 护
建 数 生 数 生 数
启 连 据 命 据 命 据
动 成 变 周 变 周 变
功 更 期 更 期 更
启动入口
Worker 通过 WebView 拉起,这种串行依赖使得
Worker 启动稍慢,同时导致 WebView 建连也得等
待 Worker 启动结束。二者相互阻塞
数据初始化源
组件树维护方
组件树在 WebView 侧维护,数据变更后,Worker
要调度生命周期来准备下一轮数据变更,必须等待
一次通信来回和视图渲染,导致 WebView 侧页面抖
动和 Worker 侧逻辑阻塞
启动入口
使用 v8 Worker 替换 serviceWorker。客户端在启动
WebView 的同时,也拉起 v8 Worker,确保二者并
行启动,不再相互阻塞
数据初始化源
启动 v8 Worker 时,直接将页面的初始化信息注
v8 Worker
入,不必等 WebView 的建连,Worker 可以提前执
行页面的逻辑代码。真正建连一到,可以立即开始
渲染
WebView Worker WebView Worker
数据变更 数据变更
组件树维护 组件树维护
DOM视图刷新 生命周期
生命周期 数据不再变更
DOM视图刷新
启动 启动 启动
启动 启动成功回调 数据变更
建立通道 生命周期
建连成功 数据不再变更
数据变更 DOM视图刷新
组件树维护
DOM视图刷新
生命周期
界面交互 界面交互
事件回调 事件回调
Part 3 更快呈现
打开即可见的使用体验
1 2 3
830ms 1250ms 1320ms
700ms
1320ms
700ms
3
白屏时间还是很长啊!
服务端渲染
H5 Server
Server Side Rendering
WebView
小程序 Client ?
安卓支付宝客户端扫码体验
首次访问会出现加载态
重启 App 后体验极速启动
唤起 WebView 小程序默认加载态 业务自定义骨架屏 正常内容
正常启动⽀付宝⼩程序
唤起 WebView 快照缓存 正常内容
携带快照启动⽀付宝⼩程序
提前渲染
在获得 Worker 初始数据之前渲染好不变内容
Snapshot 优化体验
告别加载态、骨架屏,减少用户焦虑
将真实页面内容作为唤起时的骨架屏
加速决策
用户看到页面后,可以立即决策行为
WebView Worker
启动 启动
展示快照 开启数据等待
开启数据队列 数据变更
组件树维护
生命周期
数据不再变更
攒入数据队列
异步数据返回
通知数据完备
消费所有数据
DOM视图刷新
Part 4 更快交互
富交互场景能力增强
WebView Worker WebView
界面交互 界面交互
事件回调 事件回调
数据变更 DOM视图刷新
生命周期
数据不再变更
DOM视图刷新
来回通信带来性能开销和视图卡顿 H5 开发的事件流程
支付宝小程序 SJS 事件回调流程
function handleTap(event, ownerComponent) {
event.instance.addClass('hello-world');
}
function toTimeString(timeStamp) {
return getDate(timeStamp).toTimeString();
}