组件挂载
vm._render
vm._update
import { patch } from "./vdom/patch";
import Watcher from "./observer/watcher";
export function mountComponent(vm, el) {
// 上一步模板编译解析生成了render函数
// 下一步就是执行vm._render()方法 调用生成的render函数 生成虚拟dom
// 最后使用vm._update()方法把虚拟dom渲染到页面
// 真实的el赋值给实例的$el属性
vm.$el = el;
// _update和._render方法都是挂载在Vue原型的方法 类似_init
// 引入watcher的概念 这里注册一个渲染watcher 执行vm._update(vm._render())方法渲染视图
callHook(vm, "beforeMount");
let updateComponent = () => {
vm._update(vm._render());
};
new Watcher(
vm,
updateComponent,
() => {
callHook(vm, "beforeUpdate");
},
true
);
callHook(vm, "mounted");
}
export function lifecycleMixin(Vue) {
// 把_update挂载在Vue的原型
Vue.prototype._update = function (vnode) {
const vm = this;
const prevVnode = vm._vnode; // 保留上一次的vnode
vm._vnode = vnode;
if (!prevVnode) {
// patch是渲染vnode为真实dom核心
vm.$el = patch(vm.$el, vnode); // 初次渲染 vm._vnode肯定不存在 要通过虚拟节点 渲染出真实的dom 赋值给$el属性
} else {
vm.$el = patch(prevVnode, vnode); // 更新时把上次的vnode和这次更新的vnode穿进去 进行diff算法
}
};
}
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指向当前实例
}
}
}
组件的挂载流程
export function compileToFunctions(template) {
let ast = parse(template);
// ast用来描述代码本身形成树结构 不仅可以描述html 也能描述css以及js语法
// 在ast期间进行优化静态节点等
// 通过ast 重新生成render函数
// 类似_c('div',{id:"app"},_c('div',undefined,_v("hello"+_s(name)),_c('span',undefined,_v("world"))))
// _c代表创建元素 _v代表创建文本 _s代表文Json.stringify--把对象解析成文本
let code = generate(ast);
let renderFn = new Function(`with(this){return ${code}}`);
return renderFn;
}
模板 =>
compileToFunction(template)
=> render 函数render.call(vm)
=> VDOM vnodepatch(oldVnode,vnode)
=> diff 算法 => 更新 VDOMvnode =>
cerateElm(vnode)
=> 产生真实节点给组件创建一个构造函数,基于 Vue
开始生成虚拟节点,对组件进行特殊处理
data.hook = {init(){}}
生成 dom 元素,如果当前虚拟节点有
hook.init
属性,说明是组件对组件进行
new component().$mount
=> vm.$el将组件的$el 插入到父组件中