基本概念
渲染器的作用是把虚拟DOM渲染为特定平台上的真实元素。利用响应系统的能力,可以自动调用渲染器完成页面的渲染和更新。渲染器的重点是精确地找到vnode对象的变更点并只更新变更的内容。
挂载(mount):把虚拟DOM节点渲染为真实DOM节点的过程
更新(patch):将newVNode与上一次渲染的oldVNde进行对比,找到并更新变更点
实现思路
通过createRenderer
来创建渲染器,渲染器中包含render
渲染函数
function createRenderer() {
function render() {
// ...
}
return {
render
};
}
进行渲染时,除了要执行挂载动作外,还要执行更新、卸载动作
const renderer = createRenderer();
// 首次渲染 挂载
renderer.render(vnode1, document.querySeelct("#app"));
// 第二次渲染 打补丁
renderer.render(vnode2, document.querySeelct("#app"));
// 第三次渲染 卸载
renderer.render(null, document.querySeelct("#app"));
实际上挂载也可以看作是一种特殊的打补丁(patch),所以render
的基本实现
function render(vnode, container) {
if (vnode) {
// 存在新vnode container._vnode: 容器原本的vnode节点
patch(container._vnode, vnode, container);
} else {
if (container._vnode) {
// 存在旧vnode,不存在新vnode。卸载操作
unmount(container._vnode);
}
}
// 后续渲染中的旧vnode
container._vnode = vnode;
}
patch
函数
/**
* 渲染核心
*
* @export
* @param {*} n1 旧vnode
* @param {*} n2 新vnode
* @param {*} container 容器
*/
function patch(n1, n2, container) {
if (!n1) {
// 不存在旧的节点,挂载
mountElement(n2, container, anchor);
} else {
// 存在旧节点,更新
patchElement(n1, n2);
}
}
挂载节点和元素的属性
一个虚拟节点的数据结构为:type
标识标签名,children
可能为数组或普通字符串
const vnode = {
type: 'div',
props: {
id: 'foo'
},
children: [
{
type: 'p',
children: 'hello'
}
]
}
挂载元素
把vnode.type
的值作为标签名来创建DOM元素
function mountElement(vnode, container, anchor) {
const el = createElement(vnode.type));
if (typeof vnode.children === "string") {
// 设置文本
setElementText(vnode.children);
} else if (Array.isArray(vnode.children)) {
// 存在子节点
vnode.children.forEach((child