vue3 取经

目录

  • 前言
  • 一、响应式系统
    • 1、vue2 的基于 Object.defineProperty() 的响应式
    • 2、vue3 的基于 ES6 的 Proxy 的响应式
      • (1)、Proxy 对象
      • (2)、Reflect 对象
      • (3)、proxy 代理的原理与优势
  • 二、vue 实例
    • 1、创建一个 vue 实例
    • 2、将 Vue 实例挂载到根组件上
    • 3、vue 实例的 生命周期 及其在 setup 中的映射
  • 三、Composition API 组合语法
    • 1、Vue 的 Options API 与 Composition API 对比
      • (1)、Options API 的写法也有几个很严重的问题
      • (2)、使用 Composition API 带来的好处
    • 2、使用 Composition API
      • (1)、在 setup 里使用 vue3 的生命周期
      • (2)、setup 函数、defineComponent 函数 和 script 标签上的 setup
      • (3)、ref、toRef、toRefs 和 reactive 的区别
      • (4)、setup 里的计算属性(computed)和侦听器(watch)
      • (5)、基于 setup 的组件通信
  • 四、新的组件
    • 1、fragment 片断组件(了解)
    • 2、teleport 瞬移组件
    • 3、suspense 组件
  • 五、Vite 工程化工具
  • 六、自定义渲染器
  • 七、采用 TypeScript 重构
  • 八、RFC 机制(了解)
  • 九、vue3 踩坑记

前言

vue 官网提供的:vue2 与 vue3 的对比。

vue 2 常见的缺陷:

  • vue 2 的响应式并不是真正意义上的代理,而是基于 Object.defineProperty() 实现的。这个 API 并不是代理,而是对某个属性进行拦截,所以有很多缺陷,比如:删除数据就无法监听,需要 $delete 等 API 辅助才能监听到。
  • vue 2 是使用 Flow.js 来做类型校验,但现在 Flow.js 已经停止维护了,整个社区都在全面拥抱 TypeScript 来构建基础库。
  • Vue 2 内部所有的模块都是揉在一起的,这样做会导致不好扩展的问题。

Vue 3 的新特性概览:

  • 响应式系统
  • Composition API 组合语法
  • 新的组件:
    • Fragment
    • Teleport
    • Suspense
  • Vite
  • 自定义渲染器
  • Vue 的全部模块使用 TypeScript 重构。
  • RFC 机制(了解)

一、响应式系统

Vue 2 的响应式机制是基于 Object.defineProperty() 实现的,而 vue 3 采用了 Proxy,这两者的区别一言以蔽之——defineProperty 是拦截具体某个属性,Proxy 才是真正的“代理”

1、vue2 的基于 Object.defineProperty() 的响应式

关于 Object.defineProperty() 详见这里

语法:

Object.defineProperty(data, 'count', {
  // 读取
  get () {},
  // 修改
  set () {}
})

原理:

  • 对象:对对象的已有属性进行劫持(拦截/监听)。
  • 数组:vue2 内部通过重写 “更新数组的一系列方法” 来实现对数组元素的劫持。

问题:为什么我数据都改变了,但是页面没有重新渲染呢?()

  • 对象:在对象内新添加属性 或 删除已有的属性,页面不会自动更新。
  • 数组:直接通过下标替换元素或者修改数组的 length 值,页面不会自动更新。

vue2 给出的解决方案:为了解决 defineProperty 带来的问题,vue2 中增加 $set 方法。

2、vue3 的基于 ES6 的 Proxy 的响应式

ES6 的 Proxy 和 Reflect

proxy 代理需要用到两个对象:

  • Proxy(代理)对象:用于创建一个对象的代理。
  • Reflect(反映)对象:提供拦截 JS 操作的方法。

语法:

new Proxy(data, {
  // 读取
  get (target, prop) {
	return Reflect.get(target, prop)
  },
  // 添加、修改
  set (target, prop, val) {
	return Reflect.set(target, prop, val)
  },
  // 删除
  deleteProperty (target, prop) {
	return Reflect.deleteProperty(target, prop)
  }
  // ...
})

(1)、Proxy 对象

Proxy 对象是一个构造函数,可以通过 new 关键字来创建一个代理对象的实例。

语法:

const p = new Proxy(target, handler)

参数:

  • target:要使用 Proxy 包装的目标对象(可以是任何类型的对象,包括原生数组,函数,甚至另一个代理)。
  • handler:一个通常以函数作为属性的对象(换言之,Proxy 对象的 handler 对象的成员是一些函数),各属性中的函数分别定义了在执行各种操作时代理 p 的行为。它包含有 Proxy 的各个捕获器(trap)。所有的捕捉器是可选的。如果没有定义某个捕捉器,那么就会保留源对象的默认行为。

方法:

  • Proxy 的静态方法:Proxy.revocable() ——创建一个可撤销的 Proxy 对象。
  • handler 的方法(见下表)
方法 描述
handler.getPrototypeOf() Object.getPrototypeOf 方法的捕捉器
handler.setPrototypeOf() Object.setPrototypeOf 方法的捕捉器
handler.isExtensible() Object.isExtensible 方法的捕捉器
handler.preventExtensions() Object.preventExtensions 方法的捕捉器
handler.getOwnPropertyDescriptor() Object.getOwnPropertyDescriptor 方法的捕捉器
handler.defineProperty() Object.defineProperty 方法的捕捉器
handler.has() in 操作符的捕捉器
handler.get() 属性读取操作的捕捉器
handler.set() 属性设置操作的捕捉器
handler.deleteProperty() delete 操作符的捕捉器
handler.ownKeys() Object.getOwnPropertyNames 方法和 Object.getOwnPropertySymbols 方法的捕捉器
handler.apply() 函数调用操作的捕捉器
handler.construct() new 操作符的捕捉器

(2)、Reflect 对象

Reflect 是一个内置的对象,它提供拦截 JavaScript 操作的方法。这些方法与 Proxy handler 的方法相同。

Reflect 不是一个构造函数,所以不能通过 new 运算符调用,也不能将 Reflect 对象作为一个函数来调用。Reflect 的所有属性和方法都是静态的。调用静态属性和方法必须使用本身类名来调用。

Reflect 对象的静态方法:

方法 描述
Reflect.apply(target, thisArgument, argumentsList) 对一个函数进行调用操作,同时可以传入一个数组作为调用参数。和 Function.prototype.apply() 功能类似
Reflect.construct(target, argumentsList[, newTarget]) 对构造函数进行 new 操作,相当于执行:new target(...args)
Reflect.defineProperty(target, propertyKey, attributes) 和 Object.defineProperty() 类似。如果设置成功就会返回 true
Reflect.deleteProperty(target, propertyKey) 作为函数的 delete 操作符,相当于执行:delete target[name]
Reflect.get(target, propertyKey[, receiver]) 获取对象身上某个属性的值,类似于:target[name]
Reflect.getOwnPropertyDescriptor(target, propertyKey) 类似于 Object.getOwnPropertyDescriptor()。如果对象中存在该属性,则返回对应的属性描述符,否则返回 undefined
Reflect.getPrototypeOf(target) 类似于 Object.getPrototypeOf()
Reflect.has(target, propertyKey) 判断一个对象是否存在某个属性,和 in 运算符 的功能完全相同
Reflect.isExtensible(target) 类似于 Object.isExtensible()
Reflect.ownKeys(target) 返回一个包含所有自身属性(不包含继承属性)的数组。(类似于 Object.keys(), 但不会受 enumerable 影响)
Reflect.preventExtensions(target) 类似于 Object.preventExtensions()。返回一个 Boolean
Reflect.set(target, propertyKey, value[, receiver]) 将值分配给属性的函数。返回一个 Boolean,如果更新成功,则返回 true
Reflect.setPrototypeOf(target, prototype) 设置对象原型的函数。返回一个 Boolean, 如果更新成功,则返回 true

【注意】其中的一些方法与 Object 相同,尽管二者之间存在某些细微上的差别

(3)、proxy 代理的原理与优势

1⃣️、原理

  • 通过 proxy 拦截对 data 任意属性的任意操作,包括:属性的增删,以及属性值的读写。
  • 通过 Reflect 动态对被代理的对象的相应的属性进行特定的操作。

2⃣️、优势

可以监听更多的数据格式,比如: SetMap。这是 Vue 2 做不到的。


二、vue 实例

1、创建一个 vue 实例

在 vue3 中,通过 createApp 函数可以创建一个 vue 实例。

const app = Vue.createApp({
  /* 选项 */
})

2、将 Vue 实例挂载到根组件上

假设你的根组件是

,你应该这样:

const RootComponent = {}
// 创建一个 vue 实例
const app = Vue.createApp(RootComponent)
// 将 vue 实例挂载到根组件上
const vm = app.mount('#app')

这等价于 vue2 中的这种写法:

const vue = new Vue({
  el: '#app',
  render: h => h(App)
})

【注意】在 vue2 中将 h 作为 createElement 函数的别名。

3、vue 实例的 生命周期 及其在 setup 中的映射

  • 在 vue3 中可以使用 vue3 规定的 Options-based API 生命周期钩子函数,但更推荐使用 Composition API,使用两者对应的 API 时二选一即可。
  • Composition API 里的生命周期回调 总是比 Options-based API 的生命周期钩子函数 更快一步执行。
vue2、vue3 实例的生命周期及其在 setup 中的对照表
vue2 的生命周期(Options-based API) vue3 的生命周期(Options-based API) vue3 的生命周期(Composition API)
beforeCreate 在实例初始化之后,进行数据侦听和事件/侦听器的配置之前同步调用。 beforeCreate 同 vue2。 setup() 描述
created 在实例创建完成后被立即同步调用。在这一步中,实例已完成对选项的处理,意味着以下内容已被配置完毕:数据侦听、计算属性、方法、事件/侦听器的回调函数。然而,挂载阶段还没开始,且 $el property 目前尚不可用。 created 同 vue2。
beforeMount 在挂载开始之前被调用:相关的 render 函数首次被调用。该钩子在服务器端渲染期间不被调用。 beforeMount 同 vue2。 onBeforeMount 描述
mounted 实例被挂载后调用,这时 el 被新创建的 vm.$el 替换了。如果根实例挂载到了一个文档内的元素上,当 mounted 被调用时 vm.$el 也在文档内。注意 mounted 不会保证所有的子组件也都被挂载完成。如果你希望等到整个视图都渲染完毕再执行某些操作,可以在 mounted 内部使用 vm.$nextTick。该钩子在服务器端渲染期间不被调用。 mounted 同 vue2。 onMounted 描述
beforeUpdate 在数据发生改变后,DOM 被更新之前被调用。这里适合在现有 DOM 将要被更新之前访问它,比如移除手动添加的事件监听器。该钩子在服务器端渲染期间不被调用,因为只有初次渲染会在服务器端进行。 beforeUpdate 同 vue2。 onBeforeUpdate 描述
updated 在数据更改导致的虚拟 DOM 重新渲染和更新完毕之后被调用。当这个钩子被调用时,组件 DOM 已经更新,所以你现在可以执行依赖于 DOM 的操作。然而在大多数情况下,你应该避免在此期间更改状态。如果要相应状态改变,通常最好使用计算属性或侦听器取而代之。注意,updated 不会保证所有的子组件也都被重新渲染完毕。如果你希望等待整个视图都渲染完毕,可以在 updated 内部使用 vm.$nextTick。该钩子在服务器端渲染期间不被调用。 updated 同 vue2。 onUpdated 描述
activated 被 keep-alive 缓存的组件“激活”时调用。该钩子在服务器端渲染期间不被调用。 activated 同 vue2。 - -
deactivated 被 keep-alive 缓存的组件“失活”时调用。该钩子在服务器端渲染期间不被调用。 deactivated 同 vue2。 - -
beforeDestroy 实例销毁之前调用。在这一步,实例仍然完全可用。该钩子在服务器端渲染期间不被调用。 beforeUnmount 1. 在 vue3 的项目中 beforeDestroy 改名为 beforeUnmount,所以不能用 beforeDestroy 了。

2. 在卸载组件实例之前调用。在这个阶段,实例仍然是完全正常的。该钩子在服务器端渲染期间不被调用。
onBeforeUnmount 描述
destroyed 实例销毁后调用。该钩子被调用后,对应 Vue 实例的所有指令都被解绑,所有的事件监听器被移除,所有的子实例也都被销毁。该钩子在服务器端渲染期间不被调用。 unmounted 1. 在 vue3 的项目中 destroyed 改名为 unmounted,所以不能用 destroyed 了。

2. 卸载组件实例后调用。调用此钩子时,组件实例的所有指令都被解除绑定,所有事件侦听器都被移除,所有子组件实例被卸载。该钩子在服务器端渲染期间不被调用。
onUnmounted 描述
errorCaptured 在捕获一个来自后代组件的错误时被调用。此钩子会收到三个参数:错误对象、发生错误的组件实例以及一个包含错误来源信息的字符串。此钩子可以返回 false 以阻止该错误继续向上传播。 errorCaptured 同 vue2。 onErrorCaptured 描述
- - renderTracked 跟踪虚拟 DOM 重新渲染时调用。钩子接收 debugger event 作为参数。此事件告诉你哪个操作跟踪了组件以及该操作的目标对象和键。 onRenderTracked Tracked 读作:[trækt],跟踪。
- - renderTriggered 当虚拟 DOM 重新渲染被触发时调用。和 renderTracked 类似,接收 debugger event 作为参数。此事件告诉你是什么操作触发了重新渲染,以及该操作的目标对象和键。 onRenderTriggered Triggered 读作:[ˈtrɪɡəd],触发。

vue2 的生命周期图(左/上)和 vue3 的生命周期图(右/下)——摘自 Vue 官网:
vue3 取经_第1张图片vue3 取经_第2张图片


三、Composition API 组合语法

vue3 响应性基础 API
vue3 组合式 API

你可能感兴趣的:(#,Vue.js,vue3)