05-渲染器的设计

50 阅读3分钟

上一章节学习了原始值响应式系统的设计,接下来学习渲染器的实现原理,Vue.js中很多功能都依靠渲染器,如Transition组件、Teleport组件、Suspence组件等。

1、渲染器与响应系统

渲染器可以把虚拟DOM渲染到浏览器上,配合响应式系统副作用函数可以实现自动化,代码如下所示

const { effect, ref } = VueReactivity

function renderer(domString, container) {
  container.innerHTML = domString
}

const count = ref(1)

effect(() => {
  renderer(`<h1>${count.value}</h1>`, document.getElementById('app'))
})

暴露一个全局VueReactivity,包含副作用和ref,定义渲染函数通过innerHTML进行简单渲染

完成响应自动渲染。

2、渲染器的概念

渲染器renderer把虚拟DOM渲染到特定浏览器渲染为真实DOM。

渲染(render)渲染器(renderer)不要搞混

渲染器把虚拟DOM节点渲染为真实DOM的过程叫做挂载,渲染器把该DOM元素作为容器元素

,并把内容渲染到其中,container来表达容器

function createRenderer() {

  function patch(n1, n2, container) {

  }

  function render(vnode, container) {
		if (vnode) {
      // 新 vnode 存在,将其与旧 vnode 一起传递给 patch 函数进行打补丁
      patch(container._vnode, vnode, container)
    } else {
      if (container._vnode) {
        // 旧 vnode 存在,且新 vnode 不存在,说明是卸载(unmount)操作
        // 只需要将 container 内的 DOM 清空即可
        container.innerHTML = ''
      }
    }
    // 把 vnode 存储到 container._vnode 下,即后续渲染中的旧 vnode
    container._vnode = vnode
  }
  
  return {
    render
  }
}

创建渲染函数,判断是否有新的虚拟节点,如果新旧都有,一起patch,如果旧的虚拟节点存在新的不存在,那就是unmount卸载操作。

渲染DOM时第一次渲染把vnode直接放入容器,第二次渲染之前容器的vnode作为旧vnode一起patch(打补丁,更新怎么叫都可以),第三次渲染如果传null,那么清空容器

3、自定义渲染器

定义一个h1标签

自定义一个h1标签,可以通过vnode进行描述,通过type属性来描述vnode的类型,type的字符串值作为值,可以通过render进行渲染,代码如下:

function createRenderer(options) {

  const {
    createElement,
    insert,
    setElementText
  } = options

  function mountElement(vnode, container) {
    const el = createElement(vnode.type)
    if (typeof vnode.children === 'string') {
      setElementText(el, vnode.children)
    }
    insert(el, container)
  }

  function patch(n1, n2, container) {
    if (!n1) {
      mountElement(n2, container)
    } else {
      //
    }
  }

  function render(vnode, container) {
		if (vnode) {
      // 新 vnode 存在,将其与旧 vnode 一起传递给 patch 函数进行打补丁
      patch(container._vnode, vnode, container)
    } else {
      if (container._vnode) {
        // 旧 vnode 存在,且新 vnode 不存在,说明是卸载(unmount)操作
        // 只需要将 container 内的 DOM 清空即可
        container.innerHTML = ''
      }
    }
    // 把 vnode 存储到 container._vnode 下,即后续渲染中的旧 vnode
    container._vnode = vnode
  }
  
  return {
    render
  }
}

const renderer = createRenderer({
  createElement(tag) {
    return document.createElement(tag)
  },
  setElementText(el, text) {
    el.textContent = text
  },
  insert(el, parent, anchor = null) {
    parent.insertBefore(el, anchor)
  }
})
const vnode = {
  type: 'h1',
  children: 'hello'
}
renderer.render(vnode, document.querySelector('#app'))

渲染主要包括以下部分:patch:接收三个参数,n1代表旧vnode,n2代表新vnode,n1不存在时表示没有旧vnode直接挂载即可。挂载:先创建DOM元素,设置元素内容textContent,最后appendChild添加到DOM容器中。完善:加入配置项不调用浏览器的API。

总结

1、学习了响应式系统的和渲染器的关系,依靠响应式系统,可以使渲染自动化

2、学习了渲染器和渲染的概念,学习了什么是虚拟节点vnode,学习了虚拟节点存储容器,新节点的挂载,新旧节点对比后打补丁。

3、学习了如何实现一个简单的渲染器实现,抽象出浏览器操作。