const vnode = { ... } // 某块 DOM 对应的 vnode render(vnode, document.getElementById('app'))
const render = (vnode, container) => { if (vnode == null) { // 卸载 if (container._vnode) { unmount(container._vnode, null, null, true); } } else { // 挂载或更新 patch(container._vnode || null, vnode, container, null, null, null, isSVG); } container._vnode = vnode; };我们可以通过 render 函数进行挂载、更新和删除。
ender(vnode, document.getElementById("app"));更新:
// 首次渲染 render(oldVNode, document.getElementById("app")); // 更新 render(newVNode, document.getElementById("app"));删除
// 首次渲染 render(oldVNode, document.getElementById("app")); // 删除 render(null, document.getElementById("app"));patch 函数
const patch: PatchFn = ( oldVNode, newVNode ) => { if (oldVNode === newVNode) { return } const { type, ref, shapeFlag } = newVNode switch (type) { case Text: processText(oldVNode, newVNode) break case Comment: processCommentNode(oldVNode, newVNode) break case Static: if (oldVNode == null) { mountStaticNode(newVNode) } else if (__DEV__) { patchStaticNode(oldVNode, newVNode) } break case Fragment: processFragment( oldVNode, newVNode ) break default: if (shapeFlag & ShapeFlags.ELEMENT) { processElement( oldVNode, newVNode ) } else if (shapeFlag & ShapeFlags.COMPONENT) { processComponent( oldVNode, newVNode ) } else if (shapeFlag & ShapeFlags.TELEPORT) { ;(type as typeof TeleportImpl).process( oldVNode as TeleportVNode, newVNode as TeleportVNode ) } else if (__FEATURE_SUSPENSE__ && shapeFlag & ShapeFlags.SUSPENSE) { ;(type as typeof SuspenseImpl).process( oldVNode, newVNode ) } else if (__DEV__) { warn('Invalid VNode type:', type, `(${typeof type})`) } } }在 patch 函数中,我们会根据 VNode 的不同 type 来进行不同的处理,比如处理文本节点、注释节点、DOM 节点、组件。。。
const processElement = (oldVNode, newVNode) => { if (oldVNode == null) { mountElement(newVNode); } else { patchElement(oldVNode, newVNode); } };如果 oldVNode 为 null,那么就是挂载。
并且,Vue 不仅可以在浏览器端渲染(CSR),还可以在服务端渲染(SSR),所以 Vue 就将 runtime 分成了两个包,渲染的核心逻辑代码会放到 runtime-core 中;浏览器渲染相关的代码会放到 runtime-dom 中,最终通过 options 传入。