// 在patch阶段中,会执行各个钩子const hooks =['create','activate','update','remove','destroy']exportfunctioncreatePatchFunction(backend){
let i, j
const cbs ={
}// 拿到 这两项const{
modules, nodeOps }= backend
// 将 hooks 遍历到 cbs 对象中保存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]])}}}// ... 这里定义了非常多的辅助函数// 这个patch实际上是传入了四个参数// 关注前两个,oldVnode 是 真实的el, vnode 就是vdom, 后面两个都是 falsereturnfunctionpatch(oldVnode, vnode, hydrating, removeOnly){
// 销毁时 return 跳过if(isUndef(vnode)){
if(isDef(oldVnode))invokeDestroyHook(oldVnode)return}let isInitialPatch =falseconst insertedVnodeQueue =[]// 这个 if 一般不会走到,跳过if(isUndef(oldVnode)){
// empty mount (likely as component), create new root element
isInitialPatch =truecreateElm(vnode, insertedVnodeQueue)}else{
// 这里,判断是否是真实的dom,这里是 trueconst isRealElement =isDef(oldVnode.nodeType)if(!isRealElement &&sameVnode(oldVnode, vnode)){
// patch existing root nodepatchVnode(oldVnode, vnode, insertedVnodeQueue, removeOnly)}else{
// 这里执行if(isRealElement){
// mounting to a real element// check if this is server-rendered content and if we can perform// a successful hydration.// 这里不是 ssr, 这里走不到if(oldVnode.nodeType ===1&& oldVnode.hasAttribute(SSR_ATTR)){
oldVnode.removeAttribute(SSR_ATTR)
hydrating =true}// 这里浏览器dom环境下,也走不到if(isTrue(hydrating)){
if(hydrate(oldVnode, vnode, insertedVnodeQueue)){
invokeInsertHook(vnode, insertedVnodeQueue,true)return oldVnode
}elseif(process.env.NODE_ENV!=='production'){
warn('The client-side rendered virtual DOM tree is not matching '+'server-rendered content. This is likely caused by incorrect '+'HTML markup, for example nesting block-level elements inside '+'<p>, or missing <tbody>. Bailing hydration and performing '+'full client-side render.')}}// either not server-rendered, or hydration failed.// create an empty node and replace it
oldVnode =emptyNodeAt(oldVnode)// 这个方法就是把真实的dom转化为 vnode}// replacing existing elementconst oldElm = oldVnode.elm
const parentElm = nodeOps.parentNode(oldElm)// 获取父节点// 这个函数很重要,把vnode挂载到真实dom上// create new nodecreateElm(
vnode,
insertedVnodeQueue,// extremely rare edge case: do not insert if old element is in a// leaving transition. Only happens when combining transition +// keep-alive + HOCs. (#4590)
oldElm._leaveCb ?null: parentElm,
nodeOps.nextSibling(oldElm))// update parent placeholder node element, recursivelyif(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)}// #6513// invoke insert hooks that may have been merged by create hooks.// e.g. for directives that uses the "inserted" hook.const insert = ancestor.data.hook.insert
if(insert.merged){
// start at index 1 to avoid re-invoking component mounted hookfor(let i =1; i < insert.fns.length; i++){
insert.fns[i]()}}}else{
registerRef(ancestor)}
ancestor = ancestor.parent
}}// destroy old nodeif(isDef(parentElm)){
removeVnodes(parentElm,[oldVnode],0,0)// 删除旧的节点}elseif(isDef(oldVnode.tag)){
invokeDestroyHook(oldVnode)}}}invokeInsertHook(vnode, insertedVnodeQueue, isInitialPatch)return vnode.elm
}}
进入 createElm
functioncreateElm(vnode,
insertedVnodeQueue,
parentElm,
refElm,
nested,
ownerArray,
index){
// 这个一版不会走到,跳过if(isDef(vnode.elm)&&isDef(ownerArray)){
// This vnode was used in a previous render!// now it's used as a new node, overwriting its elm would cause// potential patch errors down the road when it's used as an insertion// reference node. Instead, we clone the node on-demand before creating// associated DOM element for it.
vnode = ownerArray[index]=cloneVNode(vnode)}
vnode.isRootInsert =!nested // for transition enter checkif(createComponent(vnode, insertedVnodeQueue, parentElm, refElm)){
return}const data = vnode.data
const children = vnode.children
const tag = vnode.tag
if(isDef(tag)){
if(process.env.NODE_ENV!=='production'){
if(data && data.pre){
creatingElmInVPre++}// 这种错误是没有注册组件,却使用组件if(isUnknownElement(vnode, creatingElmInVPre)){
warn('Unknown custom element: <'+ tag +'> - did you '+'register the component correctly? For recursive components, '+'make sure to provide the "name" option.',
vnode.context
)}}// 这里生成原生dom节点
vnode.elm = vnode.ns
? nodeOps.createElementNS(vnode.ns, tag): nodeOps.createElement(tag, vnode)setScope(vnode)// 跳过/* istanbul ignore if */if(__WEEX__){
// ...}else{
// 创建子节点createChildren(vnode, children, insertedVnodeQueue)if(isDef(data)){
invokeCreateHooks(vnode, insertedVnodeQueue)}insert(parentElm, vnode.elm, refElm)}if(process.env.NODE_ENV!=='production'&& data && data.pre){
creatingElmInVPre--}}elseif(isTrue(vnode.isComment)){
vnode.elm = nodeOps.createComment(vnode.text)insert(parentElm, vnode.elm, refElm)}else{
vnode.elm = nodeOps.createTextNode(vnode.text)insert(parentElm, vnode.elm, refElm)}}
createElm 的作用是通过虚拟节点创建真实的 DOM 并插入到它的父节点中
这里的 createComponent 方法目的是尝试创建子组件,在当前这个 case 下它的返回值为 false
接下来判断 vnode 是否包含 tag,如果包含,先简单对 tag 的合法性在非生产环境下做校验,看是否是一个合法标签