小程式開發實用技巧——擴充套件 Page 頁面物件

lijiashen發表於2018-07-23

擴充套件原生 Page 物件

小程式是通過呼叫 Page 函式來註冊一個頁面的:

//index.js
Page({
  data: {
    text: "This is page data."
  },
  onLoad: function(options) {
    // Do some initialize when page load.
  },
  // Event handler.
  viewTap: function() {
    this.setData({
      text: 'Set some data for updating view.'
    }, function() {
      // this is setData callback
    })
  }
})
複製程式碼

這裡Page的作用相當於建構函式,Page會初始化頁面物件(例項),然後將配置引數中的屬性 merge 到頁面物件上。

假設你封裝了個http模組負責發出請求,你想在頁面物件中直接通過this.http引用這個模組,就需要擴充套件頁面物件。要擴充套件一個物件,在 JavaScript 中的常見做法是擴充套件建構函式的prototype屬性,這是Vue很多外掛的實現:

import axios from 'axios'
Vue.prototype.axios = axios
// 在 vue 元件中
this.axios.get(api).then(callback)
複製程式碼

很不幸,在小程式中這個辦法無效。Page並不是普通的建構函式,底層還做了很多其他事情,沒辦法直接通過Page.prototype擴充套件頁面物件。

我們可以轉變思路,擴充套件傳進Page的配置物件。既然始終要通過呼叫Page註冊頁面,可以定義一個函式,這個函式會將收到的配置物件引數進行處理,然後再傳給Page

// wxPage.js
import http from '../utils/http'

const wxPage = function(config) {
  config.http = http
  return Page(config)
}

export default wxPage
複製程式碼

註冊頁面的時候改用這個wxPage

import Page from './wxPage'

Page({
  data: {
    text: "This is page data."
  },
  onLoad: function(options) {
    console.log(this.http) // 列印 http 模組變數
    this.http.get(api).then(callback) // 直接呼叫 http 的方法
  },
})
複製程式碼

直接修改 Page 函式

為了增強頁面物件,每個需要的頁面都得引入 wxPage 是一件不太省心的事;更多時候我們是在維護一個老專案,需要擴充套件每個原有的頁面物件,這時可以直接修改 Page

const originalPage = Page //儲存原來的Page
Page = function(config) { // 覆蓋Page變數
  config.http = http
  return originalPage(config)
}
複製程式碼

一般來說,修改 Page 的時機是在App onLoad 的時候。這樣原有的頁面不用修改,直接就能通過this.http拿到http

通過擴充套件 Page 頁面物件實現常見需求

1. 給生命週期方法增加通用邏輯

有時我們希望在頁面註冊的onLoad階段執行一些通用的邏輯,例如埋點,打 log 等,這時可以改寫配置物件中的 onLoad 方法:

const originalPage = Page
Page = function(config) {
  const { onLoad } = config
  config.onLoad = function(onLoadOptions) {
    // 打 log、埋點……
    console.log('每個頁面都會打出這個log')
    if (typeof onLoad === 'function') {
      onLoad.call(this, onLoadOptions)
    }
  }
  return originalPage(config)
}
複製程式碼

2. 獲取上一頁頁面物件

小程式中的頁面跳轉會形成一個頁面棧,棧中存放著每個頁面物件,可以通過 getCurrentPages 方法獲得這個頁面棧。可以在頁面 onLoad 的時候獲取這個頁面棧,然後取出倒數第二個物件,就是當前頁上一頁的頁面物件:

// 接上...
  const { onLoad } = config
  config.onLoad = function(onLoadOptions) {
    const pages = getCurrentPages()
    this.__previousPage = pages[pages.length - 2] // 將上一頁的頁面物件賦為this.__previousPage
    if (typeof onLoad === 'function') {
      onLoad.call(this, onLoadOptions)
    }
  }
  return originalPage(config)
複製程式碼

這樣在頁面物件中可通過引用 this.__previousPage 獲取上一頁頁面物件的data及所有方法,這樣在一些只需要兩個頁面互動的情景下,當前頁直接呼叫上一個頁面物件的方法(相當於回撥)後再返回,比通過全域性狀態管理上一頁的資料要方便。

3. 跳轉頁面並傳遞資料到下一頁

這個不多說了,直接看程式碼吧:

// 接上
config.navigateTo = function(url, params) { // 實現一個navigateTo方法,引數包括跳轉url和要傳遞的引數
  this.__params = params
  wx.navigateTo({ url })
}

config.onLoad = function(onLoadOptions) {
  const pages = getCurrentPages()
  this.__previousPage = pages[pages.length - 2] // 將上一頁的頁面物件賦為this.__previousPage
  if (this.__previousPage) {
    onLoadOptions.params = this.__previousPage.__params // 獲取上一頁面的__params賦給onLoad函式的options
    delete this.__previousPage.__params
  }
  if (typeof onLoad === 'function') {
    onLoad.call(this, onLoadOptions)
  }
}

// A 頁面跳轉 B 頁面
this.navigateTo('urlToB', { foo: 'bar' })

// B 頁面的 onLoad
Page({
  onLoad(options) {
    console.log(options.params) // { foo: 'bar' }
  }
})
複製程式碼

就寫到這裡吧,在使用原生方案開發的時候,這些技巧還是挺實用的。以後再寫寫怎樣構建小程式,使小程式支援檔案預編譯、require npm 包等。

相關文章