这是一个系列文章,请关注snabbdom@3.5.1 源码分析专栏、vue@2.6.11 源码分析 专栏
vue@2.x 借助了snabbdom
的虚拟DOM能力
snabbdom 是什么?
A virtual DOM library with focus on simplicity, modularity, powerful features and performance.
为什么需要 虚拟DOM
另外一个虚拟DOM库:virtual-dom 解释了为什么(Motivation)会出现虚拟DOM?
- 手动操作DOM麻烦且混乱
- 难以跟踪之前的状态:视图实际上是数据的反映,视图每次变更都会构成一条历史记录,这种历史记录如果是纯手动维护则会很艰难。
而事实上视图是数据的体现,如果有种方式能够让数据和视图同步,数据变化时视图自动更新而不需要手动更新(数据驱动),对于开发者来说则简化很多工作,只需要关心数据就行。
所以出现了虚拟DOM,作为数据和视图的中间媒介。数据 -> 虚拟DOM -> 真实DOM。
snabbdom的使用
官方参考,官方demo如下
import {
init, classModule, propsModule, styleModule, eventListenersModule, h
} from "../build/index.js";
const patch = init([// Init patch function with chosen modules
classModule, // makes it easy to toggle classes
propsModule, // for setting properties on DOM elements
styleModule, // handles styling on elements with support for animations
eventListenersModule // attaches event listeners
]);
const container = document.getElementById("container");
const vnode = h("div#container.two.classes",
{ on: { click: someFn } },
[
h("span", { style: { fontWeight: "bold" } }, "This is bold"), " and this is just normal text",
h("a", { props: { href: "http://wwww.baidu.com" } },
"_ baidu.com_1")
]
);
function someFn() {
function anotherEventHandler() {
console.log('anotherEventHandler')
}
const newVnode = h("div#container.two.classes",
{ on: { click: anotherEventHandler } },
[
h("span", { style: { fontWeight: "normal", fontStyle: "italic" } }, "This is now italic type"),
" and this is still just normal text",
h("a", { props: { href: "http://wwww.baidu.com" } }, "_ baidu.com_2")
]
);
// Second `patch` invocation
patch(vnode, newVnode); // Snabbdom efficiently updates the old view to the new state
}
// Patch into empty DOM element – this modifies the DOM as a side effect
patch(container, vnode);
- 先是调用
init
方法,参数是module数组,后面会深入分析内置的各个module
的作用,该函数返回一个patch
函数。patch
(打补丁的意思对吧)方法用来将虚拟DOM同步到视图,patch
函数有两个入参,分别是老的虚拟节点oldVnode和新的虚拟节点vnode。
export function init(modules, domApi, options) {
//...
return function patch(oldVnode, vnode) {
//...
}
}
- 调用
h
函数,该函数用来创建虚拟DOM,后面会看到虚拟DOM具体是什么东西。
export function h(sel, b, c) {
//...
return vnode(sel, data, children, text, undefined);
}
-
第一次调用
patch
函数时,由于oldVnode是真实DOM,此时会先清空该DOM的子孙,而后对比。显然是将我们上述通过h
函数创建的虚拟DOM全部转化为真实DOM挂载到界面上。 -
此时当我们点击文本时(提供了click事件:someFn),在该点击事件的回调中:看到我们再次通过
h
函数创建一个新的虚拟节点,而后调用patch
来对比新老虚拟节点(树),和第一次情况不同,此时的第一个入参是第一次创建的老的虚拟节点(树),此时会真正对比两颗虚拟节点树的差异,而后将差异同步到视图中。
总结
- 简单为什么需要虚拟DOM
- snabbdom的基础使用,其实就是这么简单,后面会从源码角度分析这个运作过程。
snabbdom/src 目录结构