小程序分屏加載實踐

小程序分屏加載實踐

在小程序不斷迭代的時候,很容易遇到首屏渲染問題。這種問題,可能出現的緣由是:小程序包太大,資源須要加載;網絡環境太差,下載速度太慢;渲染節點太多,渲染耗時。css

針對小程序首次加載包的問題,小程序提出了分包加載的功能,這裏不作詳細描述,能夠去看下官方文檔html

這裏我選擇的是針對渲染節點去作優化。git

技術方案

在微信的API文檔裏面,有一個判斷節點與可視區域的APIgithub

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()
  }
})
複製代碼

優化

大家覺得這就完了麼,並無。app

對於一個小程序頁面,它是能夠由template或者Component組成的。對於template來講,須要在Page裏面定義,並且若是觀察的東西比較多的話,須要設置observeAll:all,可是官方文檔裏面有說同時選中過多節點,將影響渲染性能。ide

對於組件開發來講,若是每一個組件都這樣寫的話,是否也會跟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去作,可是這個性能會比較差,容易出現卡頓,這樣也能夠本身封裝一層以後用這個去實現

後話

第一次在這裏發表文章,歡迎你們一塊兒討論,交流心得

相關文章
相關標籤/搜索