在小程序不斷迭代的時候,很容易遇到首屏渲染問題。這種問題,可能出現的原因是:小程序包太大,資源需要加載;網絡環境太差,下載速度太慢;渲染節點太多,渲染耗時。
針對小程序首次加載包的問題,小程序提出了分包加載的功能,這里不做詳細描述,可以去看下官方文檔
這里我選擇的是針對渲染節點去做優化。
在微信的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
使用之前
使用之后
如果圖片不動的話可以點擊查看 可以看得出是提升是相當明顯的
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 }) }) } }) |