vm.render()
- 用于把实例渲染成一个虚拟vnode
Vue.prototype._render 会执行如下
vnode = render.call(vm._renderProxy, vm.$createElement)
initRender (vm: Component) {
vm.$createElement = (a, b, c, d) => createElement(vm, a, b, c, d, true)
}
export function _createElement (
context: Component,
tag?: string | Class<Component> | Function | Object,
data?: VNodeData,
children?: any,
normalizationType?: number
){
children = normalizeChildren(children)
let vnode, ns
if (typeof tag === 'string') {
let Ctor
ns = (context.$vnode && context.$vnode.ns) || config.getTagNamespace(tag)
if (config.isReservedTag(tag)) {
// 内置组件标签
vnode = new VNode(
config.parsePlatformTagName(tag), data, children,
undefined, undefined, context
)
} else if (isDef(Ctor = resolveAsset(context.$options, 'components', tag))) {
// component
vnode = createComponent(Ctor, data, context, children, tag)
} else {
vnode = new VNode(
tag, data, children,
undefined, undefined, context
)
}
} else {
vnode = createComponent(tag, data, context, children)
}
return vnode
vm._update()
- 用于把vnode渲染为真实的dom
Vue.prototype._update 方法执行
vm.el = vm.__patch__(vm.el, vnode)执行
patch= createPatchFunction({ nodeOps, modules })
const hooks = ['create', 'activate', 'update', 'remove', 'destroy']
export function createPatchFunction (backend) {
let i, j
const cbs = {}
const { modules, nodeOps } = backend
for (i = 0; i < hooks.length; ++i) {
cbs[hooks[i]] = []
for (j = 0; j < modules.length; ++j) {
if (isDef(modules[j][hooks[i]])) {
cbs[hooks[i]].push(modules[j][hooks[i]])
}
}
}
// ...
return function patch (oldVnode, vnode, hydrating, removeOnly) {
if (isUndef(vnode)) {
if (isDef(oldVnode)) invokeDestroyHook(oldVnode)
return
}
let isInitialPatch = false
const insertedVnodeQueue = []
if (isUndef(oldVnode)) {
isInitialPatch = true
createElm(vnode, insertedVnodeQueue)
} else {
const isRealElement = isDef(oldVnode.nodeType)
if (!isRealElement && sameVnode(oldVnode, vnode)) {
patchVnode(oldVnode, vnode, insertedVnodeQueue, removeOnly)
} else {
if (isRealElement) {
oldVnode = emptyNodeAt(oldVnode)
}
const oldElm = oldVnode.elm
const parentElm = nodeOps.parentNode(oldElm)
// create new node
createElm(
vnode,
insertedVnodeQueue,
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)
while (ancestor) {
for (let i = 0; i < cbs.destroy.length; ++i) {
cbs.destroy[i](ancestor)
}
ancestor.elm = vnode.elm
if (patchable) {
for (let i = 0; i < cbs.create.length; ++i) {
cbs.create[i](emptyNode, ancestor)
}
const insert = ancestor.data.hook.insert
if (insert.merged) {
for (let i = 1; i < insert.fns.length; i++) {
insert.fns[i]()
}
}
} else {
registerRef(ancestor)
}
ancestor = ancestor.parent
}
}
// destroy old node
if (isDef(parentElm)) {
removeVnodes(parentElm, [oldVnode], 0, 0)
} else if (isDef(oldVnode.tag)) {
invokeDestroyHook(oldVnode)
}
}
}
invokeInsertHook(vnode, insertedVnodeQueue, isInitialPatch)
return vnode.elm
}
}
diff算法
1.关于虚拟dom 结构
代码地址: github.com/chengliu050…
<div id="div1" class="container">
<p>hello</p>
<ul style="font-size:16px;">
<li><hello/li>
</ul>
</div>
对应vdom结构如下
{
tag:'div',
props:{
className:'container',
id:'div'
}
children:[
{
tag:'p',
children:'hello'
}
{
tag:'ul',
props:{
style:'font-size:16px'
},
children:[
{
tag:'li',
children:'a'
}
]
}
]
}
2.diff算法产生的补丁patch主要分为remove,text,attr,replace
## vdom diff算法大概遵守以下几点
* 只比较同一层级,不跨级比较
* tag不同则直接remove,不再深度遍历
* tag和key相同,则认为相同节点,不再深度遍历
1. 无新节点,{type:remove}
2. 有新旧节点,并且Node为string且不相同,{type:text,text:'newNode'}
3. 新旧节点的type相同,比较attribute{type:attr,{key1:'',key2:''}
遍历children