Skip to content

五、生命周期的合并

0、生命周期方法的使用

js
// 1、全局mixin混入使用
Vue.mixin({
	created: function a() {
		console.log('created 1');
	}
})

// 2、在options中以属性方式使用
let vm = new Vue({
	el: '#app',
	created() {
		// 生命周期就是回调函数,先订阅号,后续触发
		console.log('created 3');
	}
})

1、Mixin原理

js
// src\global-api\index.js

import { mergeOptions } from "../../util";

// 定义全局API
export function initGlobalApi(Vue) {
    Vue.options = {};

	// 定义混入的静态方法
    Vue.mixin = function(mixin) {
        // 合并对象-生命周期
        this.options = mergeOptions(this.options, mixin);
    }
}

2、合并生命周期

js
// 01vue\util.js

// 定义完整的生命周期数组
export const LIFECYCLE_HOOKS = [
    'beforeCreate',
    'created',
    'beforeMount',
    'mounted',
    'beforeUpdate',
    'updated',
    'beforeDestroy',
    'destroyed'
];

// 使用策略模式进行不同方法的调用
const strats = {};
strats.data = function(parentVal, childVal) {
    return childVal;
}
strats.computed = function() {}
strats.watch = function() {}

// 生命周期的合并
function mergeHook(parentVal, childVal) {
    if (childVal) {
        // 如果儿子有值,然后根据父亲是否有值来处理
        if (parentVal) {
            // 爸爸和儿子进行拼接
            return parentVal.concat(childVal);
        } else {
            // 儿子需要转化为数组
            return [childVal];
        }
    } else {
        // 如果只有父亲有值,不合并,直接采用父亲的
        return parentVal;
    }
}

// 定义生命周期的策略方法
LIFECYCLE_HOOKS.forEach(hook => {
    strats[hook] = mergeHook;
})

// 对象合并方法
export function mergeOptions(parent, child) {
    const options = {};

    // 1、处理父亲有,儿子有或没有的情况
    for (let key in parent) {
        mergeField(key);
    }

    // 2、处理儿子有,父亲没有:把儿子多余的属性赋予到父亲上
    for (let key in child) {
        if (!parent.hasOwnProperty(key)) {
            mergeField(key);
        }
    }

    function mergeField(key) {
        // 根据key,采取不同的策略合并
        if (strats[key]) {
            options[key] = strats[key](parent[key], child[key]);
        } else {
            // todo 默认合并
            options[key] = child[key];
        }
    }

    return options;
}

3、定义生命周期的调用函数

js
// src\lifecycle.js

// 定义生命周期调用方法
export function callHook(vm, hook) {
    // 是数组
    const handlers = vm.$options[hook];
    if (handlers) {
        for (let i = 0; i < handlers.length; i++) {
            handlers[i].call(vm); // 更改生命周期中的this
        }
    }
}

4、在各个节点调用生命周期

js
// 调用beforeCreate和created
Vue.prototype._init = function(options) {
	// 拿到当前实例
	const vm = this

	// 需要将用户自定义的options和全局的options做合并
	vm.$options = mergeOptions(vm.constructor.options, options);

	callHook(vm, 'beforeCreate');
	initState(vm)
	callHook(vm, 'created');

	// 如果当前有el属性,需要进行模板渲染
	if (vm.$options.el) {
		vm.$mount(vm.$options.el)
	}
}

// 调用beforeMount和mounted
export function mountComponent(vm, el) {
    vm.$el = el;

    callHook(vm, 'beforeMount');
	 // 先调用render方法创建虚拟节点,再将虚拟节点渲染到页面上
    vm._update(vm._render());
    callHook(vm, 'mounted');
}

Released under the MIT License.