Vue 中 render 基础的渲染逻辑
在 Vue 中,是如何基于渲染器来实现 vdom 渲染的呢?
/**
* @param domString - DOM 节点信息
* @param container - DOM 元素
*/
const renderer = (domString, container) => {
container.innerHTML = domString
}
renderer('<h1>Vue</h1>', document.getElementById('app'))
上面代码执行的逻辑是:把<h1>Vue</h1>字符挂载到 id 为 app 的 DOM 元素里。
这样就是一个最简单的渲染函数。
然后 Vue 基于上面的渲染函数,一层一层的封装,写分支,写语句,写循环。
renderer 是渲染器,render 是渲染。渲染器的意思就是将 vdom 渲染为真实 DOM。
vdom 和 vnode 可以混用,代表的都是一个意思。注:
翻了翻自己的文章记录,居然没找到 vdom 相关的文章 ... 此处应该有一篇详细的 vdom 文章的链接。
渲染器把 vdom 节点渲染为真实 DOM 节点的过程称为「挂载」,英文用 mount 来表达。在 Vue 里,真实 DOM 挂载完成后,会触发 onMounted 钩子方法。所以,在 onMounted 钩子里可以访问到 DOM 元素。
function createRenderer() {
function render(vnode, container) {
if (vnode) {
//如果存在新的 vnode,那就需要 patch 一下
path(container._vnode, vnode, container)
} else {
if (container._vnode) {
//旧 vnode 存在,且新的 vnode 不存在,说明是 unmount 操作,需要将 dom 清空即可。
container.innerHTML = ''
}
}
container._vnode = vnode
}
return {
render
}
}
const renderer = createRenderer()
renderer.render(vnode, document.querySelector('#app'))
在 Vue 中,用 createRenderer 函数来创建一个渲染器,函数内部封装了 render 方法。
我们来看个执行例子:
const renderer = createRenderer()
//首次渲染
renderer.render(vnode1, document.querySelector('#app'))
//第二次渲染
renderer.render(vnode2, document.querySelector('#app'))
//卸载渲染
renderer.render(null, document.querySelector('#app'))
在「首次渲染」的时候,渲染器会把 vnode1 渲染为真实 DOM。渲染完成后 vnode1 内容会存储到 container._vnode 属性里,它就是后续渲染的旧vnode。
在「第二次渲染」的时候,旧 vnode 存在,渲染器会把 vnode2 作为新的 vnode,然后把新旧的 vnode 都传给 patch 函数进行打补丁。
在「卸载渲染」时,新 vnode 的值为 null,就是说不需要渲染 DOM,目前渲染的是 vnode2,所以渲染器会清空容器,上面代码中,暂时先用 container.innerHTML = '' 来达到目的。
上面是 render 的逻辑,下面来简单刷下 patch 函数处理逻辑。
patch 函数承载了重要的渲染逻辑,就是常说的补丁、挂载、Diff算法等等,都是根据这个函数展开的,我们后续细写。
const patch = (n1, n2, conatiner) => {
// ...
}
第一个参数 n1:旧 vnode;
第二个参数 n2:新 vnode;
第三个参数 container:挂载容器;
第一次渲染时,容器的 container._vnode 属性是不存在的,会得到 undefined,也就是说传的第一个参数是 undefined,patch 会执行挂载逻辑,忽略 n1,直接把 n2 的内容渲染到容器中。
图片授权基于 www.pixabay.com 相关协议
文章内容来源于《Vue.js 设计与实现》