小程式分屏載入實踐
在小程式不斷迭代的時候,很容易遇到首屏渲染問題。這種問題,可能出現的原因是:小程式包太大,資源需要載入;網路環境太差,下載速度太慢;渲染節點太多,渲染耗時。
針對小程式首次載入包的問題,小程式提出了分包載入的功能,這裡不做詳細描述,可以去看下官方文件
這裡我選擇的是針對渲染節點去做優化。
技術方案
在微信的API文件裡面,有一個判斷節點與可視區域的API
IntersectionObserver 物件,用於推斷某些節點是否可以被使用者看見、有多大比例可以被使用者看見
這個時候就在想,能不能建立IntersectionObserver
跟元件之間的關係,使得元件進入可視區域的時候,就顯示自己的內容,否則隱藏自己,這樣達到動態載入模組的目的。
// 虛擬碼
// 建立監聽
element.observer()
// 處理進入
observer.handleEnterView(() => {
callback() // 處理回撥
disconnect() // 銷燬
})
複製程式碼
<!-- 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>
複製程式碼
開發階段
建立了基本技術方案之後,就開始到程式碼層面了
Component({
data: {
observer_status: true
},
// 在ready寫是因為元件在這個時候,才在檢視層佈局完成
ready () {
// 因為我們是把裝置的整個可視區域當成了觀參照區域,所以這裡直接選擇relativeToViewport,如果需要其他的觀察區域可以呼叫relativeTo選擇參照區域
this.observer = this.createIntersectionObserver().relativeToViewport()
// 我這裡的做法是,只要觀察的節點進入了可視區域,就顯示自己本身的內容
// 實際上這個observer的回撥觸發時機是觀察節點進入或者離開可視區域,我這裡選擇的是,只要執行了就顯示這個區域,並且關閉這個觀察
this.observer.observe(`.observer`, (res) => {
this.setData({
observer_status: false
})
this.observer.disconnect()
this.observer = null
})
},
detached () {
// 如果未進入可視區域就離開了,也需要銷燬自己的觀察
this.observer && this.observer.disconnect()
}
})
複製程式碼
優化
你們以為這就完了麼,並沒有。
對於一個小程式頁面,它是可以由template或者Component組成的。對於template來說,需要在Page裡面定義,而且如果觀察的東西比較多的話,需要設定observeAll:all
,但是官方文件裡面有說同時選中過多節點,將影響渲染效能。
對於元件開發來說,如果每個元件都這樣寫的話,是否也會跟observerAll:all
一樣影響渲染效能,還不清楚,如果確實會影響的話也只能減少觀察物件,或者把做一個大容器去觀察。但是如果每個元件都這樣寫的話也會非常的繁瑣。
這個時候,元件的好處就來了。在定義元件的時候,有一個很神奇的屬性,他就是behaviors
。簡單點說,他其實就是一個程式碼複用機制。直接使用behaviors
可以使得元件自動獲得某些方法,屬性。利用這個特性,就可以在元件裡面少寫很多程式碼了。
// mixin.js
module.exports = Behavior({
data: {
observer_status: true
},
ready () {
this.observer = this.createIntersectionObserver().relativeToViewport()
// 自己統一好observer節點的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>
複製程式碼
或者你可以把整個observer做成元件,這樣去減少observer的數量,內聚一些模組
<!-- Observer.wxml -->
<view class="observer">
<view class="observer-element" wx:if="{{ observer_status}}"></view>
<view class="observer-content" wx:else>
<slot/>
</view>
</view>
複製程式碼
需要注意的是對於元件來說,如果observer的話就需要一個觀察節點,並且這個觀察節點必須是高度不為0的可視物件,如果又想有高度又不想影響頁面位置的話可以用一些hack的方法
.component-observer {
height: 1rpx;
margin-top: -1rpx;
}
複製程式碼
後續一些討論
在使用IntersectionObserver
的時候,有試過用hidden
屬性。但是實際上,hiiden
也是會被渲染出來的,只是不顯示而已,並不會造成頁面載入速度的提升
效果圖
這裡是隨便拿的一個demo去弄的,需要的話可以點選這裡
或者瀏覽小程式程式碼片段https://developers.weixin.qq.com/s/oV1RFfmY7H4W
使用之前
使用之後
如果圖片不動的話可以點選檢視
可以看得出是提升是相當明顯的
後續進階
圖片lazyload方案
image有一個lazy-load
的屬性,但是它只能在page以及在scroll-view使用,如果在其他地方的話是不是也可以用這個去做呢
<!-- 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
})
})
}
})
複製程式碼
滾動到底部/頂部
對於在普通view裡面,如果需要做到底載入的話有scroll-view去做,但是這個效能會比較差,容易出現卡頓,這樣也可以自己封裝一層之後用這個去實現
後話
第一次在這裡發表文章,歡迎大家一起討論,交流心得