Vue 提供了一種非常靈活的混入(mixins)方式,用於分發 Vue 組件中可復用的功能。微信小程序的 Component 使用 behaviors 可以實現類似 Vue 中的 mixins。
Page 創建頁面不支持 behaviors 或 mixins,怎麼在不同頁面實現代碼復用與共享呢?
有兩種方式:
使用 Component 創建頁面頁面本身就可以看做是組件,將 Page 構造器直接替換為 Component 創建頁面,可以使用 behaviors 實現頁面邏輯的復用。
Component 構造器與 Page 構造器的主要區別是把頁面的方法定義在methods: {}對象中。
my-behavior.js
export default Behavior({
data: {
userAge: 25
},
created: function () {
console.log('mixin created')
},
methods: {
printOutUserAge () {
console.log('mixin data userAge:', this.data.userAge)
}
}使用 Behavior 來包裝混入對象。
index.js
import myBehavior from "./my-behavior"
Component({
behaviors: [myBehavior],
data: {
name: 'ejtoia'
},
created: function () {
console.log('Page create')
this.printOutUserName()
},
methods: {
printOutUserName() {
this.printOutUserAge()
console.log(this.data.name)
},
onShareAppMessage: function () {
// 頁面被用戶分享時執行
},
onReachBottom: function() {
// 頁面觸底執行
}
}
})index 頁面 log 依次輸出為:
mixin created
Page created
mixin data userAge:25
ejtoiaIndex 頁面的 onShareAppMessage、onReachBottom 等等方法都定義在 Component 構造器的 methods 對象中。
behaviors 同名欄位的覆蓋和組合規則查閱官方文檔:
https://developers.weixin.qq.com/miniprogram/dev/framework/custom-component/behaviors.html擴展 Page 的 mixins 選項原理很好理解。先保存原始的 Page 構造器函數,然後重新定義新的 Page 構造器函數。
這相當於加入一個中間函數,用來處理 options 選項對象的 mixins 值,把 mixins 中的每一個混入對象(包括生命周期函數、data、方法等)按照一定的規則合併到 options 選項對象中,最後再調用原始 Page(options) 構造器函數創建頁面。
wx-mixins.js 入口文件:
// 保存原始的 Page
const originPage = Page
// 重新定義 Page
Page = options => {
// 處理 mixins
const mixins = options.mixins
if (Array.isArray(mixins)) {
options = mergeOptions(mixins, options)
delete options.mixins
}
// 執行原始的 Page
originPage(options)
}將 mixins 中每一個混入對象都合併到 options 選項對象中,在開始 mergeOptions 函數之前,先來規定一下 mixins 的合併規則。
mixins 同名欄位的覆蓋與合併規則同名生命周期函數都會被調用,混入對象的生命周期函數在頁面生命周期函數之前被調用
data 對象在內部會進行淺合併,並在命名發生衝突時以頁面數據優先
混入對象的屬性或方法命名衝突時,頁面定義的屬性或方法優先
mergeOptions 實現// Page 生命周期函數列表
const pageHooks = [
"onLoad",
"onShow",
"onReady",
"onHide",
"onUnload",
"onPullDownRefresh",
"onReachBottom",
"onShareAppMessage",
"onPageScroll",
"onResize",
"onTabItemTap"
]
const hasOwnProperty = Object.prototype.hasOwnProperty;
const toString = Object.prototype.toString
function toRawType(value) {
return toString.call(value).slice(8, -1);
}
function isPlainObject(value) {
return toRawType(value) === "Object";
}
function isFunction(value) {
return toRawType(value) === "Function";
}
function hasOwn(obj, key) {
return hasOwnProperty.call(obj, key);
}
function mergeOptions (mixins, options) {
mixins.forEach(mixin => {
if (!isPlainObject(mixin)) {
throw new Error("typeof mixin must be plain object")
}
// 混入對象中嵌套混入對象,遞歸合併
if (mixin.mixins) {
mixin = mergeOptions(mixin.mixins, mixin, hooks)
}
// 處理混入對象中每一個值, 可能是生命周期函數、可能是 data 或 方法
for (const key in mixin) {
// 暫存頁面中初始的值
const originValue = options[key]
// 暫存混入對象的值
const mixinValue = mixin[key]
// 處理混入對象的生命周期函數
if (pageHooks.includes(key)) {
if (!isFunction(mixinValue)) {
throw new Error(`typeof ${key} must be function`)
}
// 重寫頁面的生命周期函數
options[key] = function () {
let res;
// 先執行混入對象的生命周期函數
res = mixinValue.apply(this, arguments)
// 後執行頁面中的生命周期函數
if (originValue) res = originValue.apply(this, arguments)
return res
}
}
// 混入對象的值是對象
else if (isPlainObject(mixinValue)) {
options[key] = {
...mixinValue,
...originValue
}
}
// 混入對象的屬性或方法
else {
// 頁面中不存在同名的屬性或方法時,才使用混入對象的屬性或方法
if (!hasOwn(options, key)) {
options[key] = mixinValue
}
}
}
})
return options
}
使用 mixins小程序入口文件 app.js 中引入 wx-mixins.js 擴展的實現後,那麼所有使用 Page 創建的頁面都可以使用 mixins 選項。
app.js
import "./wx-mixins"my-mixins.js
export default {
data: {
userAge: 25
},
onLoad: function () {
console.log('mixin onLoad')
},
printOutUserAge () {
console.log('mixin data userAge:', this.data.userAge)
}默認導出一個混入對象,包含多個頁面共用的生命周期、data 和方法。
index.js
import myMixin from "./my-mixins"
Page({
mixins: [index_mixins],
data: {
name: 'ejtoia'
},
onLoad: function () {
console.log('Page onLoad')
this.printOutUserName()
},
printOutUserName() {
this.printOutUserAge()
console.log(this.data.name)
},
onShareAppMessage: function () {
// 頁面被用戶分享時執行
},
onReachBottom: function() {
// 頁面觸底執行
}
})Index 頁面使用混入對象後的 log 輸出結果:
mixin onLoad
Page onLoad
mixin data userAge:25
ejtoiamixins 混入對象相比 Component 創建頁面,可以直接使用 Page 創建頁面,不需要將屬性或方法定義到 methods: {} 中。
總結在微信小程序中 Page 使用比較頻繁,為了方便頁面的邏輯代碼復用,擴展 Page 的 mixins 選項,它最核心的實現是 mergeOptions 合併函數。
也可以給 App、Component 構造器函數也擴展 mixins 選項,因為 App 使用不多,Component 已經內置了 behaviors 選項,所以這兩個擴展 mixins 的意義不是很大。
參考https://github.com/kallsave/wx-miniapp-mixinshttps://cn.vuejs.org/v2/guide/mixins.htmlhttps://developers.weixin.qq.com/miniprogram/dev/framework/custom-component/behaviors.html