在小程序不斷迭代的時(shí)候,很容易遇到首屏渲染問題。這種問題,可能出現(xiàn)的原因是:小程序包太大,資源需要加載;網(wǎng)絡(luò)環(huán)境太差,下載速度太慢;渲染節(jié)點(diǎn)太多,渲染耗時(shí)。
針對小程序首次加載包的問題,小程序提出了分包加載的功能,這里不做詳細(xì)描述,可以去看下官方文檔
這里我選擇的是針對渲染節(jié)點(diǎn)去做優(yōu)化。
在微信的API文檔里面,有一個(gè)判斷節(jié)點(diǎn)與可視區(qū)域的API
IntersectionObserver 對象,用于推斷某些節(jié)點(diǎn)是否可以被用戶看見、有多大比例可以被用戶看見
這個(gè)時(shí)候就在想,能不能建立 IntersectionObserver 跟組件之間的關(guān)系,使得組件進(jìn)入可視區(qū)域的時(shí)候,就顯示自己的內(nèi)容,否則隱藏自己,這樣達(dá)到動態(tài)加載模塊的目的。
// 偽代碼
// 建立監(jiān)聽
element.observer()
// 處理進(jìn)入
observer.handleEnterView(() => {
callback() // 處理回調(diào)
disconnect() // 銷毀
})
復(fù)制代碼
|
<!-- component -->
<view class="component">
<view class="component-header"></view>
<view class="component-observer" wx:if="{{ observer_status}}"></view>
<view class="component-content" wx:else>
<!-- your content -->
</view>
</view>
|
建立了基本技術(shù)方案之后,就開始到代碼層面了
Component({
data: {
observer_status: true
},
// 在ready寫是因?yàn)榻M件在這個(gè)時(shí)候,才在視圖層布局完成
ready () {
// 因?yàn)槲覀兪前言O(shè)備的整個(gè)可視區(qū)域當(dāng)成了觀參照區(qū)域,所以這里直接選擇relativeToViewport,如果需要其他的觀察區(qū)域可以調(diào)用relativeTo選擇參照區(qū)域
this.observer = this.createIntersectionObserver().relativeToViewport()
// 我這里的做法是,只要觀察的節(jié)點(diǎn)進(jìn)入了可視區(qū)域,就顯示自己本身的內(nèi)容
// 實(shí)際上這個(gè)observer的回調(diào)觸發(fā)時(shí)機(jī)是觀察節(jié)點(diǎn)進(jìn)入或者離開可視區(qū)域,我這里選擇的是,只要執(zhí)行了就顯示這個(gè)區(qū)域,并且關(guān)閉這個(gè)觀察
this.observer.observe('.observer', (res) => {
this.setData({
observer_status: false
})
this.observer.disconnect()
this.observer = null
})
},
detached () {
// 如果未進(jìn)入可視區(qū)域就離開了,也需要銷毀自己的觀察
this.observer && this.observer.disconnect()
}
})
|
你們以為這就完了么,并沒有。
對于一個(gè)小程序頁面,它是可以由template或者Component組成的。對于template來說,需要在Page里面定義,而且如果觀察的東西比較多的話,需要設(shè)置 observeAll:all ,但是官方文檔里面有說同時(shí)選中過多節(jié)點(diǎn),將影響渲染性能。
對于組件開發(fā)來說,如果每個(gè)組件都這樣寫的話,是否也會跟 observerAll:all 一樣影響渲染性能,還不清楚,如果確實(shí)會影響的話也只能減少觀察對象,或者把做一個(gè)大容器去觀察。但是如果每個(gè)組件都這樣寫的話也會非常的繁瑣。
這個(gè)時(shí)候,組件的好處就來了。在定義組件的時(shí)候,有一個(gè)很神奇的屬性,他就是 behaviors。簡單點(diǎn)說,他其實(shí)就是一個(gè)代碼復(fù)用機(jī)制。直接使用 behaviors 可以使得組件自動獲得某些方法,屬性。利用這個(gè)特性,就可以在組件里面少寫很多代碼了。
// mixin.js
module.exports = Behavior({
data: {
observer_status: true
},
ready () {
this.observer = this.createIntersectionObserver().relativeToViewport()
// 自己統(tǒng)一好observer節(jié)點(diǎn)的class
this.observer.observe('.component-observer', (res) => {
this.setData({
observer_status: false
})
this.observer.disconnect()
this.observer = null
})
},
detached () {
this.observer && this.observer.disconnect()
}
})
|
// Component.js
let mixin = require('你的mixin路徑')
Component({
behaviors: [mixin]
})
|
<!-- Component.wxml -->
<view class="component">
<view class="component-header"></view>
<view class="component-observer" wx:if="{{ observer_status}}"></view>
<view class="component-content" wx:else>
<!-- your content -->
</view>
</view>
|
或者你可以把整個(gè)observer做成組件,這樣去減少observer的數(shù)量,內(nèi)聚一些模塊
<!-- Observer.wxml -->
<view class="observer">
<view class="observer-element" wx:if="{{ observer_status}}"></view>
<view class="observer-content" wx:else>
<slot/>
</view>
</view>
|
需要注意的是對于組件來說,如果observer的話就需要一個(gè)觀察節(jié)點(diǎn),并且這個(gè)觀察節(jié)點(diǎn)必須是高度不為0的可視對象,如果又想有高度又不想影響頁面位置的話可以用一些hack的方法
.component-observer {
height: 1rpx;
margin-top: -1rpx;
}
|
在使用 IntersectionObserver 的時(shí)候,有試過用 hidden 屬性。但是實(shí)際上, hiiden 也是會被渲染出來的,只是不顯示而已,并不會造成頁面加載速度的提升
這里是隨便拿的一個(gè)demo去弄的,需要的話可以點(diǎn)擊 這里
或者瀏覽小程序代碼片段 https://developers.weixin.qq.com/s/oV1RFfmY7H4W
使用之前

使用之后

如果圖片不動的話可以點(diǎn)擊查看 可以看得出是提升是相當(dāng)明顯的
image有一個(gè) lazy-load 的屬性,但是它只能在page以及在scroll-view使用,如果在其他地方的話是不是也可以用這個(gè)去做呢
<!-- image-compponent -->
<view class="observer-picture">
<image src="{{ _src }}"></image>
</view>
|
// image-component js
Component({
properties:{
imageSrc: {
type: String,
value: '',
},
},
data: {
_src: "default_image"
},
ready () {
// 偽代碼
observer('.observer-picture')
.then(() => {
this.setData({
_src: this.properties.imageSrc
})
})
}
})
|