1、渲染器与响应系统的结合
响应系统和渲染器之间的管理: 利用响应系统的能力, 自动调用渲染器完成页面的渲染和更新
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div id="app"></div>
<script src="https://unpkg.com/@vue/reactivity@3.0.5/dist/reactivity.global.js"></script> // vue的reactivity包
<script src="renderer.js" defer></script>
</body>
</html>
const { effect, ref } = VueReactivity
function renderer(domString, container) {
container.innerHTML = domString
}
const count = ref(1)
effect(() => {
renderer(`<h1>${count.value}</h1>`, document.getElementById('app'))
})
count.value++
2、渲染器的基本概念
渲染器: 渲染器的作用是把虚拟DOM渲染为特定平台上的真实元素。比如在浏览器平台上, 渲染器会把虚拟DOM渲染为真实的DOM元素
虚拟DOM: vdom或者vnode
挂载: 把虚拟DOM节点渲染为真实的DOM节点的过程
容器: 渲染器通常需要接受一个挂载点作为参数, 用来指定具体的挂载位置, 这个挂载点就是容器(container)
function createRenderer() {
function render(vnode, container) {
}
function hydrate(vnode, container) {
}
return {
render,
hydrate
}
}
问: 为什么需要用createRenderer, 而不是直接使用render函数
答:渲染器是更加宽泛的概念,render只是其中的一部分。 渲染器不仅可以用来渲染, 还可以用来(在同构渲染的情况下)激活已有的DOM元素, 甚至创建应用的createApp也是渲染器的一部分
渲染器中的渲染包括了挂载, 更新和卸载的功能
function createRenderer() {
// n1旧vnode n2新vnode, container容器
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; // 存储当前的节点
}
}
const renderer = createRenderer();
const vnode1 = { type: "h1", children: "文字"};
const vnode2 = { type: "h2", children: "文字"};
renderer.render(vnode1, document.getElementById("app"));
renderer.render(vnode2, document.getElementById("app"));
renderer.render(null, document.getElementById("app"));
3、自定义渲染器
目标是设计一个不依赖浏览器平台的通用渲染器, 涉及浏览器的API应该抽取出来, 后续作为配置项
function createRenderer(options) {
const{ createElement, setElementText, insert } = options;
function mountElement(vnode, container) {
const el = createElement(vnode.type);
if(typeof vnode.children === "string") {
setElementText(el, vnode.children)
}
insert(el, container);
}
// n1旧vnode n2新vnode, container容器
function patch(n1, n2, container) {
if(!n1) {
mountElement(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({
createElement(tag){
return document.createElement(tag);
},
setElementText(el, text){
el.textContent = text;
},
insert(el, parent, anchor = null) {
parent.insertBefore(el, anchor) // parent父节点 el需要插入的节点 anchor插入时需要插入在这个节点前面
}
});
可以更改配置以适应平台
const renderer = createRenderer({
createElement(tag){
console.log("创建元素"+ tag)
return {tag};
},
setElementText(el, text){
console.log(`设置元素${JSON.stringify(el)}的文本内容${text}`)
el.textContent = text;
},
insert(el, parent, anchor = null) {
console.log(`将元素${JSON.stringify(el)}添加到${parent}下`)
parent.children = el;
}
});
// 测试结果
创建元素h1
设置元素{"tag":"h1"}的文本内容文字
将元素{"tag":"h1","textContent":"文字"}添加到{"type":"parent"}下