原理
在针对数据绑定做优化时,需要先了解小程序的运行机制。因为视图层与逻辑层的完全分离,所以二者之间的通信全都依赖于 WeixinJSBridge 实现。如:
- 开发者工具中是基于 window.postMessage
- IOS中基于 window.webkit.messageHandlers.invokeHandler.postMessage
- Android中基于WeixinJSCore.invokeHandler
因此数据绑定方法this.setData也如此,频繁的数据绑定就增加了通信的成本。再来看看this.setData究竟做了哪些事情。基于开发者工具的代码,单步调试大致还原出完整的流程,以下是还原后的代码:
/*
setData 主流程精简还原,并非完整主流程,内有注释
*/
function setData (obj) {
if (typeof(obj) !== 'Object') {
console.log('类型错误'); // 并没有预期中的return;
}
let type = 'appDataChange';
// u.default.emit(e, this.__wxWebviewId__) 代码还原
let e = [type, {
data: {data: list},
options: {timestamp: +new Date()}
},
[0] // this.__wxWebviewId__
}];
// WeixinJSBridge.publish.apply(WeixinJSBridge, e); 代码还原
var datalength = JSON.stringify(e.data).length; // 第一次 JSON.stringify
if (datalength > AppserviceMaxDataSize) { // AppserviceMaxDataSize === 1048576
console.error('已经超过最大长度');
return;
}
if (type === 'appDataChange' || type === 'pageInitData' || type === '__updateAppData') {
// sendMsgToNW({appData: __wxAppData, sdkName: "send_app_data"}) 代码还原
__wxAppData = {
'pages/page1/page1': alldata
}
e = { appData: __wxAppData, sdkName: "send_app_data" }
var postdata = JSON.parse(JSON.stringify(e)); // 第二次 JSON.stringify 第一次 JSON.parse
window.postMessage({
postdata
}, "*");
}
// sendMsgToNW({appData: __wxAppData, sdkName: "send_app_data"}) 代码还原
e = {
eventName: type,
data: e[1],
webviewIds: [0],
sdkName: 'publish'
};
var postdata = JSON.parse(JSON.stringify(e)); // 第三次 JSON.stringify 第二次 JSON.parse
window.postMessage({
postdata
}, "*");
}
setData 运行的流程如下:
图片描述
从上面代码以及流程图中可以看出,在一次setData({a: 1})作时,会进行三次 JSON.stringify,二次JSON.parse以及两次window.postMessage操作。并且在第一次window.postMessage时,并不是单单只处理传递的{a:1},而是处理当前页面的所有 data 数据。因此可想而知每次setData操作的开销是非常大的,只能通过减少数据量,以及减少setData操作来规避。
与 setData 相近的是 React 的 setState 方法,同样是使用 setState 去更新视图的,可以通过源码 React:L199 看到 setState的关键代码如下:
function enqueueUpdate(component) {
ensureInjected();
if (!batchingStrategy.isBatchingUpdates) {
batchingStrategy.batchedUpdates(enqueueUpdate, component);
return;
}
dirtyComponents.push(component);
}
setState的工作流程如下:
图片描述
可以看出,setState 加入了一个缓冲列队,在同一执行流程中进行多次 setState 之后也不会重复渲染视图,这就是一种很好的优化方式。
更多建议: