# 小程序启动流程
- 包拉取
- 包大小
- 代码注入
- js 到jscore
- wxml,wxss 到渲染
- 首页创建
- onLaunch - onLoad
- 首页渲染
- onReady - onLoad
- 首页可交互
- 接口返回 - onLoad
# 运行问题
- 卡顿,渲染时间长
- setData
setData 耗时的起始点是在逻辑层发起调用时,终止点是渲染层完成渲染并通知到逻辑层的时候,所以 setData 耗时可以最直观的反映出页面的渲染情况。
- setData
- 闪退
- memoryWarning
- 白屏
- jsError
- requestError
- resoureceFail
- 加载
- 接口耗时
- 页面实现
# 数据收集
通过劫持微信的生命周期方法和部分微信的 api 计算耗时,它的优点是对业务代码几乎零侵入,对业务逻辑影响小,可移植性强。
wx.getperfotmance // 获取启动耗时以及代码注入时间
function sizeof(object) {
const objectList = [];
const stack = [object];
let bytes = 0;
while (stack.length) {
const value = stack.pop();
if (typeof value === 'boolean') {
bytes += 4;
} else if (typeof value === 'string') {
bytes += value.length * 2;
} else if (typeof value === 'number') {
bytes += 8;
} else if (typeof value === 'object' && objectList.indexOf(value) === -1) {
objectList.push(value);
for (let i in value) {
stack.push(value[i]);
}
}
}
return bytes;
}
console.time('setData');
this.setData({
key: value,
}, () => {
console.timeEnd('setData');
});
app.onError
app.onUnhandledRejection
wx.onMemoryWarning
// 在page on ready 时候进行采样
// 假设我们已经获取到了采样点元素和容器元素的 id
let sampleElementId = 'sample-element';
let containerElementId = 'container-element';
// 使用 createSelectorQuery API 获取元素信息
let query = wx.createSelectorQuery();
// 获取采样点元素信息
query.select(`#${sampleElementId}`).boundingClientRect();
// 获取容器元素信息
query.select(`#${containerElementId}`).boundingClientRect();
query.exec((results) => {
// results 是一个数组,包含了我们查询的所有元素的信息
let sampleElement = results[0];
let containerElement = results[1];
// 判断两个元素是否相同
if (sampleElement.id === containerElement.id) {
console.log('采样点元素与容器元素相同');
} else {
console.log('采样点元素与容器元素不相同');
}
});
# 监控用户行为
小程序捕获tap、touchmove事件。
重写Page对象,在用户的 tap 和 touchmove 事件中插入自定义的行为。
遍历页面对象的所有属性,如果属性是一个函数,那么它就用一个新的函数替换这个方法。新的函数会检查第一个参数是否是一个 tap 或 touchmove 事件,如果是,那么它就调用 gestureTrigger 函数,并给事件对象添加一个特殊的标记,以防止事件被无限透传。然后,新的函数调用原始的方法,传入原始的参数。
export function replacePage() {
const originPage = Page;
Page = function (pageOptions) {
function gestureTrigger(e) {
// 给事件对象增加特殊的标记,避免被无限透传
e.__miTopProcessed = true;
// 拿到用户行为回调参数e
console.log('e', e);
}
const listenerTypes = ['touchmove', 'tap'];
Object.keys(pageOptions).forEach((m) => {
if (typeof pageOptions[m] === 'function') {
const originMethod = pageOptions[m];
pageOptions[m] = function (...args) {
const e = args[0];
if (e && e.type && e.currentTarget && !e.__miTopProcessed) {
if (listenerTypes.indexOf(e.type) !== -1) {
gestureTrigger(e);
}
}
return originMethod.apply(this, args);
};
}
});
return originPage.call(this, pageOptions);
};
}
# 性能优化通用方法
- 轻量:需要尽量精简数据大小、资源体积、代码逻辑、接口请求等。
- 代码懒注入
- 包体积
- vuex store 按需
- 提前:将页面渲染需要的环境、数据等提前准备好,例如数据预拉取、接口数据缓存等。
- 初始渲染缓存
在小程序页面第一次被打开后,将页面初始数据渲染结果记录下来,写入一个持久化的缓存区域(缓存可长时间保留,但可能因为小程序更新、基础库更新、储存空间回收等原因被清除);在这个页面被第二次打开时,检查缓存中是否还存有这个页面上一次初始数据的渲染结果,如果有,就直接将渲染结果展示出来;
{ "window": { "initialRenderingCache": "static" } }
- 初始渲染缓存
- 并行:减少没有必要的串行等待过程,例如在 page 创建的时候异步准备页面需要的数据。
- 实时:用户操作永远是最高优先级,需要给出实时的 ui 反馈,例如骨架屏、loading 信息等。
- 分片渲染
如果 setData 过于频繁或者数据量过大,会导致渲染线程繁忙。而我们能够操控的各种点击、滚动事件将拥堵在 webview js 线程上,得不到响应 - 图片渲染
用两个image节点,先展示缩略图,等高清图加载完之后再替换上 - 虚拟列表
用空白节点顶掉已经在可视区间的节点
- 分片渲染