vue2源码学习--05Mixin和生命周期核心原理

132 阅读2分钟

本篇目的是实现Mixin核心原理,以及生命周期函数的调用原理。
关于mixin的功能,核心就是选项合并,data就是数据合并,声明周期函数是会把所有混入进来的放入到一个队列里循环调用。
然后我们需要认识下一个静态属性Vue.options,所有mixin的数据会先一步合并存放在该静态属性上,然后才时跟原options进行合并。
先看下原版效果

image.png

image.png

可以看到两个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(){}

看下效果

image.png

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))
     }
}

看下效果

image.png

image.png
ok,生命周期和mixin核心原理写完了,下一篇写不愿面对的数组的依赖收集