虚拟DOM
虚拟DOM介绍
我们在最开始提到过,Vue
从2.0+
版本开始就引入了虚拟DOM
,也知道Vue
中的虚拟DOM借鉴了开源库snabbdom (opens new window)的实现,并根据自身特色添加了许多特性。
Vue
在1.0+
版本还没有引入虚拟DOM
的时候,当某一个状态发生变化时,它在一定程度上是知道哪些节点使用到了这个状态,从而可以准确的针对这些节点进行更新操作,不需要进行对比。但这种做法是有一定的代价的,因为更新的粒度太细,每一次节点的绑定都需要一个Watcher
去观察状态的变化,这样会增加更多的内存开销。当一个状态被越多的节点使用,它的内存开销就越大。
因此在Vue
的2.0+
版本中,引入了虚拟DOM
将更新粒度调整为组件级别,当状态发生变化的时候,只派发更新到组件级别,然后组件内部再进行对比和渲染。这样做以后,当一个状态在同一个组件内被引用多次的时候,它们只需要一个render watcher
去观察状态的变化即可。
Vue中的虚拟DOM
虚拟DOM
解决DOM
更新的方式是:通过状态生成一个虚拟节点树,然后使用虚拟节点树进行渲染,在渲染之前会使用新生成的虚拟节点树和上一次生成的虚拟节点树进行对比,然后只渲染其不相同的部分(包括新增和删除的)。
在Vue
中,根实例就是虚拟节点树的根节点,各种组件就是children
孩子节点,树节点使用VNode
类来表示。它使用template
模板来描述状态与DOM
之间的映射关系,然后通过parse
编译将template
模板转换成渲染函数render
,执行渲染函数render
就可以得到一个虚拟节点树,最后使用这个虚拟节点树渲染到视图上。
因此根据上面这段话,我们可以得到Vue
使用虚拟DOM
进行模板转视图的一个流程。
VNode介绍
在DOM
元素中有不同类型的节点,例如:元素节点、文本节点和注释节点,如果VNode
实例代表节点的话,那么VNode
实例应该能创建不同类型的实例,用来表示不同类型的节点,而VNode
也确实可以做到。VNode
类定义在src/core/vdom/vnode.js
文件中,其代码如下:
export default class VNode {
tag: string | void;
data: VNodeData | void;
children: ?Array<VNode>;
text: string | void;
elm: Node | void;
ns: string | void;
context: Component | void; // rendered in this component's scope
key: string | number | void;
componentOptions: VNodeComponentOptions | void;
componentInstance: Component | void; // component instance
parent: VNode | void; // component placeholder node
// strictly internal
raw: boolean; // contains raw HTML? (server only)
isStatic: boolean; // hoisted static node
isRootInsert: boolean; // necessary for enter transition check
isComment: boolean; // empty comment placeholder?
isCloned: boolean; // is a cloned node?
isOnce: boolean; // is a v-once node?
asyncFactory: Function | void; // async component factory function
asyncMeta: Object | void;
isAsyncPlaceholder: boolean;
ssrContext: Object | void;
fnContext: Component | void; // real context vm for functional nodes
fnOptions: ?ComponentOptions; // for SSR caching
devtoolsMeta: ?Object; // used to store functional render context for devtools
fnScopeId: ?string; // functional scope id support
constructor (
tag?: string,
data?: VNodeData,
children?: ?Array<VNode>,
text?: string,
elm?: Node,
context?: Component,
componentOptions?: VNodeComponentOptions,
asyncFactory?: Function
) {
this.tag = tag
this.data = data
this.children = children
this.text = text
this.elm = elm
this.ns = undefined
this.context = context
this.fnContext = undefined
this.fnOptions = undefined
this.fnScopeId = undefined
this.key = data && data.key
this.componentOptions = componentOptions
this.componentInstance = undefined
this.parent = undefined
this.raw = false
this.isStatic = false
this.isRootInsert = true
this.isComment = false
this.isCloned = false
this.isOnce = false
this.asyncFactory = asyncFactory
this.asyncMeta = undefined
this.isAsyncPlaceholder = false
}
// DEPRECATED: alias for componentInstance for backwards compat.
/* istanbul ignore next */
get child (): Component | void {
return this.componentInstance
}
}
我们可以从以上代码看到,VNode
有很多个属性,我们千万不要被这些属性吓到,最重要的属性只有几个:tag
、data
、children
和key
。其余很多属性只是在Vue
中为适用不同的场景,额外添加的。
Vue
中的VNode
实例有需要类型,具体如下:
- 注释节点:注释节点可以使用
text
和isComment
两个属性来配合表示。
<template>
<!-- 一个注释节点 -->
</template>
在Vue
中,我们通过createEmptyVNode
方法来定义注释节点,此方法跟VNode
类是定义在同一个地方,其代码如下:
export const createEmptyVNode = (text: string = '') => {
const node = new VNode()
node.text = text
node.isComment = true
return node
}
- 文本节点:文本节点比注释节点更简单,只需要
text
属性即可。
<template>
一个文本节点
</template>
创建文本节点可以使用createTextVNode
方法,此方法跟VNode
类是定义在同一个地方,其代码如下:
export function createTextVNode (val: string | number) {
return new VNode(undefined, undefined, undefined, String(val))
}
- 克隆节点:克隆节点是将现有某个节点的所有属性全部克隆到另外一个新节点,让新节点和某个节点属性保持一致,唯一的区别是新克隆节点的
isCloned
属性为true
,我们会在后续组件章节介绍克隆节点的具体作用。
// 原始文本节点
const originNode = {
text: '原始节点',
isCloned: false
}
// 克隆文本节点
const cloneNode = {
text: '原始节点',
isCloned: true
}
克隆一个节点,可以使用cloneVNode
方法,它与VNode
类是定义在同一个地方,其代码如下:
export function cloneVNode (vnode: VNode): VNode {
const cloned = new VNode(
vnode.tag,
vnode.data,
// #7975
// clone children array to avoid mutating original in case of cloning
// a child.
vnode.children && vnode.children.slice(),
vnode.text,
vnode.elm,
vnode.context,
vnode.componentOptions,
vnode.asyncFactory
)
cloned.ns = vnode.ns
cloned.isStatic = vnode.isStatic
cloned.key = vnode.key
cloned.isComment = vnode.isComment
cloned.fnContext = vnode.fnContext
cloned.fnOptions = vnode.fnOptions
cloned.fnScopeId = vnode.fnScopeId
cloned.asyncMeta = vnode.asyncMeta
cloned.isCloned = true
return cloned
}
- 元素节点:元素节点是我们日常开发中接触最多的,它可以使用
tag
、data
、children
和context
几个属性配合表示。
<template>
<div id="app" class="app-main">元素节点</div>
</template>
假设以上template
模板,那么div
元素节点可以使用VNode
表示为:
const vnode = {
tag: 'div',
data: {
attrs: {
id: 'app'
}
class: 'app-main'
},
children: [VNode],
context: vm
}
tag
表示为元素标签的类型,例如:p
、div
或者ul
等。data
表示节点上的数据,包括atts
、style
和class
等。children
表示子节点列表,它是一个VNode
实例数组。context
当前节点所处的编译作用域。
- 组件节点:组件节点和元素节点有很多相似的地方,但它有两个独有的属性,分别是
componentOptions
和componentInstance
,其中componentOptions
表示组件的options
选项,componentInstance
表示当前组件的实例。
<template>
<child-component />
</template>
常见问题FAQ
- 免费下载或者VIP会员专享资源能否直接商用?
- 本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
- 提示下载完但解压或打开不了?
- 找不到素材资源介绍文章里的示例图片?
- 模板不会安装或需要功能定制以及二次开发?
发表评论
还没有评论,快来抢沙发吧!