-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
1.6.Vue虚拟DOM解析-Virtual Dom实现和Dom diff算法
- Loading branch information
Showing
1 changed file
with
119 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,119 @@ | ||
## 关于VNode | ||
* 在浏览器中我们可以通过js来操作DOM,但是这样的操作性能很差,因为真正的DOM元素非常庞大,于是Virtual Dom应运而生。 | ||
可以这样理解,Virtual Dom就是在js中模拟DOM对象树来优化DOM操作的一种技术或思路。 | ||
|
||
* 下面先来看看Vue中的Virtual Dom,源码位置在`src/core/vdom/vnode.js` | ||
```vuejs | ||
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; | ||
key: string | number | void; | ||
componentOptions: VNodeComponentOptions | void; | ||
componentInstance: Component | void; | ||
parent: VNode | void; | ||
//... | ||
constructor ( | ||
tag?: string, // vNode的标签,例如div、p等标签 | ||
data?: VNodeData, // vNode上的data值,包括其所有的class、attribute属性、style属性 | ||
children?: ?Array<VNode>, // vNode上的子节点 | ||
text?: string, // 文本 | ||
elm?: Node, // vNode上对应的真实dom元素 | ||
context?: Component, // vdom的上下文 | ||
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 | ||
//... | ||
} | ||
//... | ||
} | ||
``` | ||
* 从上面源码可以看出,Virtual Dom相对浏览器中的真实Dom要简单得多。其实Vue中的Virtual Dom就是把真实Dom中一些常用的属性类型, | ||
再加上Vue里面的一些配置项组成的。 | ||
|
||
* Vue中的Virtual Dom不仅对真实浏览器Dom进行简化外,还对不同场景进行了Virtual Dom的分类。下面是EmptyVNode、TextVNode、 | ||
CloneVNode的创建,源码位置在`src/core/vdom/vnode.js` | ||
```vuejs | ||
// EmptyVNode | ||
export const createEmptyVNode = (text: string = '') => { | ||
const node = new VNode() | ||
node.text = text | ||
node.isComment = true | ||
return node | ||
} | ||
// TextVNode | ||
export function createTextVNode (val: string | number) { | ||
return new VNode(undefined, undefined, undefined, String(val)) | ||
} | ||
// CloneVNode | ||
export function cloneVNode (vnode: VNode): VNode { | ||
const cloned = new VNode( | ||
vnode.tag, | ||
vnode.data, | ||
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 | ||
} | ||
``` | ||
* 关于Virtual Dom的分类,除了上面三种外, 还有ElementVnode,ComponentVnode等。 | ||
|
||
* 关于VNode的创建这里就不多说,在"Vue模板编译-AST生成Render字符串"那一节内容中我们就说了生成Render字符串中_c就是 | ||
createElement创建VNode,还不清楚的可以回看一下。 | ||
|
||
## DOM diff原理 | ||
* 比较两棵DOM树的差异是 `Virtual DOM` 最核心的部分,这也是所谓的 `Virtual DOM` 的 diff 算法。 它的作用就是根据 | ||
两个虚拟对象创建出描述改变内容的补丁,将这个补丁用来更新DOM。 | ||
|
||
* 两个Dom树的完全的 diff 算法是一个时间复杂度为 O(n^3) 的问题,但实际上在前端当中,很少会跨越层级地移动DOM元素。 | ||
即Virtual DOM 只会对同一个层级的元素进行对比,这样算法复杂度就可以达到 O(n)。 | ||
|
||
|
||
## Patch 补丁更新 | ||
|
||
|
||
## VNode生命周期 | ||
|
||
|
||
## 总结一下 | ||
* 最后,在这里总结一下Vue中Virtual Dom执行过程: | ||
|
||
> 1.用JS对象模拟DOM树,即调用createElement生成VNode | ||
> 2.比较两棵虚拟DOM树的差异, | ||
> 3.把差异应用到真正的DOM树上 | ||
|
||
## 相关资料 | ||
* [https://juejin.im/post/5c8e5e4951882545c109ae9c](https://juejin.im/post/5c8e5e4951882545c109ae9c) | ||
* [https://segmentfault.com/a/1190000008291645](https://segmentfault.com/a/1190000008291645) | ||
* [https://github.com/livoras/blog/issues/13](https://github.com/livoras/blog/issues/13) | ||
* [https://github.com/liutao/vue2.0-source/blob/master/vdom%E2%80%94%E2%80%94VNode.md](https://github.com/liutao/vue2.0-source/blob/master/vdom%E2%80%94%E2%80%94VNode.md) |