原文来自个人博客: 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 的能力,对于代码解耦与复用来讲将会更加方便。