擴充套件原生 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 包等。