2-12 createElement
还是接着_render方法讲:
vnode = render.call(vm._renderProxy, vm.$createElement)
之前讲了vm._renderProxy是怎么回事,现在学习一下vm.$createElement
部分。
首先在core/instance/render.js中就可以看到vm.$creteElement
的定义。
模板编译出来的代码会执行:
vm._c = (a, b, c, d) => createElement(vm, a, b, c, d, false)
而我们用户手写的render函数会执行:
vm.$createElement = (a, b, c, d) => createElement(vm, a, b, c, d, true)
但其实无论是哪一块。最终都会执行createElement方法,区别在于最后一个参数。
createElement方法最终调用的是_createElement方法,相当于这里做了一层封装处理。
而_createElement的执行大概可以记为:
- 第一步是检查VNodeData是不是响应式的,vue定义vNode不能是响应式的。也就是说数据上不能存在
_ob_
,如果变量上存在_ob_
说明这是一个响应式的。 - 如果是响应式的,就使用createEmptyVNode创建一个空的vnode
- 接下来会对children做一层normaliz。两个函数:simpleNormalizeChildren和normalizeChildren。第一个是假设render函数只有一层。第二个是假设有很多层然后借助normalizeArrayChildren递归的去使用。但其实两者的作用都是将render改为一维数组。
2-14 update 作用:将vnode渲染成真实dom。
首先看一下_update的实现方法在哪:core/instance/lifecycle.js
首先认清update在什么时期会执行1.初次渲染的时候会把vnode渲染成真实dom。2.之后的数据变化引起的视图dom的改变。
所以如果是首次加载的话,那么prevVnode其实是不存在的。所以会执行:
vm.$el = vm.__patch__(vm.$el, vnode, hydrating, false /* removeOnly */)
那么patch方法便映入眼帘了。
patch方法的定义是在:platforms/web/runtime/index中进行挂载到Vue原型上的。
Vue.prototype.__patch__ = inBrowser ? patch : noop
export const patch: Function = createPatchFunction({ nodeOps, modules })
nodeOps:主要是封装一些操作dom的原声方法,modules:style class 相关的更改时的一些钩子函数的触发
当然作者在实现patch的时候使用一个技巧是函数颗粒化。我们只需要在初始化的时候使用到nodeOps, modules,没必要每次调用patch都需要初始化。所以这里使用了颗粒化的思想去解决这个问题。
接下来一个比较重要的函数是createElm。这个函数就是把vnode挂载到真实的dom上,先创建子节点,再创建父节点的一个过程