Vue碎碎念 —— 第一篇 Vue发展史及Vue3.0优势

421 阅读13分钟

序言:

Vue算是个老话题了,相关学习资料也不胜枚举。

      我写这个连载文章的初衷是想把Vue从1.X到3.X都体系化的梳理一遍,想让对Vue有一定了解的开发者,能更深入系统的把Vue这事搞懂。

      与此同时从源码的角度聊聊他为什么会受到这么普遍的使用,又在不断的迭代过程中优化了哪些能力。

      文章主要围绕着: Vue发展史及Vue3.0优势 、Vue2源码设计三、Vue3源码设计来开展。那今天开个头,看看Vue的发展史。

第一篇主要泛泛的介绍下Vue的发展,在每次迭代后出现了什么性能提升。要是深入探讨如何提升的,我会在源码篇中介绍。

Vue的诞生

老生常谈:Vue.js 是由前谷歌工程师尤雨溪(Evan You)创立的。尤雨溪在他的工作中使用了 AngularJS,但他认为 AngularJS 存在一些复杂性,特别是在项目较小的时候。他想要创建一个更轻便、更易于上手的框架,同时保持一些如数据绑定和组件化的强大功能。

Vue.js 最初的版本于2014年2月发布。尤雨溪将 Vue 设计为一个易于集成的框架,可以只在视图层使用,也可以作为在复杂应用中与其他库和项目协同工作的框架。这种灵活性和易用性使得 Vue.js 很快获得了开发者社区的广泛关注和采用。

Vue1.X

      在Vue1.X时日常使用的核心功能,开发者们能触碰到逻辑其实已经确立了下来,比如:

  1. 响应式数据绑定

• Vue.js 的核心特性之一是响应式数据绑定。通过使用 Object.defineProperty() 对数据进行劫持,Vue 可以追踪数据变化并自动更新 DOM。当数据变化时,只有依赖这些数据的组件会重新渲染,使得更新效率高。

  1. 组件系统

• Vue.js 1.0 提供了一个简单但强大的组件系统。开发者可以创建可复用的组件,这些组件封装了自己的视图和逻辑,便于构建大型应用。每个组件实例都有独立的作用域,使得数据管理更清晰。

  1. 指令系统

• Vue.js 包括一套扩展的 HTML 属性,称为指令。这些指令提供了声明式的方法来将数据绑定到视图上,常用的指令有 v-model、v-show、v-if、v-for 等,它们简化了 DOM 操作。

  1. 事件处理

• 通过 v-on 指令,Vue.js 允许开发者很方便地在模板中绑定事件监听器。这些监听器可以调用组件中定义的方法,从而响应用户输入。

  1. 过滤器

• Vue.js 允许开发者定义过滤器,这些过滤器可以在模板中用于文本格式化。过滤器可以串联使用,也可以在 JavaScript 表达式中使用,为视图层的数据处理提供了灵活性。

  1. 计算属性

• 计算属性是基于它们的依赖进行缓存的数据属性。如果一个计算属性依赖的响应式属性未发生变化,它就不会重新计算,这使得计算属性比方法更高效,特别是在有重计算需求时。

  1. 依赖收集与观察者

• 依赖收集确保只有在数据变化时才重新计算和重新渲染,避免了不必要的计算和DOM更新。每个组件都相当于一个观察者,精确控制其依赖的数据,从而提高性能。

但在初代版本中并没有虚拟dom,所以采用的是直接的 DOM 操作和更新策略。这个响应式系统的核心是‘依赖追踪’和‘变量通知’。执行流程大概是这样的:

1. 响应式数据

当你创建一个 Vue 实例并为其数据对象添加属性时,Vue 会使用 Object.defineProperty() 方法将这些属性转换成 getter/setter。这个过程称为“数据劫持”。通过这种方式,Vue 可以追踪对数据的访问和修改。

2. 依赖收集

在 Vue 组件的模板被渲染时,任何被访问的响应式数据都会触发其 getter。在 getter 函数中,Vue 会记录哪些组件依赖于这些数据属性。这称为“依赖收集”。Vue 中的每个组件实例都有一个对应的“观察者”对象,这个对象会记录所有数据属性依赖项。

3. 变更检测

当响应式数据的 setter 被触发(即数据发生变化时),Vue 会通知所有依赖于该数据的组件,告诉它们数据已经改变。这个过程称为“变更通知”。

4. DOM 更新

一旦数据变更,依赖于这些数据的组件将会重新渲染。Vue 1.0 使用的是一种较简单的比较和重绘策略。它不会比较虚拟节点的差异,而是直接根据新的数据重新生成 HTML 字符串,并替换旧的 DOM 元素。这种方式在处理大量数据或频繁更新的情况下可能会导致性能问题。

优化

尽管没有使用虚拟 DOM,Vue 1.0 中的性能通常足够好,因为它通过细粒度的依赖追踪来限制只更新真正需要更新的部分。每个组件都独立更新其自己的 DOM,避免了不必要的 DOM 操作。

能看出来Vue1.X确实与后来大家普遍用的2.X很像了,Vue1.X的这些核心特性依然是 Vue 生态系统的基石。

Vue2.X

这次的大版本迭代其实做了不少对于1.x的优化,最值得一提的就是,虚拟DOM的加入,优化了页面渲染速度。对观察者也做了优化。具体如下:

1. 虚拟 DOM(Virtual DOM)

Vue.js 2.0 引入了虚拟 DOM 技术,这是其最显著的更新之一。虚拟 DOM 允许框架在内存中维护一个轻量级的 DOM 副本。通过比较新旧虚拟 DOM 的差异,框架可以计算出最小的必要 DOM 更新,从而减少实际的 DOM 操作次数。

性能提升:虚拟 DOM 减少了直接的 DOM 操作,尤其在复杂或大型界面中,性能提升显著。根据 Vue 开发团队的基准测试,Vue 2 使用虚拟 DOM 后,渲染性能比 Vue 1 快 2 到 5 倍。

2. 组件渲染和观察者机制优化

Vue.js 2.0 对组件的渲染机制和观察者机制也进行了优化。

观察者机制:Vue 2 使用基于类的观察者来跟踪依赖,这比 Vue 1 中使用的基于对象的依赖追踪更加内存效率。这一变更减少了内存的占用,并提高了依赖追踪的效率。

组件渲染优化:Vue 2.0 优化了条件渲染(如 v-if 和 v-for)的性能,使得这些常用指令的处理更加高效。

3. 服务器端渲染(SSR)

Vue.js 2.0 支持服务器端渲染,这对于需要优化搜索引擎优化(SEO)或提高首屏加载速度的应用特别有用。

性能提升:服务器端渲染可以显著提高首次内容的加载速度,因为用户的浏览器不需要等待所有的 JavaScript 都加载完毕即可看到完整的页面。

4. 更轻量

尽管增加了虚拟 DOM 等新功能,Vue.js 2.0 的运行时仍然保持相对轻量,gzip 后的大小仅为 ~22KB。

关于虚拟 DOM我会在源码解析中详聊。简单聊聊第二点中 观察者机制 是如何优化的:

Vue1.x,每个数据属性都通过一个闭包来创建自己的依赖列表,这导致了较高的内存使用,因为每个属性都要维护一个独立的依赖列表。

而在 Vue.js 2.0 中,引入了基于类的观察者模型。这种模型使用更为统一和标准化的方式来管理依赖,其中几个核心的改变包括:

1.统一的依赖管理

Vue.js 2.0 中,一个观察者类(Watcher 类)用于管理所有类型的响应式依赖。这意味着依赖管理变得更为一致和集中,减少了内存分配和垃圾回收的负担。

2. 依赖收集优化

在 Vue 2.0 中,依赖收集变得更加高效。由于使用了类来表示观察者和依赖关系,Vue 可以避免创建过多的闭包和临时对象,减少内存占用,并优化 JavaScript 引擎的优化路径。

3. 批量异步更新

Vue 2.0 引入了异步更新队列。当响应式数据变化时,不是立即更新视图,而是将更新推迟到下一个事件循环中。这样,如果多个数据变化几乎同时发生,Vue 可以将这些变化合并,从而减少实际的计算和 DOM 更新操作。

4.更细粒度的变更检测

通过使用虚拟 DOM 和基于类的观察者模型,Vue 2.0 可以更精确地确定哪些组件需要更新。这不仅减少了不必要的组件渲染,还使得每次更新更加高效。

5. v-model 的改进

在 Vue 2.x 中,v-model 指令得到了增强,支持在自定义组件上使用。这使得开发者可以更方便地在组件之间实现双向数据绑定。

6. 作用域插槽(Scoped Slots)

Vue 2.1 引入了作用域插槽,这是一种强大的组件插槽系统,允许父组件将模板结构传递给子组件,同时保留访问父组件数据的能力。这对于构建高度可复用的组件库非常有用。

在这些功能已经足够好用,也在那几年养成了我们编程舒适的舒适区,早些年是会Vue就很好找到工作,过了几年是只要会Vue就能找到工作。早些年互联网还算得上辉煌,加上市场对MVVM的热捧,前端的身价一下狂涨,Vue在市场上很快普及,基本是 React Angular Vue 三足鼎立态势,并以Vue为主。笔者最了解的框架也是Vue。

那为什么明明这么好的技术栈不好好的迭代,却要有这么大变化的更新,以至于在很长一段时间内,大型项目都不敢随意尝试Vue3.x,只在小型网页项目试水。那就聊聊Vue3.x吧。

Vue3.x

在尤雨溪眼中,Vue2.x并不是很理想,因为:

  1. 源码自身的维护性
  2. 数据量大后带来的渲染和更新性能问题
  3. 兼容性,未舍弃但又鸡肋的API
  4. 希望给开发人员带来更好的编程体验、更好的TypeScript支持、更好的逻辑复用。

通过对源码维护,性能,提供更好API,这几个角度来看,尤雨溪决定要推出一个大变更的Vue3.x,例如:

  1. 性能提升

Proxy 替代 Object.defineProperty:Vue3 使用 Proxy 实现响应式系统,相较于 Vue2 的 Object.defineProperty,更加高效且能够代理更多操作,包括新增和删除属性。

编译优化:Vue3 的编译器进行了优化,生成的代码更加高效,减少了运行时开销。比如使用了 Block Tree,将vnode更新性能由与模版整体大小相关提升为与动态内容相关,对模版更多静态分析:Template Compilation。

虚拟 DOM 优化:Vue3 加入了Patch Flags在编译阶段生成标记来指示哪些部分的虚拟 DOM 是动态的,改进了虚拟 DOM diff,Keyed Diff Algorithm ,提升了性能,特别是在大型应用中。

  1. Composition API

• 引入了 Composition API,提供了一种新的组织和复用代码的方式,使代码更加模块化和可复用。相比于 Options API,Composition API 更加灵活,适合处理复杂的逻辑和状态管理。

  1. 更好的 TypeScript 支持

• Vue3 原生支持 TypeScript,提供了更好的类型推断和类型检查,使得在使用 TypeScript 开发时更加顺畅和高效。

  1. Tree-shaking

• Vue3 更好地支持 Tree-shaking,打包时可以移除未使用的代码,减小打包后的文件体积,提高加载性能。

  1. 新特性

Fragments:支持组件返回多个根元素,简化了模板结构。

Teleport:允许将模板的一部分渲染到 DOM 树的不同位置,提供了更大的灵活性。

Suspense:处理异步组件加载的方案,使异步数据的处理和加载变得更加简洁。

  1. 优化的生命周期钩子

• 生命周期钩子的命名进行了调整,使得名称更加统一和直观,如 beforeCreate 和 created 被合并为 setup。

  1. 更小的打包体积

• Vue3 的核心库体积更小,使用了更优化的构建工具,减少了最终打包文件的大小。

  1. 改进的插件系统

• Vue3 的插件系统进行了改进,使得插件的注册和使用更加灵活和高效。

  1. 更好的模板编译

• 模板编译器进行了优化,支持更多的语法和特性,使得模板的编写更加灵活。

  1. 更强的生态系统支持

• Vue3 的生态系统得到了进一步完善,Vue Router、Vuex 等官方库都进行了相应的升级和优化,与 Vue3 完美兼容。

11.源码的优化

使用monorepoTypeScript管理和开发源码,目的是提高代码的可维护性。

可以看到Vue3.x是一个重构后的代码

从源码的管理方式:相对Vue2.x的源码组织方式,monorepo拆分了不同的package(如下图,也会在源码篇提到),这让模块依赖明确,更易懂,职责划分明确,开发者也可以单独引入某一个方法,也方便了Tree-shaking.

vue2代码框架.png

Vue2

vue3代码结构.jpg

Vue3

从响应式的优化:由object.defineProperty改成了Proxy,提升了性能(具体如何提升细节放在源码片中)。

提供了与Vue2完全不同的API:从Options APIComposition API,尤雨溪受到 React Hooks 的启发,让开发更加灵活,更加可复用

TypeScript 在Vue1.x中 并没有类型规范,只是JavaScript的编写,在Vue2.X时,使用的Flow来做类型规范,他是Facebook出品的JS静态类型检查工具,但对复杂场景类型的检查支持并不好,我在Vue2的源码的注释中有看到对他的吐槽如图。由于后面的烂尾,就转用TypeScript。

image.png

what the fuck

结束语:

以上就是从Vue1Vue3的一个变迁,Vue3中还调整了很多细节调整,这些可以分析源码时详聊.这篇文章主要提到的是大家熟知的知识,只是把这些知识根据Vue的发展,更系统的梳理了一番。在后续文章我会分多篇来介绍Vue2Vue3的源码。

我们只知道使用或源码原理是远远不够的,学习编程更多的是学习编程思维和不同场景如何使用合适的设计模式。我们借鉴大佬的代码,站在他们的肩膀上,让我们看的更远。


如果此文章对您有帮助或启发,那便是我的荣幸.