社区编辑申请
注册/登录
Vue.js设计与实现之十一-渲染器的设计
开发 前端
在本文中,我们了解到渲染器的作用和实现,知道其是将虚拟DOM转为真实DOM挂载到节点上,在进行节点更新的时候是通过patch实现的。为了实现跨平台渲染,那么就需要用户通过自定义配置项渲染器,从而实现DOM的渲染挂载。

1、写在前面

在Vue.js框架很多功能依赖渲染器实现,也是框架性能的核心,能够直接影响框架性能。对此,Vue.js3的渲染器通过快捷路径更新的方式,利用编译器提供的信息提升性能。

2、渲染器和响应系统的结合

渲染器是用来执行渲染任务的,可以在浏览器平台来渲染真实DOM元素,它还能实现框架跨平台能力。

前面已经实现了reactivity的源码,为了后续方便讲解后续的操作,对此可以直接使用vue.js3的源码导入使用。

<script src="https://unpkg.com/@vue/reactivity@3.0.5/dist/reactivity.global.js" />

这样就可以通过暴露的API使用:

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++

渲染器把虚拟DOM节点渲染为真实DOM节点的过程叫做挂载,在Vue中mounted钩子函数会在挂载完成时触发,从而实现虚拟DOM的挂载。此外,渲染器还需要一个挂载点作为参数,用于指定具体的挂载位置,渲染器会将此DOM元素作为容器元素,可以实现内容的渲染。

在Vue.js3中是通过createRenderer函数来创建渲染器renderer,而renderer不仅包含渲染器函数render,还包括服务端渲染使用的hydrate函数。用于创建应用的createApp函数也是渲染器的一部分。

const renderer = createRenderer();
//首次渲染
renderer.render(oldVNode, document.querySelector("#app"));
//第二次渲染
renderer.render(newVNode, document.querySelector("#app"));

在上面代码中,首先调用createRenderer函数创建一个渲染器,通过调用渲染器的renderer.render函数创建新的DOM元素,而后将虚拟DOM节点vnode渲染到挂载点上,这就是挂载。

在同一个挂载DOM上,调用renderer.render函数进行渲染时,渲染器除了实现常规的DOM挂载外,还需要执行页面更新操作,也就是DOM更新。在渲染器会使用vnode与上次渲染的oldVNode进行比较,从而找到并更新变更点,此过程叫做"打补丁"(patch)即更新。

function createRenderer(){
function render(domString, container){
if(vnode){
// 新vnode存在,将其与旧vnode传递给patch函数,进行打补丁
patch(container._vnode, vnode, container);
}else{
if(container._vnode){
// 如果旧节点vnode存在,则需要清空挂载点的DOM元素
container.innerHTML = "";
}
}
// 将vnode存储到container._vnode中
container._vnode = vnode
}
return {
render
}
}

在上面代码中,是render函数的基本实现。patch函数是整个渲染器的核心入口,承载了最重要的渲染逻辑。

patch(n1, n2, container){
/***/
}
  • n1:旧VNode。
  • n2:新VNode。
  • container:挂载容器。

3、自定义渲染器

我们看看如何自定义实现渲染器,实现跨平台能力,那就从普通的h1标签开始。

const vnode = {
type:"h1",
children:"pingping"
}

在上面代码中实现了一个简单的vnode,使用type属性描述vnode类型,type属性是字符串类型值时是普通标签。

const vnode = {
type:"h1",
children:"pingping"
}
const renderer = createRenderer();
//首次渲染
renderer.render(vnode, document.querySelector("#app"));

这样,我们就可以渲染为<h1>pingping</h1>为了完成渲染工作,下面可以补充patch函数。

function createRenderer(){
function patch(n1, n2, container){
//如果n1不存在的时候,意味着挂载,需要调用mountElement函数完成挂载
if(!n1){
mountElement(n2, container);
}else{
//n1存在,即打补丁更新DOM节点
}
}
function render(domString, container){
if(vnode){
// 新vnode存在,将其与旧vnode传递给patch函数,进行打补丁
patch(container._vnode, vnode, container);
}else{
if(container._vnode){
// 如果旧节点vnode存在,则需要清空挂载点的DOM元素
container.innerHTML = "";
}
}
// 将vnode存储到container._vnode中
container._vnode = vnode
}
return {
render
}
}

在上面代码中,patch函数中参数n1不存在时,意味着没有旧VNode,此时只需要进行挂载即可。

function mountElement(vnode, container){
const el = document.createElement(vnode.type);
if(typeof vnode.children === "string"){
el.textContent = vnode.children;
}
container.appendChild(el);
}

在上面代码中,简单实现了一个普通标签元素的挂载工作。Vue.js3是个跨平台框架,得益于一个不依赖平台的通用渲染器,而在mountElement函数中依赖了大量浏览器API。想要设计通用渲染器,就必须剥离与平台相关的API,当然可以将这些操作DOM的API作为createRenderer的配置项。

const renderer = createRenderer({
//创建元素
createElement(tag){
return document.createElement(tag);
},
//设置元素的文本节点
setElementText(el, text){
el.textContent = text;
},
//用于给指定父节点添加指定元素
insert(el, parent, achor = null){
parent.insertBefore(el, anchor);
}
})

上面代码中,可以将操作DOM的API封装为一个对象,并把它传递到createRenderer函数,可以通过配置项来取得操作DOM的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);
}
function patch(n1, n2, container){
//...
}
function render(domString, container){
//...
}
return {
render
}
}

现在我们对自定义渲染器有个更深刻的认识,自定义渲染器不是什么黑盒子,只是通过代码抽象,让核心代码不再强依赖于平台特定API,通过自定义配置项实现跨平台。

4、写在最后

在本文中,我们了解到渲染器的作用和实现,知道其是将虚拟DOM转为真实DOM挂载到节点上,在进行节点更新的时候是通过patch实现的。为了实现跨平台渲染,那么就需要用户通过自定义配置项渲染器,从而实现DOM的渲染挂载。

责任编辑:姜华 来源: 前端一码平川
相关推荐

2022-04-04 16:53:56

2022-06-20 22:37:25

Linux操作系统命令

2022-06-15 11:51:14

Vue3开发避坑

2022-05-17 08:39:05

VueViteTypeScript

2022-06-23 09:22:57

Vue技巧前端

2022-06-15 09:01:41

2022-06-07 14:15:44

Vue开发工具

2022-04-01 10:41:09

Vue.js开发工具

2022-06-15 16:16:21

分布式数据库鸿蒙

2022-05-09 11:19:12

CSS函数开源

2022-04-06 11:27:05

harmonyeTS 开发NAPI开发

2022-05-19 14:57:30

CSS代码工具

2022-06-20 15:19:51

前端监控方案

2022-04-21 14:29:40

前端文件预览

2022-06-13 06:33:04

浏览器浏览器插件

2022-06-23 06:34:56

Node.js子线程

2022-06-16 07:32:38

VSCodePython插件

2022-06-02 14:27:05

UI框架JS

2022-04-18 10:37:01

鸿蒙操作系统开发工具

2022-04-24 15:15:57

前端技术阿里

同话题下的热门内容

手把手教你用装饰器扩展 Python 计时器哪个版本的JVM最快?IOC-Golang 的 AOP 原理与应用分析了 700 万份工作需求,市场需求最高的八种编程语言是这些Vue 里,多级菜单要如何设计才显得专业?Vue 2.7 正式发布,代号为 Naruto手把手教你实现一个 Python 计时器2022 年编程语言趋势:Swift、Kotlin 热度持续增长,收入最高的五种语言竟是它们

编辑推荐

太厉害了,终于有人能把TCP/IP协议讲的明明白白了!牛人5次面试腾讯不成功的经验HBase原理–所有Region切分的细节都在这里了Javascript如何监听页面刷新和关闭事件如何搭建一个HTTPS服务端
我收藏的内容
点赞
收藏

51CTO技术栈公众号