前言
vue是一个现代化的前端JavaScript框架,用于构建交互式、响应式的用户界面的mvvm框架,通过数据双向绑定,实现数据驱动视图更新。
Vue 2 已于 2023 年 12 月 31 日停止维护。详见 Vue 2 终止支持 (EOL).Vue.js 3(通常称为Vue 3)是Vue.js框架的第三个主要版本,。Vue 3在性能、开发体验和扩展性方面都有许多改进和新特性。那么本文章从原理,以及特性深入解析。
一、mvvm框架
- M:Model(模型)代表应用程序的数据和业务逻辑。它负责数据的获取、处理和存储,以及定义应用程序的行为。
- V:View(视图)代表应用程序的用户界面。它负责展示数据和与用户进行交互,通常由 HTML、XML 或其他类似的标记语言编写。
- VM:ViewModel(视图模型)它是 View 和 Model 之间的连接层。ViewModel 处理用户界面上的事件和输入,并将它们转化为对 Model 的操作。它还负责从 Model 中获取数据,并将数据绑定到 View 中,使得数据的变化能够自动更新到用户界面上。
二、响应式原理
1.Vue2.0x 的响应式是通过 Object.defineProperty 对数据进行劫持,并结合发布订阅者模式实现。 Vue 利用 Object.defineProperty 创建一个 observe 来劫持监听所有的属性,把这些属性(遍历data)全部转为 getter 和 setter。Vue 中每个组件实例都会对应一个 watcher 实例,它会在组件渲染的过程中把使用过的数据属性通过 getter 收集为依赖。之后当依赖项的 setter 触发时,会通知 watcher,从而使它关联的组件重新渲染。
2.vue3.0x的响应式是通过ES6种的Proxy代理,Proxy能够拦截目标对象上的操作(如获取、设置等),从而实现对数据的追踪和更新。与之前版本不同的是,Vue.js 3无需再手动编写getter和setter函数,只需要将原始数据包装成Proxy对象后就可以开始追踪和更新。Proxy 不仅可以代理对象,还可以代理数组。还可以代理动态增加的属性。
Object.defineProperty():用来给一个对象定义一个属性或者修改一个现有的属性,并且返回这个对象
//语法
Object.defineProperty(obj,props,descripter)
const obj = {};
Object.defineProperty(obj, 'name', {
// configurable: false,//默认为false
// enumerable: false,//默认为false
value: 'kongzhi',
// writable: false,/默认为false
// get(){},
// set(v){}
});
console.log(obj.name); // 输出 kongzhi
// 改写obj.name 的值
obj.name = 111;
console.log(obj.name); // 还是打印出 kongzhi,因为writable默认为false
Vue2.0 对于数据响应式的实现上是有一些局限性的,比如:
- 无法检测数组和对象的新增
- 无法检测通过索引改变数组的操作
Object.defineProperty 在数组中的表现和在对象中的表现是一致的,Object.defineProperty 是有监控数组下标变化的能力的,只是在 vue2.x 放弃了这个特性,因为性能问题。
Vue 对数组的7个操作方法(push、pop、shift、unshift、splice、sort、reverse)也实现了响应式。
proxy: 理解为在target对象前设置一层拦截,每次对target访问都得通过这层拦截,就可以对target进行一些改写
//语法
const p = new Proxy(target,handler)
const obj = {
name: '伯约同学',
age: 18
}
const objProxy = new Proxy(obj, {
// 获取值时的捕获器
get(target, key) {
console.log(`监听到对象的${key}属性被访问了`, target)
return target[key]
},
// 设置值时的捕获器
set(target, key, newValue) {
console.log(`监听到对象的${key}属性被设置了`, target)
target[key] = newValue
},
// 监听 in 捕获器
has(target, key) {
console.log(`监听到对象的in操作符`, target)
return key in target
},
// 监听 delete 捕获器
deleteProperty(target, key) {
console.log(`监听到对象的delete操作符`, target)
delete target[key]
},
// 获取对象原型捕获器
getPrototypeOf(target) { xxx },
// 设置对象原型捕获器
setPrototypeOf(target, prototype) { xxx },
// 是否可以扩展捕获器
isExtensible() { xxx },
// 阻止扩展捕获器
preventExtensions() { xxx },
// 获取自己的属性描述符捕获器
getOwnPropertyDescriptor() { xxx },
// 定义属性捕获器
defineProperty() { xxx },
// 监听属性名和 symbol捕获器
ownKeys() { xxx },
// 函数调用操作的捕获器,用于函数对象
apply() { xxx },
// new 操作符捕获器,用于函数对象
construct() { xxx }
})
二、主要区别
(1)2.x和3.x 生命周期区别
tip:setup 是围绕 beforeCreate 和 created 生命周期钩子运行的,所以不需要显式地去定义
(2)多根节点 ,Vue3 支持多个根节点,也就是 fragment
// vue2只能存在一个根节点,需要用一个<div>来包裹着
<template>
<div>
<header></header>
<main></main>
<footer></footer>
</div>
</template>
// vue3只能存在一个根节点,需要用一个<div>来包裹着
<template>
<header></header>
<main></main>
<footer></footer>
</template>
(3)Composition API
Vue2 是选项API(Options API),一个逻辑会散乱在文件不同位置(data、props、computed、watch、生命周期钩子等),导致代码的可读性变差。当需要修改某个逻辑时,需要上下来回跳转文件位置。
Vue3 组合式API(Composition API)则很好地解决了这个问题,可将同一逻辑的内容写到一起,增强了代码的可读性、内聚性,其还提供了较为完美的逻辑复用性方案。
(4)异步组件(Suspense)
Vue3 提供 Suspense 组件,允许程序在等待异步组件加载完成前渲染兜底的内容,如 loading ,使用户的体验更平滑。使用它,需在模板中声明,并包括两个命名插槽:default 和 fallback。Suspense 确保加载完异步内容时显示默认插槽,并将 fallback 插槽用作加载状态。
<tempalte>
<suspense>
<template #default>
<List />
</template>
<template #fallback>
<div>
Loading... </div>
</template>
</suspense>
</template>
(5)Teleport
Vue3 提供 Teleport 组件可将部分 DOM 移动到 Vue app 之外的位置。比如项目中常见的 Dialog 弹窗
<button @click="dialogVisible = true">显示弹窗</button>
<teleport to="body">
<div class="dialog" v-if="dialogVisible">
我是弹窗,我直接移动到了body标签下 </div>
</teleport>
(5)虚拟dom
虽然vue能够保证进行最小化的更新,节约浏览器的渲染性能,但却新增了新旧Vdom对比的性能消耗。因此, vdom的大小就直接影响了对比的快慢,而vdom中有一部分是静态标签,不会改变,但在对比时,仍会进行遍历,因此就会增加性能的消耗。 所以在vue3.x中新增了静态标记,虚拟DOM上增加 patchFlag 字段。
1 代表节点为动态文本节点,那在 diff 过程中,只需比对文本对容,无需关注 class、style等。除此之外,发现所有的静态节点(HOISTED 为 -1),都保存为一个变量进行静态提升,可在重新渲染时直接引用,无需重新创建。
(5)事件缓存
Vue3 的cacheHandler可在第一次渲染后缓存我们的事件。相比于 Vue2 无需每次渲染都传递一个新函数。加一个 click 事件。
<div id="app">
<h1>vue3事件缓存讲解</h1>
<p>今天天气真不错</p>
<div>{{name}}</div>
<span onCLick=() => {}><span>
</div>
(5)打包优化
Tree shaking 是一种通过清除多余代码方式来优化项目打包体积的技术,专业术语叫 Dead code elimination;
在Vue2中,无论我们使用什么功能,它们最终都会出现在生产代码中。主要原因是Vue实例在项目中是单例的,捆绑程序无法检测到该对象的哪些属性在代码中被使用到
import Vue from 'vue'
Vue.nextTick(() => {})
而Vue3源码引入tree shaking特性,将全局 API 进行分块。如果您不使用其某些功能,它们将不会包含在您的基础包中
import { nextTick, observable } from 'vue'
nextTick(() => {})
通过Tree shaking,Vue3给我们带来的好处是:
- 减少程序体积(更小)
- 减少程序执行时间(更快)
- 便于将来对程序架构进行优化(更友好)