本文为拉勾网大前端高薪训练营第一期笔记
8.2.2 Vue.js 源码剖析-虚拟 DOM
虚拟 DOM 回顾
什么是虚拟 DOM
虚拟 DOM(Virtual DOM) 是使用 JavaScript 对象来描述 DOM,虚拟 DOM 的本质就是 JavaScript 对
象,使用 JavaScript 对象来描述 DOM 的结构。应用的各种状态变化首先作用于虚拟 DOM,最终映射
到 DOM。Vue.js 中的虚拟 DOM 借鉴了 Snabbdom,并添加了一些 Vue.js 中的特性,例如:指令和组
件机制。
Vue 1.x 中细粒度监测数据的变化,每一个属性对应一个 watcher,开销太大Vue 2.x 中每个组件对应一个 watcher,状态变化通知到组件,再引入虚拟 DOM 进行比对和渲染
为什么要使用虚拟 DOM
使用虚拟 DOM,可以避免用户直接操作 DOM,开发过程关注在业务代码的实现,不需要关注如
何操作 DOM,从而提高开发效率
作为一个中间层可以跨平台,除了 Web 平台外,还支持 SSR、Weex。
关于性能方面,在首次渲染的时候肯定不如直接操作 DOM,因为要维护一层额外的虚拟 DOM,
如果后续有频繁操作 DOM 的操作,这个时候可能会有性能的提升,虚拟 DOM 在更新真实 DOM
之前会通过 Diff 算法对比新旧两个虚拟 DOM 树的差异,最终把差异更新到真实 DOM
Vue.js 中的虚拟 DOM
演示 render 中的 h 函数
h 函数就是 createElement()
vm.$createElement(tag, data, children, normalizeChildren)
tag: 标签名称或者组件对象
data: 描述tag,可以设置DOM的属性或者标签的属性
children: tag中的文本内容或者子节点
VNode
核心属性
- tag
- data
- children
- text
- elm
- key
const vm = new Vue({
el: '#app',
render(h) {
// h(tag, data, children)
// return h('h1', this.msg)
// return h('h1', { domProps: { innerHTML: this.msg } })
// return h('h1', { attrs: { id: 'title' } }, this.msg)
const vnode = h(
'h1', {
attrs: { id: 'title' }
},
this.msg)
console.log(vnode)
return vnode
},
data: {
msg: 'Hello Vue'
}
})
虚拟 DOM 创建过程分析
createElement
功能
createElement() 函数,用来创建虚拟节点 (VNode),我们的 render 函数中的参数 h,就是
createElement()
render(h) {
// 此处的 h 就是 vm.$createElement
return h('h1', this.msg)
}
定义
在 vm._render() 中调用了,用户传递的或者编译生成的 render 函数,这个时候传递了 createElement
- src/core/instance/render.js
vm._c = (a, b, c, d) => createElement(vm, a, b, c, d, false)
// normalization is always applied for the public version, used in
// user-written render functions.
vm.$createElement = (a, b, c, d) => createElement(vm, a, b, c, d, true)
vm._c和vm.$createElement都调用createElement,区别只有最后一个参数
- src/core/vdom/create-element.js
执行完 createElement 之后创建好了 VNode,把创建好的 VNode 传递给 vm._update() 继续处理
export function createElement(
context: Component,
tag: any,
data: any,
children: any,
normalizationType: any,
alwaysNormalize: boolean
): VNode | Array<VNode> {
// 判断第三个参数
// 如果 data 是数组或者原始值的话就是 children,实现类似函数重载的机制
if (Array.isArray(data) || isPrimitive(data)) {
normalizationType = children
children = data
data = undefined
}
if (isTrue(alwaysNormalize)) {