vlambda博客
学习文章列表

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(nulldocument.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 的内容渲染到容器中。

当只有新元素时,渲染器直接执行挂载;当 新旧内容都存在时,对比新旧 vnode 执行打补丁操作; 当没有新元素时,则执行卸载。



图片授权基于 www.pixabay.com 相关协议

文章内容来源于《Vue.js 设计与实现》