渲染器相关的属于和概念
- 渲染器(renderer)和渲染(render)
- 渲染器: 作用是把虚拟 DOM 渲染为特定平台上的真实元素。浏览器平台上,渲染器会把虚拟 DOM 渲染为真实 DOM 元素。
- 渲染: render 动词,表示渲染的动作
- 虚拟DOM和虚拟节点(vnode)
- 虚拟 DOM 和真实 DOM 的结构一样,都是由一个个节点组成的树型结构
- 虚拟节点: 虚拟 DOM 是树型结构,这棵树中的任何一个 vnode 节点都可以是一棵子树,因此 vnode 和 vdom 有时可以替换使用。
- 挂载(mounted)
- 渲染器把虚拟 DOM 节点渲染为真实 DOM 节点的过程叫作挂载,英文 mount, Vue.js 组件中的 mounted 钩子就会在挂载完成时触发。这就意味着,在 mounted 钩子中可以访问真实 DOM 元素。
- 打补丁(patch)
- 首次渲染时已经把 oldVNode 渲染到 container 内了,第二次渲染 newVNode ,就不能简单地执行挂载动作了。渲染器会使用 newVNode 与上一次渲染的 oldVNode 进行比较,试图找到并更新变更点。这个过程叫作“打补丁”(或更新 ),英patch 。实际上,挂载动作本身也可以看作一利特殊的打补丁,它的特殊之处在于旧的 vnode 是不存在的。
function createRenderer() {
function patch(n1, n2, container) {
}
function render(vnode, container) {
if (vnode) {
patch(container._vnode, vnode, container)
} else {
if (container._vnode) {
container.innerHTML = ''
}
}
container._vnode = vnode
}
return {
render
}
}
const renderer = createRenderer()
renderer.render(vnode1, document.querySelector('#app'))
renderer.render(vnode2, document.querySelector('#app'))
renderer.render(null, document.querySelector('#app'))
渲染器的基本实现
- 渲染器不仅能够把虚拟 DOM 渲染为浏览器平台上的真实 DOM。过将渲染器设计为可配置的“通用”渲染器,即可实现渲染到任意目标平台上。
- 为了让渲染器不直接依赖浏览器平台特有的 API,我们将这些来创建、修改和删除元素的操作抽象成可配置的对象。用户可以在调用createRenderer 函数创建渲染器的时候指定自定义的配置对象,从而实现自定义的行为。
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) {
patch(container._vnode, vnode, container)
} else {
if (container._vnode) {
container.innerHTML = ''
}
}
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 renderer2 = createRenderer({
createElement(tag) {
console.log(`创建元素 ${tag}`)
return { tag }
},
setElementText(el, text) {
console.log(`设置 ${JSON.stringify(el)} 的文本内容:${text}`)
el.text = text
},
insert(el, parent, anchor = null) {
console.log(`将 ${JSON.stringify(el)} 添加到 ${JSON.stringify(parent)} 下`)
parent.children = el
}
})