上一章节学习了原始值响应式系统的设计,接下来学习渲染器的实现原理,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、学习了如何实现一个简单的渲染器实现,抽象出浏览器操作。