vue2源码(九)-- 挂载阶段

38 篇文章 2 订阅
14 篇文章 0 订阅

原理分析

1、$mounted函数实际上调用的是mountComponent函数,该函数首先判断是否存在render函数,如果不存在,则给一个默认的渲染函数,该渲染函数会创建一个注释类型的VNode节点,然后给出警告。

2、触发beforeMount声明周期函数

3、定义updateComponent函数,该函数内部首先执行render渲染函数,得到一份最新的VNode节点树,然后执行_update方法对最新的Vnode节点和上一次的VNode节点进行对比,并更新DOM元素(即patch操作),完成一次渲染。这样子完成了挂载操作的一半工作。

4、另一半工作是开启对模板中的数据监控,当数据发生变化时,通知期其依赖进行视图更新。首先创建一个watcher实例,并将定义好的updateComponent函数作为第二个参数传入,watcher 构造函数的第二个参数支持 2 种类型,一种是数据路径,另外一种是函数。如果是数据路径,就会根据路径去读取这个数据,如果是函数,就会执行函数,一旦读取了数据或者执行了函数,就会触发数据的getter方法,getter方法会将watcher实例添加到改数据的依赖列表中,,当数据发生变化时,就会通知依赖列表进行更新,当依赖接收到通知后,就会调用第四个参数回调函数去更新视图。

换句话来说,updateComponent函数所使用到的数据都会被watcher监控,只要这些数据发生了变化,那么watcher都将会得到通知,从而掉用第四个参数回调函数去更新视图

源码

// hydrating是跟服务端渲染相关的
Vue.prototype.$mount = function (
  el?: string | Element,
  hydrating?: boolean
): Component {
  el = el && inBrowser ? query(el) : undefined;
  return mountComponent(this, el, hydrating);
};
// $mount实际调用的是这个函数
export function mountComponent(
  vm: Component,
  el: ?Element,
  hydrating?: boolean
): Component {
  vm.$el = el;
  // $mount最终需要的是render函数
  if (!vm.$options.render) {
    // 没有渲染函数的情况下会默认给一个渲染函数
    vm.$options.render = createEmptyVNode;
    // ...
  }
  // 触发beforeMount钩子函数
  callHook(vm, "beforeMount");

  let updateComponent;
  if (process.env.NODE_ENV !== "production" && config.performance && mark) {
    updateComponent = () => {
      // ...
      const vnode = vm._render();
      vm._update(vnode, hydrating);
    };
  } else {
    updateComponent = () => {
      vm._update(vm._render(), hydrating);
    };
  }

  // Watcher2个作用,一是初始化的时候执行回调函数,二是当vm实例中检测的数据发生变化就执行回调函数
  // 渲染watcher
  // 如果是渲染watcher,在初始化的时候会把vm._watcher赋值为watcher的实例
  new Watcher(
    vm,
    updateComponent,
    noop,
    {
      before() {
        if (vm._isMounted && !vm._isDestroyed) {
          // 如果数据发生了变化,并且组件在渲染状态并且没有销毁,触发beforeUpdate生命周期函数
          callHook(vm, "beforeUpdate");
        }
      },
    },
    true /* isRenderWatcher */
  );
  hydrating = false;

  // vm.$vnode:Vue 实例的父虚拟 Node,为null表示为根vue实例
  if (vm.$vnode == null) {
    // 根实例
    vm._isMounted = true; // 标识已经挂载
    callHook(vm, "mounted");
  }
  return vm;
}
// 把vnode渲染成真实的dom
Vue.prototype._update = function (vnode: VNode, hydrating?: boolean) {
  const vm: Component = this;
  const prevEl = vm.$el;
  const prevVnode = vm._vnode;
  const restoreActiveInstance = setActiveInstance(vm);
  vm._vnode = vnode;
  // Vue.prototype.__patch__ is injected in entry points
  // based on the rendering backend used.
  if (!prevVnode) {
    // 没有旧prevVnode就是第一次渲染
    vm.$el = vm.__patch__(vm.$el, vnode, hydrating, false /* removeOnly */);
  } else {
    // 更新
    vm.$el = vm.__patch__(prevVnode, vnode);
  }
  restoreActiveInstance();
  // update __vue__ reference
  if (prevEl) {
    prevEl.__vue__ = null;
  }
  if (vm.$el) {
    vm.$el.__vue__ = vm;
  }
  if (vm.$vnode && vm.$parent && vm.$vnode === vm.$parent._vnode) {
    vm.$parent.$el = vm.$el;
  }
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值