You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
很多人认为虚拟 DOM 最大的优势是 diff 算法,减少 JavaScript 操作真实 DOM 的带来的性能消耗。虽然这一个虚拟 DOM 带来的一个优势,但并不是全部。虚拟 DOM 最大的优势在于抽象了原本的渲染过程,实现了跨平台的能力,而不仅仅局限于浏览器的 DOM,可以是安卓和 IOS 的原生组件,可以是近期很火热的小程序,也可以是各种GUI
三、如何实现虚拟DOM
首先可以看看vue中VNode的结构
源码位置:src/core/vdom/vnode.js
export defaultclassVNode{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
functionalContext: Component|void;// only for functional component root nodes
key: string|number|void;
componentOptions: VNodeComponentOptions|void;
componentInstance: Component|void;// component instance
parent: VNode|void;// component placeholder node
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?constructor(tag?: string,data?: VNodeData,children?: ?Array<VNode>,text?: string,elm?: Node,context?: Component,componentOptions?: VNodeComponentOptions){/*当前节点的标签名*/this.tag=tag/*当前节点对应的对象,包含了具体的一些数据信息,是一个VNodeData类型,可以参考VNodeData类型中的数据信息*/this.data=data/*当前节点的子节点,是一个数组*/this.children=children/*当前节点的文本*/this.text=text/*当前虚拟节点对应的真实dom节点*/this.elm=elm/*当前节点的名字空间*/this.ns=undefined/*编译作用域*/this.context=context/*函数化组件作用域*/this.functionalContext=undefined/*节点的key属性,被当作节点的标志,用以优化*/this.key=data&&data.key/*组件的option选项*/this.componentOptions=componentOptions/*当前节点对应的组件的实例*/this.componentInstance=undefined/*当前节点的父节点*/this.parent=undefined/*简而言之就是是否为原生HTML或只是普通文本,innerHTML的时候为true,textContent的时候为false*/this.raw=false/*静态节点标志*/this.isStatic=false/*是否作为跟节点插入*/this.isRootInsert=true/*是否为注释节点*/this.isComment=false/*是否为克隆节点*/this.isCloned=false/*是否有v-once指令*/this.isOnce=false}// DEPRECATED: alias for componentInstance for backwards compat./* istanbul ignore next https://github.com/answershuto/learnVue*/getchild(): Component|void{returnthis.componentInstance}}
export function_createElement(context: Component,tag?: string|Class<Component>|Function|Object,data?: VNodeData,children?: any,normalizationType?: number): VNode|Array<VNode>{if(isDef(data)&&isDef((data: any).__ob__)){process.env.NODE_ENV!=='production'&&warn(`Avoid using observed data object as vnode data: ${JSON.stringify(data)}\n`+'Always create fresh vnode data objects in each render!',context`
)returncreateEmptyVNode()}// object syntax in v-bindif(isDef(data)&&isDef(data.is)){
tag =data.is}if(!tag){// in case of component :is set to falsy valuereturncreateEmptyVNode()}
...
// support single function children as default scoped slotif(Array.isArray(children)&&typeofchildren[0]==='function'){data=data||{}data.scopedSlots={default: children[0]}children.length=0}if(normalizationType===ALWAYS_NORMALIZE){children=normalizeChildren(children)}elseif(===SIMPLE_NORMALIZE){children=simpleNormalizeChildren(children)}// 创建VNode
...
}
一、什么是虚拟DOM
虚拟 DOM (
Virtual DOM
)这个概念相信大家都不陌生,从React
到Vue
,虚拟DOM
为这两个框架都带来了跨平台的能力(React-Native
和Weex
)实际上它只是一层对真实
DOM
的抽象,以JavaScript
对象 (VNode
节点) 作为基础的树,用对象的属性来描述节点,最终可以通过一系列操作使这棵树映射到真实环境上在
Javascript
对象中,虚拟DOM
表现为一个Object
对象。并且最少包含标签名 (tag
)、属性 (attrs
) 和子元素对象 (children
) 三个属性,不同框架对这三个属性的名命可能会有差别创建虚拟
DOM
就是为了更好将虚拟的节点渲染到页面视图中,所以虚拟DOM
对象的节点与真实DOM
的属性一一照应在
vue
中同样使用到了虚拟DOM
技术定义真实
DOM
实例化
vue
观察
render
的render
,我们能得到虚拟DOM
通过
VNode
,vue
可以对这颗抽象树进行创建节点,删除节点以及修改节点的操作, 经过diff
算法得出一些需要修改的最小单位,再更新视图,减少了dom
操作,提高了性能二、为什么需要虚拟DOM
DOM
是很慢的,其元素非常庞大,页面的性能问题,大部分都是由DOM
操作引起的真实的
![](https://camo.githubusercontent.com/1f19485a2eb94c951b128a54230f7fb7c9e8c716e949fad7c85da717081fae40/68747470733a2f2f7374617469632e7675652d6a732e636f6d2f63633935633766302d343432632d313165622d616239302d6439616538313462323430642e706e67)
DOM
节点,哪怕一个最简单的div
也包含着很多属性,可以打印出来直观感受一下:由此可见,操作
DOM
的代价仍旧是昂贵的,频繁操作还是会出现页面卡顿,影响用户的体验举个例子:
你用传统的原生
api
或jQuery
去操作DOM
时,浏览器会从构建DOM
树开始从头到尾执行一遍流程当你在一次操作时,需要更新10个
DOM
节点,浏览器没这么智能,收到第一个更新DOM
请求后,并不知道后续还有9次更新操作,因此会马上执行流程,最终执行10次流程而通过
VNode
,同样更新10个DOM
节点,虚拟DOM
不会立即操作DOM
,而是将这10次更新的diff
内容保存到本地的一个js
对象中,最终将这个js
对象一次性attach
到DOM
树上,避免大量的无谓计算三、如何实现虚拟DOM
首先可以看看
vue
中VNode
的结构源码位置:src/core/vdom/vnode.js
这里对
VNode
进行稍微的说明:context
选项都指向了Vue
实例elm
属性则指向了其相对应的真实DOM
节点vue
是通过createElement
生成VNode
源码位置:src/core/vdom/create-element.js
上面可以看到
createElement
方法实际上是对_createElement
方法的封装,对参数的传入进行了判断可以看到
_createElement
接收5个参数:context
表示VNode
的上下文环境,是Component
类型tag 表示标签,它可以是一个字符串,也可以是一个
Component
data
表示VNode
的数据,它是一个VNodeData
类型children
表示当前VNode
的子节点,它是任意类型的normalizationType
表示子节点规范的类型,类型不同规范的方法也就不一样,主要是参考render
函数是编译生成的还是用户手写的根据
normalizationType
的类型,children
会有不同的定义simpleNormalizeChildren
方法调用场景是render
函数是编译生成的normalizeChildren
方法调用场景分为下面两种:render
函数是用户手写的slot
、v-for
的时候会产生嵌套数组无论是
simpleNormalizeChildren
还是normalizeChildren
都是对children
进行规范(使children
变成了一个类型为VNode
的Array
),这里就不展开说了规范化
children
的源码位置在:src/core/vdom/helpers/normalzie-children.js在规范化
children
后,就去创建VNode
createComponent
同样是创建VNode
源码位置:src/core/vdom/create-component.js
稍微提下
createComponent
生成VNode
的三个关键流程:Ctor
installComponentHooks
安装组件钩子函数vnode
小结
createElement
创建VNode
的过程,每个VNode
有children
,children
每个元素也是一个VNode
,这样就形成了一个虚拟树结构,用于描述真实的DOM
树结构参考文献
The text was updated successfully, but these errors were encountered: