W3Cschool
恭喜您成為首批注冊用戶
獲得88經(jīng)驗(yàn)值獎(jiǎng)勵(lì)
基礎(chǔ)庫 3.160.12 及以上版本開始支持Page.onInit。
大部分小程序,需要發(fā)起至少一次網(wǎng)絡(luò)請求并調(diào)用setData,才能完成整個(gè)頁面的最終渲染。如果能優(yōu)化該環(huán)節(jié),頁面的渲染時(shí)間將會(huì)大大縮短。
回顧一下我們在性能優(yōu)化的原理和手段中所介紹的小程序啟動(dòng)流程,可以發(fā)現(xiàn)無論是把主數(shù)據(jù)請求放在App.onLaunch里還是Page.onLoad里,都會(huì)存在一些難以解決問題:
小程序提供一種頁面級(jí)別的生命周期Page.onInit。
該生命周期的執(zhí)行時(shí)機(jī)介于App.onLaunch和Page.onLoad之間。具體的執(zhí)行時(shí)機(jī)可參考下圖,小程序是在setInitData之后立即執(zhí)行Page.onInit()。
如果把主數(shù)據(jù)請求從 Page.onLoad 轉(zhuǎn)移到 Page.onInit 中,將極大提升小程序的頁面加載性能。
開發(fā)者可以在onInit中向服務(wù)器請求數(shù)據(jù),并執(zhí)行setData。圖中展示了setData的兩種時(shí)機(jī),同時(shí)應(yīng)注意:
由于App.onShow是依賴客戶端通知邏輯線程的,所以Page.onInit不一定會(huì)在首次 App.onShow之后執(zhí)行??纱_定的前后順序是:App.onLaunch-> Page.onInit ->Component.created->Component.attached->Page.onLoad
盡量將頁面的業(yè)務(wù)數(shù)據(jù)請求放在 Page.onInit 中。
function getData(param) {
return new Promise((resolve, reject) => {
swan.request({
url: 'xxx',
success: res => resolve(res)
});
});
}
Page(
// 使用一個(gè)標(biāo)記位,確保只請求一次主數(shù)據(jù)
hasRequest: false
data: {
value: ''
},
onInit(param) {
if (!this.hasRequest) {
this.hasRequest = true;
getData(param).then(res => {
this.setData({
value: res.data
});
})
}
},
onLoad(param) {
if (!this.hasRequest) {
this.hasRequest = true;
getData(param).then(res => {
this.setData({
value: res.data
});
})
}
}
);
selectComponent
、selectAllComponents
、swan.createSelectorQuery
、swan.createMapContext
、swan.createCameraContext、swan.createCanvasContext
等;
- 由于并非所有版本的基礎(chǔ)庫都支持此生命周期,開發(fā)者可以參考上述代碼片段,增加兼容邏輯;
- 如果您的小程序在邏輯線程初始化階段存在較大瓶頸,那么使用Page.onInit可能不會(huì)有明顯效果。建議從減少動(dòng)態(tài)庫和插件的使用、減少App.onLaunch耗時(shí)等角度進(jìn)行優(yōu)化。
onInit 的收益
此處以百度知道、百度百科和寶寶知道小程序?yàn)槔褂肞age.onInit進(jìn)行了優(yōu)化,上屏?xí)r長均有明顯提升。
以下是三個(gè)小程序把主數(shù)據(jù)請求從 Page.onLoad 遷移到 Page.onInit 后,獲取到的收益:
小程序
收益(單位 ms)
百度知道
210
百度百科
100
寶寶知道
150
百度App 11.26及以上版本開始支持prelink
小程序首屏的渲染,通常依賴某個(gè)網(wǎng)絡(luò)請求返回的數(shù)據(jù),我們將這個(gè)請求定義為主請求。大部分情況下,主請求的速度提升,渲染速度也會(huì)隨之提升。所以,小程序框架提供了prelink機(jī)制,用于提前和業(yè)務(wù)服務(wù)器建立網(wǎng)絡(luò)連接,使隨后的主請求可以復(fù)用該連接,提升請求速度。
通過上面的對比可以看出,在配置了 prelink 時(shí),框架會(huì)在加載業(yè)務(wù)代碼 app.js 的同時(shí)發(fā)送一個(gè)預(yù)連接請求。在發(fā)送主請求時(shí),可以直接復(fù)用該連接,達(dá)到減少主請求時(shí)長的目的。
正確使用 prelink,需要做以下兩個(gè)操作:
1.在開發(fā)者平臺(tái)配置 prelink 信息
2.在小程序業(yè)務(wù)代碼中,盡可能前置需要復(fù)用預(yù)創(chuàng)建連接的請求(一般為主請求)。
在百度App 中,提供了 prelink 的測試工具,用于開發(fā)者驗(yàn)證配置的正確性,開發(fā)者可按下圖中的流程進(jìn)行測試驗(yàn)證。
通過上文的 onInit,我們可以在大部分場景下,讓主數(shù)據(jù)請求的時(shí)機(jī)更早,但在一些場景下,主數(shù)據(jù)的請求可能需要更早,或是主數(shù)據(jù)請求依賴其他一些需要耗時(shí)獲取的數(shù)據(jù)作為參數(shù),比如在一些商業(yè)線索類頁面,商家會(huì)希望用戶進(jìn)入頁面時(shí)立即看到線索相關(guān)信息,而不需要再等待,這種時(shí)候,我們需要一些能夠更早獲取數(shù)據(jù)的機(jī)制。本節(jié),我們將介紹如何進(jìn)行數(shù)據(jù)前置獲取的優(yōu)化手段。
“數(shù)據(jù)前置獲取”,就是把關(guān)鍵頁面數(shù)據(jù)獲取的時(shí)機(jī)提前至前一個(gè)頁面,當(dāng)用戶進(jìn)入這些關(guān)鍵頁面后,不再從后端請求數(shù)據(jù),而是直接渲染從上一頁面中獲取到的數(shù)據(jù)。
通常情況下,用戶看到一個(gè)有效頁面的流程是:點(diǎn)擊頁面入口 → 進(jìn)入頁面 → 請求頁面的數(shù)據(jù) → 渲染請求到的數(shù)據(jù)。如果頻繁訪問某個(gè)頁面的話,頁面的數(shù)據(jù)也需要反復(fù)請求,不僅網(wǎng)絡(luò)開銷大,而且用戶每次進(jìn)入頁面,都需要等待網(wǎng)絡(luò)請求數(shù)據(jù)時(shí)間,用戶體驗(yàn)較差。
通過數(shù)據(jù)前置獲取,可以把關(guān)鍵頁面的數(shù)據(jù)放在入口頁面獲取,頁面跳轉(zhuǎn)至關(guān)鍵頁面時(shí),將數(shù)據(jù)以頁面參數(shù)的形式傳輸給落地頁。這時(shí)候,用戶看到關(guān)鍵頁面的流程就變成了:點(diǎn)擊頁面入口 → 進(jìn)入關(guān)鍵頁面 → 渲染傳輸過來的數(shù)據(jù)。
用戶需要等待網(wǎng)絡(luò)請求,一般約 200ms--400ms 左右:
關(guān)鍵頁面直接獲取前一頁面拿到的數(shù)據(jù),然后渲染數(shù)據(jù),可以節(jié)省 200ms--400ms 的網(wǎng)絡(luò)時(shí)間:
如果跳轉(zhuǎn)的新頁面頁數(shù)據(jù)量較?。ㄗ侄慰倲?shù)不超過 15 個(gè)、字段層級(jí)不超過 3 層),可以把數(shù)據(jù)放在跳轉(zhuǎn)參數(shù)中傳遞到下一頁:
toNextPage(pageData) {
swan.navigateTo({
url: '/pages/to/next/path?pageData=' + encodeURIComponent(pageData)
});
}
onLoad(option) {
let pageData = option.pageData;
// 轉(zhuǎn)入對應(yīng)的數(shù)據(jù)處理函數(shù)中
if (pageData) {
this.setPageData(pageData);
}
}
如果跳轉(zhuǎn)的新頁面據(jù)量較大(字段總數(shù)在 15 個(gè)以上、字段層級(jí)超過 3 層),數(shù)據(jù)就不合適放在跳轉(zhuǎn)參數(shù)中傳遞,可以放進(jìn)全局變量中:
toNextPage(pageData) {
// 這里使用到的pageData全局變量,需要在app.js中定義
getApp().globalData.pageData = pageData;
swan.navigateTo({
url: '/pages/to/next/path'
});
}
onLoad(option) {
if (getApp().globalData.pageData) {
let pageData = getApp().globalData.pageData;
// 轉(zhuǎn)入對應(yīng)的數(shù)據(jù)處理函數(shù)中
this.setPageData(pageData);
// 清空全局變量
getApp().globalData.pageData = null;
}
}
“關(guān)鍵頁數(shù)據(jù)前置獲取”,本質(zhì)上是把關(guān)鍵頁的數(shù)據(jù)獲取成本轉(zhuǎn)移到了入口頁,所以并非所有的頁面都可以使用。如要使用該手段優(yōu)化某些頁面,則頁面必須滿足如下條件:
在智能小程序中,我們把:從服務(wù)端獲取到的數(shù)據(jù),通過swan.setStorage方法保存在本地,頁面渲染時(shí)直接從本地獲取所需數(shù)據(jù)的這一行為叫做“數(shù)據(jù)持久化”。
在包含了:固定 banner、篩選、導(dǎo)航等這些低時(shí)效性數(shù)據(jù)的頁面里,把這些低時(shí)效性的數(shù)據(jù)存在本地,頁面加載時(shí)直接從本地讀取、渲染數(shù)據(jù),會(huì)使頁面上的留白時(shí)間大大減少,極大的提升用戶體驗(yàn),對白屏率指標(biāo)也將有宜。
注意:對于存放在本地的數(shù)據(jù),并非永遠(yuǎn)不再更新。而是在請求當(dāng)前頁面其他數(shù)據(jù)時(shí),拿到最新的要持久化的數(shù)據(jù),并將新數(shù)據(jù)替換進(jìn)本地,供下次加載使用。
<未使用數(shù)據(jù)緩存的用戶體驗(yàn)
使用數(shù)據(jù)緩存的用戶體驗(yàn)
<
從本地獲取數(shù)據(jù)的方法:
function getLocalData(key) {
return new Promise((resolve, reject) => {
let group = 'homeData'; // 數(shù)據(jù)存儲(chǔ)的組,開發(fā)者可按照頁面進(jìn)行分組,防止不同頁面的數(shù)據(jù)互相覆蓋
swan.getStorage({
key: group + '_' + key,
success: res => {
// console.log('get', res);
let localData = res.data;
resolve(localData);
},
fail: err => {
console.error('讀取失敗', err);
reject(err);
}
});
});
}
module.exports.getLocalData = getLocalData;
向本地存儲(chǔ)數(shù)據(jù)的方法:
function setLocalData (data) {
return new Promise((resolve, reject) => {
let group = 'homeData'; // 數(shù)據(jù)存儲(chǔ)的組,開發(fā)者可按照頁面進(jìn)行分組,,防止不同頁面的數(shù)據(jù)互相覆蓋
let keys = Object.keys(data);
keys.forEach(item => {
swan.setStorage({
key: group + '_' + item,
data: data[item],
success: res => {
resolve(res);
},
fail: err => {
console.error(err);
reject(err);
}
});
});
});
}
module.exports.setLocalData = setLocalData;
const utils = require('./lib'); // 引入存取本地?cái)?shù)據(jù)的公共方法
Page({
onload: function() {
// 從緩存中獲取指定的數(shù)據(jù)
utils.getLocalData('diamondzone').then(diamondzone => {
// 把獲取到的數(shù)據(jù),傳入對應(yīng)的數(shù)據(jù)處理函數(shù)中
if (diamondzone) {
this.setDiamondzoneData(diamondzone);
}
});
}
})
const utils = require('./lib'); // 引入存取本地?cái)?shù)據(jù)的公共方法
Page({
request(path, params) {
swan.request({
url: path, // 接口地址
data: params, // 接口參數(shù)
success: res => {
utils.setLocalData(res.diamondzone); // 將從后端獲取到的最新數(shù)據(jù)存進(jìn)本地
}
})
}
})
Copyright©2021 w3cschool編程獅|閩ICP備15016281號(hào)-3|閩公網(wǎng)安備35020302033924號(hào)
違法和不良信息舉報(bào)電話:173-0602-2364|舉報(bào)郵箱:jubao@eeedong.com
掃描二維碼
下載編程獅App
編程獅公眾號(hào)
聯(lián)系方式:
更多建議: