Vue源码探秘(六)(Virtual DOM)
2020/3/27 11:07:02
本文主要是介绍Vue源码探秘(六)(Virtual DOM),对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!
引言
Virtual DOM
(后文简称vdom
)的概念大规模的推广得益于react
的出现,vdom
也是react
框架比较重要的特性之一。相比较频繁的手动去操作dom
而带来性能问题,vdom
很好的将dom
做了一层映射关系,进而将在我们本需要直接进行dom
的一系列操作,映射到了操作vdom
。
Vue.js 2.0
引入vdom
,比Vue.js 1.0
的初始渲染速度提升了 2-4 倍,并大大降低了内存消耗。那么,什么是vdom
呢?
让我们进入今天的文章。
VNode
VNode
是 Virtual DOM
在 Vue.js
中的数据结构定义,它被定义在 src/core/vdom/vnode.js
中:
// 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
是一个类,有很多属性。每一个vnode
都映射到一个真实的dom
节点上。我们这里先了解几个重要的属性:
tag
: 对应真实节点的标签名data
: 当前节点的相关数据(节点上的class
,attribute
,style
以及绑定的事件),是VNodeData
类型。该类型声明在flow/vnode.js
中:
// flow/vnode.js declare interface VNodeData { key?: string | number; slot?: string; ref?: string; is?: string; pre?: boolean; tag?: string; staticClass?: string; class?: any; staticStyle?: { [key: string]: any }; style?: string | Array<Object> | Object; normalizedStyle?: Object; props?: { [key: string]: any }; attrs?: { [key: string]: string }; domProps?: { [key: string]: any }; hook?: { [key: string]: Function }; on?: ?{ [key: string]: Function | Array<Function> }; nativeOn?: { [key: string]: Function | Array<Function> }; transition?: Object; show?: boolean; // marker for v-show inlineTemplate?: { render: Function, staticRenderFns: Array<Function> }; directives?: Array<VNodeDirective>; keepAlive?: boolean; scopedSlots?: { [key: string]: Function }; model?: { value: any, callback: Function }; } 复制代码
children
:vnode
的子节点text
:当前节点的文本elm
: 当前虚拟节点对应的真实节点parent
: 当前节点的父节点
看完VNode
在Vue.js
中的数据结构定义,我想你已经大概知道vdom
是什么了吧。
Virtual DOM 是什么?
本质上来说,vdom
只是一个简单的js
对象,并且最少包含tag
、props
和children
三个属性。不同的框架对这三个属性的命名会有点差别,但表达的意思是一致的。它们分别是标签名(tag)
、属性(props)
和子元素对象(children)
。下面是举一个经典的vdom
例子:
<div> Hello jack-cool <ul> <li id="1" class="li-1"> 我是森林 </li> </ul> </div> 复制代码
vdom
和dom
对象有着一一对应的关系,上面的html
对应生成的vdom
如下:
{ tag: "div", props: {}, children: [ "Hello jack-cool", { tag: "ul", props: {}, children: [{ tag: "li", props: { id: 1, class: "li-1" }, children: ["我是", "森林"] }] } ] } 复制代码
Virtual DOM 有什么作用?
vdom
的最终目标是将vnode
渲染到视图上。但是如果直接使用新节点覆盖旧节点的话,会有很多不必要的DOM
操作。
我们先来看下引入vdom
前后,实现视图更新的不同流程:
引入 vdom 之前
数据 + 模板生成真实 DOM
数据发生改变 新的数据 + 模板生成新的 DOM
新的 DOM
替换掉原来的DOM
这么做的缺点在于:即使模板中只有一个元素发生了变化,也会把整个模板替换掉。例如,一个ul
标签下很多个li
标签,其中只有一个li
有变化,这种情况下如果使用新的ul
去替代旧的ul
,会有很多不必要的DOM
操作而造成性能上的损失。
为了避免不必要的DOM
操作,vdom
在vnode
映射到视图的过程中,将vnode
与上一次渲染视图所使用的旧虚拟节点(oldVnode
)做对比,找出真正需要更新的节点来进行DOM
操作,从而避免操作其他无需改动的DOM
。
引入 vdom 之后
数据 + 模板生成虚拟 DOM
虚拟 DOM
生成真实DOM
数据发生改变 新的数据 + 模板生成新的虚拟 DOM
而不是真实DOM
用新的虚拟 DOM
和原来的虚拟DOM
作对比(diff
算法,后面会详细介绍)【性能up↑
】找出发生改变的元素 直接修改原来的真实 DOM
【性能up↑
】
总结
这一节我带大家大概了解了Virtual DOM
的概念。Vue.js
的 VNode
其实是借鉴了 snabbdom
的实现。
从VNode
到真实 DOM
需要经过 create
、diff
、patch
等几个过程。本小节呢,我们只是大概了解一下vdom
是什么以及它有什么作用。关于vdom
的一些详细概念、流程和内部实现,我会在后面的章节中和大家分享(其实关于Virtual DOM
单独出一个系列文章也不为过)。
回顾上一节,我们提到 vm._c
和 vm.$createElement
都要调用 createELement
函数,其实这个函数就是 VNode
的 create
过程,下一节我们就来分析 createELement
函数。
这篇关于Vue源码探秘(六)(Virtual DOM)的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!
- 2024-10-04package.json 文件位置在哪?-icode9专业技术文章分享
- 2024-10-01Craco.js学习:从入门到实践指南
- 2024-10-01Create-React-App学习:入门与实践指南
- 2024-10-01CSS-in-JS学习:从入门到实践指南
- 2024-09-30JSX语法学习:从入门到初步掌握
- 2024-09-30Mock.js学习:入门教程与实战演练
- 2024-09-30React Hooks学习:从入门到实践
- 2024-09-30受控组件学习:React中的基础入门教程
- 2024-09-29JS定时器教程:初学者必看指南
- 2024-09-29JS对象教程:初学者的全面指南