组件挂载

  • 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;
}
  1. 模板 =>compileToFunction(template) => render 函数

  2. render.call(vm) => VDOM vnode

  3. patch(oldVnode,vnode) => diff 算法 => 更新 VDOM

  4. vnode => cerateElm(vnode) => 产生真实节点

  5. 给组件创建一个构造函数,基于 Vue

  6. 开始生成虚拟节点,对组件进行特殊处理 data.hook = {init(){}}

  7. 生成 dom 元素,如果当前虚拟节点有hook.init属性,说明是组件

  8. 对组件进行new component().$mount => vm.$el

  9. 将组件的$el 插入到父组件中