Vue.js 设计与实现 【渲染器】

第七章 渲染器的设计

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

回顾: 初识渲染器

渲染器就是把虚拟DOM渲染为真实DOM

 

实现思路:

  • 创建元素
  • 为元素添加属性和事件
  • 处理children

以上是简单的创建节点的过程,但渲染器的精髓在更新节点的阶段。

1. 渲染器与响应系统的结合

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

 

利用响应系统,自动调用渲染器完成页面的渲染和更新。副作用函数执行完毕后,会与响应式数据建立响应联系。修改count.value的值时,副作用函数会重新执行,完成重新渲染。所以上面的代码运行完毕后,最终渲染到页面的内容是<h1>2</h1>

从本章开始,将使用@vue/reactivity包提供的响应式API进行讲解。

2. 渲染器的基本概念

  • 渲染器(renderer): 渲染器的作用是把虚拟DOM渲染为特定平台的真实元素。在浏览器上,渲染器会把DOM渲染为真实DOM元素。注意与render的区别。
  • 虚拟DOM(vdom):虚拟DOM和真实DOM的结构一样,都是由一个个节点组成的树型结构。
  • 虚拟节点(vnode):虚拟DOM是树型结构,其中任何一个vnode节点都可以是一棵子树,因此vdom和vnode可以替换使用。本书统一使用vnode
  • 挂载(mount):渲染器把虚拟DOM节点渲染成真实DOM节点的过程叫做挂载
  • 容器(container):渲染器通常需要一个挂载点作为参数,用来指定具体的挂载位置。

代码:

 

createRenderer函数用来创建一个渲染器。调用createRenderer函数会得到一个render函数,该render函数会以container为挂载点,将vnode渲染为真实DOM并添加到该挂载点下。

  • 渲染器和渲染:渲染器是更加宽泛的概念,其包含了渲染。渲染器不仅可以用来渲染,还可以用来激活已有的DOM元素。render函数只是其中一部分。

 

  • 挂载、更新(打补丁patch)和卸载(unmount)

 

挂载: 首先用createRenderer函数创建一个渲染器,然后用渲染器renderer.render函数执行渲染任务。首次调用renderer.render函数时,只需要创建新的DOM元素即可,这个过程只涉及挂载

 

更新: 多次在同一个container上调用renderer.render函数进行渲染时,除了要挂载动作外,还要执行更新动作。在这种情况下,就要渲染器就要将newVnode和上一次渲染的oldVnode进行比较,试图找到更新变更点。这个过程叫“打补丁”(或更新)

实际上,挂载动作本身也可以看成是一种特殊的打补丁,其特殊之处在于旧的vnode是不存在的。

 

卸载: 第三次渲染是,新vnode 的值为null,即什么都不渲染。渲染器需要清空容器,暂且使用container.innerHTML  = '' 来清空容器。

综上:render函数的基本实现。

 

patch的函数签名:

实际上,patch函数时整个渲染器的核心入口,承载了最重要的渲染逻辑。

 

 3. 自定义渲染器

渲染器不仅能够把虚拟DOM渲染为浏览器平台上的真实DOM,通过将渲染器设计为可配置的“通用”渲染器,即可实现渲染到任意目标平台上。

通过抽象,将浏览器特定的API抽离,这样就可以使得渲染器的核心不依赖于浏览器。在此基础上,我们再为那些被抽离的API提供可配置的接口,即可实现渲染器的跨平台能力。

先从渲染一个普通的<h1>标签开始,可以用如下vnode对象来描述一个<h1>标签

 

上面的vnode对象,使用type属性来描述vnode类型,当type属性时字符串类型值时,可以认为其描述的是普通标签,并使用该type属性的字符串值作为标签的名称。这样我们可以使用render函数来渲染,如下面代码所示:

 

为了完成渲染工作,需要补充patch函数

 

n1代表旧vnode,n2代表新vnode。当n1不存在时,意味着没有旧vnode,此时只需要执行挂载即可。这时调用mountElement完成挂载,其实现如下:

 

上面的代码实现了挂载一个普通标签元素的工作,但是我们的目的是设计一个不依赖于浏览器平台的通用渲染器,而mountElement函数内调用了大量依赖于浏览器的api,所以第一步要做的就是将这些浏览器特有的api抽离。具体可以将这些操作DOM的API作为配置项,该配置项可以作为createRenderer的参数,如下面代码
 

 

这里直接把用于操作DOM的API封装为一个对象,并将其传递给createRenderer函数。这样在mountElement等函数内就可以通过配置项来取得操作DOM的API了

 

接着从配置项取出得到的API重新实现mountElement函数:

 

如上面代码所示,重构后的mountElement 函数在功能上没有任何变化。不同的是,它不再直接依赖于浏览器特有的API了。这意味着,只要传入不同的配置项,就能够完成非浏览器环境下的渲染工作。

接下来我们可以实现一个用来打印渲染器操作流程的自定义渲染器,如下面代码所示(7.3自定义渲染器.html可演示)

 

 

总结:自定义渲染器就是通过抽象的手段,让核心代码不再依赖平台特有的 API,再通过支持个性化配置的能力来实现跨平台。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值