typora-copy-images-to: assets
虚拟DOM介绍
-
什么是虚拟DOM
虚拟DOM是将状态映射成试图的众多解决方案之一。页面交互的本质还是通过改变状态(变量)来改变试图渲染,而框架(像主流框架vue、react、angular)的应用可以让我们把关注的焦点更多的放在状态上,省略对DOM的操作(框架内部已经帮我们完成了)。
而虚拟DOM映射视图的方式是通过状态生成一个虚拟节点树,然后通过虚拟节点树进行渲染。虚拟节点树本质上是一个普通的js对象,它包含了创建一个DOM元素所需要的属性 -
为什么要在vue中引入虚拟DOM
- 像react(虚拟DOM)、angular(脏检查)对状态变化的侦测都有一个特点:他们不知道那个状态发生了变化,当有状态发生变化时,就对整个视图进行重新渲染。
// 问题:react angular vue的监听原理对比
// 1. 脏检查、虚拟DOM、细粒度绑定分别是什么原理
// 1.1 脏检查:页面所有的数据变动都是由事件或者异步操作触发的。
// 浏览器提供了监测事件的API,但没有检测数据变化的API。
// 因此每次在执行一次事件后都会对数据进行大检查,监听那些数据发生了改变。
// 网址:https://www.zhihu.com/question/43470158/answer/108283135
// 1.2 虚拟DOM:生成一个类DOM对象,通过这个对象可以生成DOM元素。
// react是怎么来监听数据变化的呢?
// 只要页面有事件发生,就会把所有的DOM全部删除,再重新渲染。不会进行DOM对比
// 1.3 细粒度绑定
// 基本原理在第二和第三章有讲解 - 在vue1.0中用的细粒度监听方式,但每一个监听都会对应一个watcher,这样对内存和依赖(请看前面两篇博客)追踪的开销太大。vue2.0中采用中等粒度 + 虚拟DOM的解决方案。既以组件级别为一个依赖实例,组件内部状态的变化监听就由虚拟DOM完成。
- 像react(虚拟DOM)、angular(脏检查)对状态变化的侦测都有一个特点:他们不知道那个状态发生了变化,当有状态发生变化时,就对整个视图进行重新渲染。
-
vue中的虚拟DOM
执行渲染函数会生成虚拟树节点,在虚拟DOM渲染视图的过程中,会将新生成的vnode与上一次渲染的vnode进行对比,找出真正需要更新的节点。而这个对比的过程成为patch算法,也是虚拟DOM的核心,后面的博客会讲解到 -
vnode
vuejs中有一个Vnode类,通过它可以实例化不同类型的vnode实例,不同类型的vnode实例代表不同类型的DOM元素。DOM元素有元素节点、文本节点、注释节点等,对应的vnode也有不同类型。vnode本质上是js中的一个对象,可以把它理解为节点描述对象
export default class VNode {
tag: string | void;
data: VNodeData | void;
children: ?Array;
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 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节点分类
不同类型的节点之间,只是有效属性不同;比如注释节点有效属性只有text和i你Comment;其余属性默认都是undefiend或者false; 文本节点的有效属性只有text-
注释节点:对应视图中的注释
export const createEmptyVnode = text => {
const node = new VNode()
node.text = text
node.isComment = true
return node
}
对应的注释节点就是:
{
text: ‘注释节点‘,
isComment: true
} -
文本节点: 对应视图中的文字,比如我是文本节点中的我是文本节点就是文本节点
export const createTextVnode = text => {
return new VNode(undefined, undefined, undefined, string(text))
}
对应的文本节点就是:
{
text: ‘这是文本节点‘
} -
克隆节点: 将现有节点的属性复制到新节点当中;作用是优化静态节点和插槽节点;以静态节点为例,静态节点从第一次渲染后,无论页面中的状态怎么发生变化他都不会改变,因此只需第一次渲染的时候获取一下(在模板编译的优化器阶段就已经标注那些节点是静态节点,请看下篇博客)
本人有个小问题:渲染静态节点的过程难道比优化器的实现过程更加耗费性能吗? -
元素节点:
常见的属性:- tag: 节点名称,对应着DOM元素标签名
- tata: 包含了一些节点上的数据,如class、style attrs等(具体获取方式请看下篇博客)
- children: 子节点
- context: 当前组件的vue实例
-
- vnode节点分类