百度智能小程序 提前請求主數(shù)據(jù)

2020-09-05 15:04 更新

在 onInit 請求首屏主數(shù)據(jù)

基礎(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ì)存在一些難以解決問題:

  1. 如果在App.onLaunch中請求主數(shù)據(jù),那么每個(gè)頁面的請求邏輯都需要在放在App的生命周期中,這樣不僅造成了邏輯的耦合,也將一定程度影響初始數(shù)據(jù)initData的發(fā)送,繼而拖慢渲染層的初次渲染。
  2. 如果在Page.onLoad中請求主數(shù)據(jù),那么必須要等到渲染層完成firstRender之后才能請求主數(shù)據(jù),時(shí)機(jī)比較晚。

onInit 簡介

小程序提供一種頁面級(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)注意:

  1. 如果開發(fā)者setData發(fā)出的時(shí)機(jī)早于渲染線程的 FCP ,那么在onLoad中將能獲取到本次setData的視圖信息。
  2. 如果setData晚于 FCP ,那么onLoad中將獲取不到本次setData的視圖信息。
由于App.onShow是依賴客戶端通知邏輯線程的,所以Page.onInit不一定會(huì)在首次 App.onShow之后執(zhí)行??纱_定的前后順序是:App.onLaunch-> Page.onInit ->Component.created->Component.attached->Page.onLoad

如何使用 onInit

盡量將頁面的業(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
                });
            })
		}
	}
);

使用建議

  1. 不能進(jìn)行任何依賴視圖層的操作,包括且不限于:selectComponentselectAllComponents、swan.createSelectorQueryswan.createMapContext、swan.createCameraContext、swan.createCanvasContext等;
  2. 由于并非所有版本的基礎(chǔ)庫都支持此生命周期,開發(fā)者可以參考上述代碼片段,增加兼容邏輯;
  3. 如果您的小程序在邏輯線程初始化階段存在較大瓶頸,那么使用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



prelink 預(yù)連接

百度App 11.26及以上版本開始支持prelink

prelink 簡介

小程序首屏的渲染,通常依賴某個(gè)網(wǎng)絡(luò)請求返回的數(shù)據(jù),我們將這個(gè)請求定義為主請求。大部分情況下,主請求的速度提升,渲染速度也會(huì)隨之提升。所以,小程序框架提供了prelink機(jī)制,用于提前和業(yè)務(wù)服務(wù)器建立網(wǎng)絡(luò)連接,使隨后的主請求可以復(fù)用該連接,提升請求速度。

  • 使用prelink前后對比 配置prelink前后對比

通過上面的對比可以看出,在配置了 prelink 時(shí),框架會(huì)在加載業(yè)務(wù)代碼 app.js 的同時(shí)發(fā)送一個(gè)預(yù)連接請求。在發(fā)送主請求時(shí),可以直接復(fù)用該連接,達(dá)到減少主請求時(shí)長的目的。


如何使用 prelink

正確使用 prelink,需要做以下兩個(gè)操作:

1.在開發(fā)者平臺(tái)配置 prelink 信息

  • 進(jìn)入開發(fā)者平臺(tái),在【開發(fā)管理】的【設(shè)置】點(diǎn)擊【開發(fā)設(shè)置】。 配置1
  • 找到【服務(wù)器域名】,點(diǎn)擊【修改】。 配置2
  • 在彈出的窗口中,找到【 prelink 的合法地址】,輸入 prelink 地址后點(diǎn)擊【保存并提交】。 配置3
  • 該接口的域名需要和主請求域名保持一致。
  • 支持配置多個(gè)預(yù)連接接口,對不同域名發(fā)起預(yù)連接。
  • 該接口需要支持HEAD請求,其他規(guī)則(如域名校驗(yàn)、UA、REFERER 等)和 request 保持一致。
  • 該接口的響應(yīng)速度和穩(wěn)定性均會(huì)影響 prelink 的效果,為了盡可能的減少預(yù)連接請求的響應(yīng)時(shí)長,建議配置靜態(tài)接口,并保證接口的穩(wěn)定性。

2.在小程序業(yè)務(wù)代碼中,盡可能前置需要復(fù)用預(yù)創(chuàng)建連接的請求(一般為主請求)。


如何測試 prelink

在百度App 中,提供了 prelink 的測試工具,用于開發(fā)者驗(yàn)證配置的正確性,開發(fā)者可按下圖中的流程進(jìn)行測試驗(yàn)證。

preklink測試方法



數(shù)據(jù)前置獲取的優(yōu)勢

通過上文的 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ù)前置獲取

“數(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ù)。

不使用數(shù)據(jù)前置獲取的用戶體驗(yàn)

用戶需要等待網(wǎng)絡(luò)請求,一般約 200ms--400ms 左右:

使用數(shù)據(jù)前置獲取的用戶體驗(yàn)

關(guān)鍵頁面直接獲取前一頁面拿到的數(shù)據(jù),然后渲染數(shù)據(jù),可以節(jié)省 200ms--400ms 的網(wǎng)絡(luò)時(shí)間:

數(shù)據(jù)前置獲取的優(yōu)化方案示例

如果跳轉(zhuǎn)的新頁面頁數(shù)據(jù)量較?。ㄗ侄慰倲?shù)不超過 15 個(gè)、字段層級(jí)不超過 3 層),可以把數(shù)據(jù)放在跳轉(zhuǎn)參數(shù)中傳遞到下一頁:

  1. 在調(diào)用 swan.navigateTo 跳轉(zhuǎn)到落地頁時(shí),將落地頁所需的數(shù)據(jù)拼接在跳轉(zhuǎn) url 參數(shù)中。
    toNextPage(pageData) {
       swan.navigateTo({
             url: '/pages/to/next/path?pageData=' + encodeURIComponent(pageData)
       });
    }
    
  2. 新頁面在 onload 時(shí),從 option 參數(shù)中取數(shù)據(jù),并解析、渲染。
    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)全局變量中:

  1. 在調(diào)用 swan.navigateTo 跳轉(zhuǎn)到落地頁前,將落地頁所需的數(shù)據(jù)存在全局變量中
    toNextPage(pageData) {
       // 這里使用到的pageData全局變量,需要在app.js中定義
       getApp().globalData.pageData = pageData;
       swan.navigateTo({
             url: '/pages/to/next/path'
       });
    }
    
  2. 新頁面在 onload 時(shí),從全局變量里獲取頁面所需的數(shù)據(jù)
    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;
       }
    }
    

使用注意事項(xiàng)

“關(guān)鍵頁數(shù)據(jù)前置獲取”,本質(zhì)上是把關(guān)鍵頁的數(shù)據(jù)獲取成本轉(zhuǎn)移到了入口頁,所以并非所有的頁面都可以使用。如要使用該手段優(yōu)化某些頁面,則頁面必須滿足如下條件:

  1. 數(shù)據(jù)較簡單、數(shù)據(jù)量少(字段總數(shù)不超過 15 個(gè)、字段層級(jí)不超過 3 層);
  2. 該頁面訪問的頻次較高,存在退出該頁面后重新進(jìn)入的情況;


數(shù)據(jù)持久化

在智能小程序中,我們把:從服務(wù)端獲取到的數(shù)據(jù),通過swan.setStorage方法保存在本地,頁面渲染時(shí)直接從本地獲取所需數(shù)據(jù)的這一行為叫做“數(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ù)持久化的優(yōu)化方案示例

  1. 定義存、取本地?cái)?shù)據(jù)的工具方法:

    從本地獲取數(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;
    
  2. 在頁面onload時(shí),調(diào)用getLocalData方法,從本地取數(shù)據(jù)
    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);
                }
            });
        }
    })
    
  3. 在頁面 request 方法的 success 回調(diào)中,調(diào)用setLocalData方法把新數(shù)據(jù)替換到本地
    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)本地
                }
            })
        }
    })
    

使用注意事項(xiàng)

  1. 本地存儲(chǔ)的數(shù)據(jù)總量不宜過大,建議維持在 1M 以內(nèi);
  2. “數(shù)據(jù)持久化”僅限于對時(shí)效性沒有要求的數(shù)據(jù)使用,對于時(shí)效要求高的數(shù)據(jù),不應(yīng)存儲(chǔ)在本地;


以上內(nèi)容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號(hào)
微信公眾號(hào)

編程獅公眾號(hào)