Vue 进阶系列丨Patch 和模板编译

Vue 进阶系列教程将在本号持续发布,一起查漏补缺学个痛快!若您有遇到其它相关问题,非常欢迎在评论中留言讨论,达到帮助更多人的目的。若感本文对您有所帮助请点个赞吧!


2013年7月28日,尤雨溪第一次在 GItHub 上为 Vue.js 提交代码;2015年10月26日,Vue.js 1.0.0版本发布;2016年10月1日,Vue.js 2.0发布。

最早的 Vue.js 只做视图层,没有路由, 没有状态管理,也没有官方的构建工具,只有一个库,放到网页里就可以直接用了。

后来,Vue.js 慢慢开始加入了一些官方的辅助工具,比如路由(Router)、状态管理方案(Vuex)和构建工具(Vue-cli)等。此时,Vue.js 的定位是:The Progressive Framework。翻译成中文,就是渐进式框架。

Vue.js2.0 引入了很多特性,比如虚拟 DOM,支持 JSX 和 TypeScript,支持流式服务端渲染,提供了跨平台的能力等。Vue.js 在国内的用户有阿里巴巴、百度、腾讯、新浪、网易、滴滴出行、360、美团等等。

Vue 已是一名前端工程师必备的技能,现在就让我们开始深入学习 Vue.js 内部的核心技术原理吧!


什么是 Patch

上一篇文章我们已经知道了 Vue.js 的虚拟 DOM 渲染机制以及虚拟 DOM 所做的两件事:

  • 提供与真实 DOM 节点所对应的虚拟节点 vnode

  • 将新的虚拟节点和旧的虚拟节点进行对比,然后更新页面

vnode 其实就是 JS 创建的一个节点描述对象,描述了应该怎样去创建真实的 DOM 节点,我们已在上一篇文章讲解,详细信息请移步Vue 进阶系列丨虚拟DOM和VNode

将新的虚拟节点和旧的虚拟节点进行对比这个过程,就是我们将要介绍的patch,也可以叫做 patching 算法。通过它可以对比新旧两个 vnode 之间的不同,计算出真正要重新渲染的 DOM 节点,而不是暴力覆盖原有的 DOM。

这样做的目的,是因为直接操作 DOM 的速度远不如 JS 代码的执行速度快,所以将大量的 DOM 操作移到了 JS 中,本质上是使用 JS 的运算成本去替换操作 DOM 的成本,而 JS 的运算速度快得多,这样做能很大程度的节省性能,所以才会有虚拟 DOM 这个东西。


Patch介绍

Patch的主要目的是对比新旧两个虚拟DOM,然后渲染视图。由于是对比后的操作,也就包含了新增、删除、修改等操作。那么什么情况下去新增?去删除?去修改呢?


新增节点

这里我们主要讨论什么情况下新增节点。如果一个新的 vnode 中,一个节点已经存在于 oldVnode 中了,那么就不需要重新创建新的节点去替换已经存在的节点了,如果新的节点在 DOM 中不存在,我们就需要创建一个新的节点并插入到 DOM 中。

第一种情况,首次渲染时。当页面首次进行渲染时,oldVnode 还不存在,我们就需要使用 vnode 去生成真实的 DOM 来渲染视图。

第二种情况,当 oldVnode 和 vnode 完全不是同一个节点时,我们要使用 vnode 生成真实 DOM 来渲染视图。


删除节点

删除节点就是当一个节点只在 oldVnode 存在,vnode 不存在时,我们需要以 vnode 为准,所以 vnode 中不存在的节点都将被删除。


更新节点

当新旧两个节点是同一个节点,我们就需要对这两个节点进行比较细致的对比,然后对 oldVnode 在视图上对于的真实节点进行更新。

比如新旧两个节点都是文本节点,但是文本内容不一样,我们就要重新设置oldVnode 在视图中对应的真实 DOM 节点中的文本。


模板编译

模板编译是 Vue.js 的一个重要技术,通过在模板中定义变量、方法等,声明式的描述状态和 DOM 之间的关系,然后通过模板来生成真实 DOM 并将其展示在界面上。

我们先来介绍一下模板到真实的视图的整个流程:

  • 通过模板编译,生成渲染函数

  • 执行渲染函数生成虚拟 DOM,也就是 vnode

  • patching 算法对比虚拟 DOM,最小量的进行 DOM 操作,渲染视图

通过模板编译生成渲染函数的过程,也要分为三个步骤。先将模板解析成 AST(Abstract Syntax Tree,抽象语法树),由于静态节点不需要总是重新渲染,所以遍历抽象语法树,对静态节点做一个标记,避免重新渲染提高性能,最后是使用 AST 生成渲染函数。

  • 将模板解析为 AST

  • 对 AST 标记静态节点

  • 使用 AST 生成渲染函数

这三部分在模板编译中,分别抽象成了三个模板来实现各自的功能,分别是;

  • 解析器

  • 优化器

  • 生成器


解析器

解析器的作用就是将模板解析成 AST,解析器内部还分成了很多小解析器,其中包括过滤器解析器、文本解析器和 HTML 解析器。

  • 在使用模板时,可以使用过滤器解析器来解析过滤器。

  • 文本解析器就是用来解析文本的,主要的目的是来解析那些带变量的文本,如:

hfun {{ name }}
  • 最后也是最重要的是 HTML 解析器,用来解析模板,通过解析 HTML 标签的开始位置、结束位置、文本或者注释时,都会触发对应的钩子函数,然后通过钩子函数将参数传递出来。我们通过传递出来的参数,来构建AST 抽象语法树。

  • AST 和 vnode 类似,本质上也是 JS 对象,通过 JS 对象来描述节点。


优化器

优化器的目的是遍历 AST,检测所有静态节点和静态子树,给它打上一个标记。如:

<p> hello hfun </p>

上面的代码中,p 标签就是一个静态节点,他没有使用任何变量,一旦首次渲染完成之后,后期无论状态怎么变,这个节点都不需要重新渲染。

当 AST 中的静态子树被打上标记之后,每次重新渲染时,都不需要为打上标记的静态节点创建新的虚拟节点,而是直接克隆已存在的虚拟节点,关于克隆节点我们在讲解 vnode 一节有介绍,详情请移步Vue 进阶系列丨虚拟DOM和VNode。在虚拟 DOM 的更新操作时,如果发现两个节点是同一个节点,正常情况会对这两个节点进行更新,但是如果这两个节点是静态节点,则直接跳过更新节点的步骤。

优化器的主要作用是避免对不会改变的节点做浪费时间和性能的工作,从而提高性能。静态节点除了首次渲染之外,后续不需要任何重新渲染的工作。


代码生成器


代码生成器的作用是将 AST 转换成渲染函数的过程。如:

<p title="hfun" @click="onClick">hfun</p>

生成后的渲染函数的代码字符串是:

with(this){ 
  return _c(
    'p',
    {
      attrs:{"title","hfun"},
      on:{"click",onClick}
    },
    [_v("hfun")]
  )
}

注:with 语句的作用是改变代码块的执行作用域,关于作用域的详细介绍,请移步ES6 新特性梳理系列丨Var + Let + Const 的区别与作用域

前面说过,渲染函数作用是创建 vnode,如何创建 vnode 呢?是因为代码中包含了很多函数调用,_c、_v 等都是虚拟 DOM 提供的创建 vnode 的方法,vnode 有很多种类型,针对不同类型有不同的创建方法。比如,_c 可以创建元素类型的 vnode,_v 可以创建文本类型的 vnode,_e 可以创建注释类型的 vnode。


Vue 进阶系列教程将在本号持续发布,一起查漏补缺学个痛快!若您有遇到其它相关问题,非常欢迎在评论中留言讨论,达到帮助更多人的目的。若感本文对您有所帮助请点个赞吧!

叶阳辉

HFun 前端攻城狮

往期精彩:

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值