读Vue2源码(2)-mountComponent

120 阅读3分钟

mountComponent

我们一样忽略掉这个方法中其他的操作,看到 mountComponent 核心就是先实例化一个渲染 Watcher,在它的回调函数中会调用 updateComponent 方法,在此方法中调用 vm._render 方法先生成虚拟 Node,最终调用 vm._update 更新 DOMWatcher 在这里起到两个作用,一个是初始化的时候会执行回调函数,另一个是当 vm 实例中的监测的数据发生变化的时候执行回调函数。

接下来我们要重点分析其中的细节,也就是最核心的 2 个方法:vm._render 和 vm._update

updateComponent = function () {
   vm._update(vm._render(), hydrating);
};

vm._render

用来把 render 渲染成一个 vNode

vnode = render.call(vm._renderProxy, vm.$createElement);

这个 render 方法就是在 compileToFunctions 方法中,用 template 转换得到的 render 函数,在这样的转换之后,我们得到的 render 方法如下图: image.png 也就是说我们生成 vnode 的时候调用的是 vm._c() 方法。

vm._c()

在初始化的时候,就调用过 initRender 方法,此方法中定义了两个将 render 方法转换为 vnode 的方法:

vm._c = function (a, b, c, d) { debugger; return createElement$1(vm, a, b, c, d, false); };
vm.$createElement = function (a, b, c, d) { debugger; return createElement$1(vm, a, b, c, d, true); };

vm._c 是被模板编译成的 render 函数使用,而 vm.$createElement 是用户手写 render 方法使用的, 它们支持的参数相同,并且内部都调用了 createElement 方法。

vm._render 最终是通过执行 createElement 方法并返回的是 vnode,它是一个虚拟 Node。

虚拟 Dom

真正的 DOM 元素是非常庞大的,因为浏览器的标准就把 DOM 设计的非常复杂。当我们频繁的去做 DOM 更新,会产生一定的性能问题。Virtual DOM 就是用一个原生的 JS 对象去描述一个 DOM 节点,所以它比创建一个 DOM 的代价要小很多。在 Vue.js 中,Virtual DOM 是用 VNode 这么一个 Class 去描述,由于 VNode 只是用来映射到真实 DOM 的渲染,不需要包含操作 DOM 的方法,因此它是非常轻量和简单的。

createElement

这里的逻辑少了很多,因为案例过于简单,很多步骤没有执行到,需要后面再回顾

createElement 方法中整理了参数,随后调用了 _createElement 方法:

  1. context: 表示 VNode 的上下文环境,就是一个` vue 实例。
  2. tag:表示标签,在这个例子中,它的值是 `'div'。
  3. data:用于描述 vnode 的数据,在这个例子中,它的值是 {attrs: {id: 'app'}}
  4. children:表示当前 VNode 的子节点,它是任意类型的,它接下来需要被规范为标准的 VNode 数组。
  5. normalizationType:表示子节点规范的类型,类型不同规范的方法也就不一样,它主要是参考 render 函数是编译生成的还是用户手写的。
function _createElement(context, tag, data, children, normalizationType) { ... }

规范子元素

simpleNormalizeChildren 方法调用场景是 render 函数是编译生成的。理论上编译生成的 children 都已经是 VNode 类型的,但函数式组件是一个例外。

normalizeChildren 方法的调用场景有 2 种,一个场景是 render 函数是用户手写的;另一个场景是当编译 slotv-for 的时候会产生嵌套数组的情况。

 if (normalizationType === ALWAYS_NORMALIZE) {
    children = normalizeChildren(children);
 } else if (normalizationType === SIMPLE_NORMALIZE) {
    children = simpleNormalizeChildren(children);
 }

经过规范后,children 变成了一个类型为 VNode 的 Array。

创建 vnode

每个 VNode 有 children,children 每个元素也是一个 VNode,这样就形成了一个 VNode Tree,它很好的描述了我们的 DOM Tree。