本篇目的是实现Mixin核心原理,以及生命周期函数的调用原理。
关于mixin的功能,核心就是选项合并,data就是数据合并,声明周期函数是会把所有混入进来的放入到一个队列里循环调用。
然后我们需要认识下一个静态属性Vue.options,所有mixin的数据会先一步合并存放在该静态属性上,然后才时跟原options进行合并。
先看下原版效果
可以看到两个mixin的created,在Vue.options上是以数组的形式存放的。
这其实就是一种发布订阅,我们先把订阅搞好,具体在哪发布直接调用即可。
src/gloablAPI.js
// 这个方法也要在入口文件调用下
export function initGloablAPI(Vue) {
// 静态方法
Vue.options = {}
Vue.mixin = function(mixin) {
// {} {created(){}} => {created:[fn]}
// {created:[fn]} {created(){}} => {created:[fn, fn]}
// 合并方法 this指向Vue构造函数
this.options = mergeOptions(this.options, mixin)
return this
}
}
实现mergeOptions src/util.js
// 循环所有传进来的对象,先传进来的为父,后传进来的为子
// 每次调用 mergeField 都会给options加一个属性
export function mergeOptions(parent, child) {
const options = {}
for (let key in parent) {
mergeField(key)
}
for (let key in child) {
if(!parent.hasOwnProperty()) {
mergeField(key)
}
}
function mergeField(key) {
// 策略模式 减少if else
if(strats[key]){
options[key] = strats[key](parent[key], child[key])
} else {
// 不在策略中以儿子为主
options[key] = child[key] || parent[key]
}
}
return options
}
options选项有很多,不同的属性有不同的合并方式,如果通过判断key来做不同的合并策略的话,会有大量的if else判断,所以采用策略模式减少if else
以下是部分勾子函数的实现,结果就是生成类似 created: [fn,fn]的形式
const strats = {}
const LIFECYCLE = [
'beforeCreated',
'created'
]
LIFECYCLE.forEach(hook => {
strats[hook] = function(p, c) {
if(c) {
if(p) {
return p.concat(c)
} else {
return [c]
}
}else {
return p
}
}
})
// 不改变主逻辑 不同的选项采用不同的合并方式
// strats.computed = function(){}
// strats.watch = function(){}
// strats.methods = function(){}
看下效果
ok我们写的vue也实现了静态属性上存放合并好的mixin选项,再然后我们需要将合并好的mixin的选项数据和原options数据进行合并。
之前我们为了方便使用options直接将options赋值给vm.$options挂到了vue的属性上,写到这我们就可以调用生命周期函数了,对之前的_init做下修改
Vue.prototype._init = function(options) {
const vm = this;
// this指向实例化后,需要到构造函数上的静态属性
vm.$options = mergeOptions(this.constructor.options,options)
// 数据劫持之前
callHook(vm, 'beforeCreate')
initState(vm);
// 数据劫持之后 页面挂载前
callHook(vm, 'created')
if(options.el) {
vm.$mount(options.el)
}
}
就是发布订阅,在合适的时机调用,下面我们实现callHook,以上我们其实已经知道了生命周期函数是以数组的形式存放的,所以函数本身就是循环调用$options上对应数组里的回调函数。
lifecycle.js
export function callHook(vm, hook) {
const handlers = vm.$options[hook]
if(handlers) {
handlers.forEach(handler => handler.call(vm))
}
}
看下效果
ok,生命周期和mixin核心原理写完了,下一篇写不愿面对的数组的依赖收集