1.我们知道vue使用的是虚拟DOM去减少对真是DOM的操作次数,来提升页面的运行的效率。那他的内部原理是怎样的呢。首先vue和react在更新dom时,使用的算法基本相同,都是基于anabbdom。当浏览器的页面数据发生变化时,vue不会立即渲染。二十经过diff算法,判断出哪些是不需要变化的,那些是需要变化更新的,只需要更新那些需要更细的DOM就可以了,这样就减少了很多不必要的DOM操作,在很大程度上提高了性能。vue内部就是使用了这样的抽象节点VNode,它是对真实DOM的抽象,所以他不依赖任何平台,包括浏览器,weex,甚至是node平台也可以 对这样一颗抽象DOM树进行创建删除修改等操作。
在vue早期版本中1.0中,每个数据都对应一个Watcher;而在vue2.x中一个组件对应一个Watcher,这样当我们的数据变化的时候,In the set function,the notify function of Dep will be triggered to notify the watcher to execute vm._update(vm._render(), hydrating)method to update the view,Let’s take a look_Update method
Vue.prototype._update = function (vnode: VNode, hydrating?: boolean) {
const vm: Component = this
const prevEl = vm.$el
const prevVnode = vm._vnode
const restoreActiveInstance = setActiveInstance(vm)
vm._vnode = vnode
//Vue.prototype.__patch__ is injected in entry points
//based on the rendering backednd used.
// 基于后端渲染Vue.prototype.__patch__被用来作为一个入口
if (!prevVnode) {
// initial render
vm.$el = vm.__patch__(vm.$el, vnode, hydrating, false /* removeOnly*/)
} else {
//updates
vm.$el = vm.__patch__(prevVnode, vnode)
}
restoreActiveInstance()
// update __vue__ reference
/*更新新的实例对象的__vue__*/
if (prevEl) {
prevEl.__vue__ = null
}
if (vm.$el) {
vm.$el.__vue__ = vm
}
//if parent is a HOC, upadate its $el as well
if (vm.$vnode && vm.$parent && vm.$vnode === vm.$parent._vnode) {
vm.$parent.$el = vm.$el
}
//update hook is called by the scheduler to ensure that children are
//update in a parent's updated hook.
}
Obviusly, we can see _The update method will patch the incoming vnode and the old vnode.
Let’s take a look at what happens in the patch function.
The patch function compares the new and old nodes, and then determines which nodes need to be modified. Only these nodes needs to be modifie,so that the DOM can be updated more eddifciently. Let’s take a look at the code first.
return function patch (oldVnode, vnode, hydrating, removeOnly){
/*Vnode dose not exist. Call the destroy hook to delete the node*/
if (isUndef(vnode)) {
if (isDef(oldVnode) invokeDestroyHook(oldVnode))
returns
}
let isInitialPath = false
const insetedVnodeQueue = {
}
/*oldVnode does not exist. Create a new node directly.*/
if (isUndef(oldVnode)) {
//empty mount (likely as component),create new root element
isInitialPath = true
createElm(vnode, insetedVnodeQueue)
} else {
/*Mark whether the old vnode has nodeType*/
const isRealElement = isDef(oldVnode.nodeType)
if(!isRealElement && sameVnode(oldVnode.nodeType)) {
//patch existing root node
/*When it is the same node,modify the existing node directly*/
patchVnode(oldVnode, vnode, insetedVnodeQueue, null, null, removeOnly)
} else {
if (isRealElement) {
//mounting to a real element
//check if this is server-rendered content and if we can perform
//a successful hydration
if (oldVnode.nodeType === 1 && oldCnode.hasAttribute(SSR_ATTR)) {
/*When the old vnode is the element rendered by the sever,
the drawing is marked as true*/
oldVnode.removeAttribute(SSR_ATTR)
hydrating = true
}
if (isTrue(hydrating)){
//need to merge to real DOM
if (hydrating(oldVnode, vnode, insertedVnodeQueue)) {
//call the insert hook
invokeInsertHook(vnode, insertedVnodeQueue, true)
return oldVnode
} else if(process.env.NODE_ENV !== 'production') {
warn(
)
}
}
//either not server-rendered,or hydration failed.
//create an empty node and replace it
oldVnode = emptyNodeAt(oldVnode)
}
//replacing existing element
const oldElm = oldVnode.elm
const parentElm = nodeOps.parentNode(oldElm)
//create new node
createElm(
vnode,
insertedVnodeQueue,
//extremely rare edge case: do not insert if old element is in a leaving
//transition.Only happen when combining transition +
//keep-alive + HOCs. (#4590)
oldElm.leaveCb ? null : parentElm,
nodeOps.nextSibling(oldElm)
)
//update parent placeholder node element, recursively
if(isDef(vnode.parent)) {
let ancestor = vnode.parent
const patchable = isPatchable(vnode)