用对象保存所有生命周期函数,给用户传入 options 中的钩子函数绑定 this 指向当前实例
import Vue from '../module/Vue';
var vm = new Vue({
...
beforeCreate() {
console.log('beforeCreate')
},
created() {
console.log('created')
},
beforeMount() {
console.log('beforeMount')
},
mounted() {
console.log('mounted')
this.isShowImg1 = false;
this.isShowImg2 = false;
},
...
});
console.log(vm);
var Vue = (function() {
function Vue(options) {
// 用对象保存生命周期函数
var recycles = {
beforeCreate: options.beforeCreate.bind(this),
created: options.created.bind(this),
beforeMount: options.beforeMount.bind(this),
mounted: options.mounted.bind(this)
}
// init之前,调用 beforeCreate
recycles.beforeCreate(); // 得在上边改this指向,否则指向的是recycles
this.$el = document.querySelector(options.el);
this.$data = options.data();
// 初始化
this._init(this, options.template, options.methods, recycles);
}
Vue.prototype._init = function(vm, template, methods, recycles) {
var container = document.createElement('div');
container.innerHTML = template;
var showPool = new Map();
var eventPool = new Map();
initData(vm, showPool);
// 创建完成调用 created
recycles.created();
initPool(container, methods, showPool, eventPool);
bindEvent(vm, eventPool);
render(vm, showPool, container, recycles);
}
function initData(vm, showPool) {
var _data = vm.$data;
for (var key in _data) {
(function(key) {
Object.defineProperty(vm, key, {
get: function() {
return _data[key];
},
set: function(newVal) {
_data[key] = newVal;
update(vm, key, showPool)
}
});
})(key);
}
}
function initPool(container, methods, showPool, eventPool) {
var _allNodes = container.getElementsByTagName('*');
var dom = null;
for (var i = 0; i < _allNodes.length; i++) {
dom = _allNodes[i];
var vIfData = dom.getAttribute('v-if');
var vShowData = dom.getAttribute('v-show');
var vEvent = dom.getAttribute('@click');
if (vIfData) {
showPool.set(
dom,
{
type: 'if',
prop: vIfData
}
)
dom.removeAttribute('v-if');
} else if (vShowData) {
showPool.set(
dom,
{
type: 'show',
prop: vShowData
}
)
dom.removeAttribute('v-show');
}
if (vEvent) {
eventPool.set(
dom,
methods[vEvent]
)
dom.removeAttribute('@click');
}
}
console.log(eventPool);
}
function bindEvent(vm, eventPool) {
for (var [dom, handler] of eventPool) {
vm[handler.name] = handler;
dom.addEventListener('click', vm[handler.name].bind(vm), false);
}
}
function render(vm, showPool, container, recycles) {
var _data = vm.$data;
var _el = vm.$el;
for (var [dom, info] of showPool) {
switch (info.type) {
case 'if':
info.comment = document.createComment(['v-if']);
// 如果为假,就用 comment 节点替换 dom 节点
// replaceChild(newNode, oldNode);
!_data[info.prop] && dom.parentNode.replaceChild(info.comment, dom);
break;
case 'show':
// 如果为假,就设置display为none
!_data[info.prop] && (dom.style.display = 'none');
break;
default:
break;
}
}
// 挂载之前
recycles.beforeMount();
_el.appendChild(container);
// 挂载之后
recycles.mounted();
}
function update(vm, key, showPool) {
var _data = vm.$data;
for (var [dom, info] of showPool) {
if (info.prop === key) {
switch (info.type) {
case 'if':
!_data[key] ? dom.parentNode.replaceChild(info.comment, dom)
: info.comment.parentNode.replaceChild(dom, info.comment);
break;
case 'show':
!_data[key] ? (dom.style.display = 'none')
: (dom.removeAttribute('style'));
break;
default:
break;
}
}
}
}
return Vue;
})();
export default Vue;
当 Vue 组件实例化时,会依次调用各个生命周期钩子。这些钩子在组件的不同阶段被调用,如下:
beforeCreate
:此时组件实例已创建,但 data
和 props
还未初始化。created
:此时组件实例已经创建完成,data
、props
已经初始化,但还没有挂载 DOM。beforeMount
:此时组件已准备好挂载,DOM 尚未渲染。mounted
:此时组件已挂载到真实 DOM,可以访问 DOM 元素。beforeUpdate
:当响应式数据更新时,此钩子会在虚拟 DOM 重新渲染之前调用。updated
:虚拟 DOM 重新渲染并应用更新到真实 DOM 后调用。beforeDestroy
:在实例销毁之前调用,此时实例仍然完全可用。destroyed
:在实例销毁后调用,此时组件的所有事件监听器、子实例、以及 DOM 绑定都已销毁。