template转换成视图的过程
- Vue.js通过编译将template模板转换成渲染函数(render),执行渲染函数就可以得到一个虚拟节点树。
- 对 Model 进行操作时,会触发对应 Dep 中的 Watcher 对象。Watcher 对象会调用对应的 update 来修改视图。这个过程主要是将新旧虚拟节点进行差异对比,即patch(核心是diff算法),然后根据对比结果进行DOM操作来更新视图。
原理:


虚拟DOM
虚拟DOM是一个JS对象(VNode节点),至少包含三个属性:标签名(tag)、属性(attrs)和子元素对象( children)。
Vue的diff算法是基于snabbdom改造过来的,仅在同级的vnode间做diff,递归地进行同级vnode的diff,最终实现整个DOM树的更新。因为跨层级的操作是非常少的,忽略不计,这样时间复杂度就从O(n³)变成O(n)。
diff 算法:
- patch(container,vnode)
初次渲染时,让VNode渲染成真正的DOM。
function createElement(vnode) {
var tag = vnode.tag
var attrs = vnode.attrs || {}
var children = vnode.children || []
if (!tag) {
return null
}
// 创建真实的 DOM 元素
var elem = document.createElement(tag)
// 属性
var attrName
for (attrName in attrs) {
if (attrs.hasOwnProperty(attrName)) {
// 给 elem 添加属性
elem.setAttribute(attrName, attrs[attrName])
}
}
// 子元素
children.forEach(function (childVnode) {
// 给 elem 添加子元素,如果还有子节点,则递归的生成子节点。
elem.appendChild(createElement(childVnode)) // 递归
})
// 返回真实的 DOM 元素
return elem
}
- patch(vnode,newVnode)
再次渲染时,将新的vnode和旧的vnode相对比,然后之间差异应用到所构建的真正的DOM树上。 这里我们只考虑vnode与newVnode如何对比的情况:
function updateChildren(vnode, newVnode) {
var children = vnode.children || []
var newChildren = newVnode.children || []
// 遍历现有的children
children.forEach(function (childVnode, index) {
var newChildVnode = newChildren[index]
// 两者tag一样
if (childVnode.tag === newChildVnode.tag) {
// 深层次对比,递归
updateChildren(childVnode, newChildVnode)
} else {
// 两者tag不一样
replaceNode(childVnode, newChildVnode)
}
}
)}