原文來自個人博客: https://jrainlau.github.io/#/...
在原生開發小程序的過程當中,發現有多個頁面都使用了幾乎徹底同樣的邏輯。因爲小程序官方並無提供 Mixins 這種代碼複用機制,因此只能採用很是不優雅的複製粘貼的方式去「複用」代碼。隨着功能愈來愈複雜,靠複製粘貼來維護代碼顯然不科學,因而便尋思着如何在小程序裏面實現 Mixins。javascript
Mixins 直譯過來是「混入」的意思,顧名思義就是把可複用的代碼混入當前的代碼裏面。熟悉 VueJS 的同窗應該清楚,它提供了更強大了代碼複用能力,解耦了重複的模塊,讓系統維護更加方便優雅。java
先看看在 VueJS 中是怎麼使用 Mixins 的。git
// define a mixin object var myMixin = { created: function () { this.hello() }, methods: { hello: function () { console.log('hello from mixin!') } } } // define a component that uses this mixin var Component = Vue.extend({ mixins: [myMixin] }) var component = new Component() // => "hello from mixin!"
在上述的代碼中,首先定義了一個名爲 myMixin
的對象,裏面定義了一些生命週期函數和方法。接着在一個新建的組件裏面直接經過 mixins: [myMixin]
的方式注入,此時新建的組件便得到了來自 myMixin
的方法了。github
明白了什麼是 Mixins 之後,即可開始着手在小程序裏面實現了。小程序
Mixins 也有一些小小的細節須要注意的,就是關於生命週期事件的執行順序。在上一節的例子中,咱們在 myMixin
裏定義了一個 created()
方法,這是 VueJS 裏面的一個生命週期事件。若是咱們在新建組件 Component
裏面也定義一個 created()
方法,那麼執行結果會是如何呢?segmentfault
var Component = Vue.extend({ mixins: [myMixin], created: function () { console.log('hello from Component!') } }) var component = new Component() // => // Hello from mixin! // Hello from Component!
能夠看運行結果是先輸出了來自 Mixin 的 log,再輸出來自組件的 log。數組
除了生命週期函數之外,再看看對象屬性的混入結果:app
// define a mixin object const myMixin = { data () { return { mixinData: 'data from mixin' } } } // define a component that uses this mixin var Component = Vue.extend({ mixins: [myMixin], data () { return { componentData: 'data from component' } }, mounted () { console.log(this.$data) } }) var component = new Component()
在 VueJS 中,會把來自 Mixins 和組件的對象屬性當中的內容(如 data
, methods
等)混合,以確保兩邊的數據都同時存在。ide
通過上述的驗證,咱們能夠獲得 VueJS 中關於 Mixins 運行機制的結論:svg
可是在小程序中,這套機制會和 VueJS 的有一點區別。在小程序中,自定義的方法是直接定義在 Page 的屬性當中的,既不屬於生命週期類型屬性,也不屬於對象類型屬性。爲了避免引入奇怪的問題,咱們爲小程序的 Mixins 運行機制多加一條:
在小程序中,每一個頁面都由 Page(options)
函數定義,而 Mixins 則做用於這個函數當中的 options
對象。所以咱們實現 Mixins 的思路就有了——劫持並改寫 Page
函數,最後再從新把它釋放出來。
新建一個 mixins.js
文件:
// 保存原生的 Page 函數 const originPage = Page Page = (options) => { const mixins = options.mixins // mixins 必須爲數組 if (Array.isArray(mixins)) { delete options.mixins // mixins 注入並執行相應邏輯 options = merge(mixins, options) } // 釋放原生 Page 函數 originPage(options) }
原理很簡單,關鍵的地方在於 merge()
函數。merge
函數即爲小程序 Mixins 運行機制的具體實現,徹底按照上一節總結的三條結論來進行。
// 定義小程序內置的屬性/方法 const originProperties = ['data', 'properties', 'options'] const originMethods = ['onLoad', 'onReady', 'onShow', 'onHide', 'onUnload', 'onPullDownRefresh', 'onReachBottom', 'onShareAppMessage', 'onPageScroll', 'onTabItemTap'] function merge (mixins, options) { mixins.forEach((mixin) => { if (Object.prototype.toString.call(mixin) !== '[object Object]') { throw new Error('mixin 類型必須爲對象!') } // 遍歷 mixin 裏面的全部屬性 for (let [key, value] of Object.entries(mixin)) { if (originProperties.includes(key)) { // 內置對象屬性混入 options[key] = { ...value, ...options[key] } } else if (originMethods.includes(key)) { // 內置方法屬性混入,優先執行混入的部分 const originFunc = options[key] options[key] = function (...args) { value.call(this, ...args) return originFunc && originFunc.call(this, ...args) } } else { // 自定義方法混入 options = { ...mixin, ...options } } } }) return options }
在小程序的 app.js
裏引入 mixins.js
require('./mixins.js')
撰寫一個 myMixin.js
module.exports = { data: { someData: 'myMixin' }, onShow () { console.log('Log from mixin!') } }
在 page/index/index.js
中使用
Page({ mixins: [require('../../myMixin.js')] })
大功告成!此時小程序已經具有 Mixins 的能力,對於代碼解耦與複用來講將會更加方便。