1) 性能提升
- 打包大小减少 41%
- 初次渲染快 55%,更新渲染快 133%
- 内存减少 54%
- 使用 Proxy 代替 defineProperty 实现数据响应式
- 重写虚拟 DOM 的实现和 Tree-Shaking
2) 新增特性
- Composition (组合) API
- setup
- ref 和 reactive
- computed 和 watch
- 新的生命周期函数
- provide 与 inject
- 新组件
- Fragment - 文档碎片
- Teleport - 瞬移组件的位置
- Suspense - 异步加载组件的 loading 界面
- 其它 API 更新
- 全局 API 的修改
- 将原来的全局 API 转移到应用对象
- 模板语法变化
详解
1) setup
- 新的 option,所有的组合 API 函数都在此使用,只在初始化时执行一次
- 函数如果返回对象,对象中的属性或方法,模板中可以直接使用
2) ref
- 作用:定义一个数据的响应式
- 语法:const xxx = ref(initValue):
- 创建一个包含响应式数据的引用(reference)对象
- js 中操作数据:xxx.value
- 模板中操作数据:不需要.value
- 一般用来定义一个基本类型的响应式数据
3) reactive
- 作用: 定义多个数据的响应式
- const proxy = reactive(obj): 接收一个普通对象然后返回该普通对象的响应式代理器对象
- 响应式转换是“深层的”:会影响对象内部所有嵌套的属性
- 内部基于 ES6 的 Proxy 实现,通过代理对象操作源对象内部数据都是响应式的
4) 比较 Vue2 与 Vue3 的响应式(重要)
vue2 的响应式
- 核心:
- 对象:通过 defineProperty 对对象的已有属性值的读取和修改进行劫持(监视/拦截)
- 数组:通过重写数组更新数组一系列更新元素的方法来实现元素修改的劫持
- 问题:
- 对象直接新添加的属性或删除已有属性,界面不会自动更新
- 直接通过下标替换元素或更新 length,界面不会自动更新 arr[1] = {}
Vue3 的响应式
- 核心:
- 通过 Proxy(代理):拦截对 data 任意属性的任意(13 种)操作,包括属性值的读写,属性的添加,属性的删除等...
- 通过 Reflect(反射):动态对被代理对象的相应属性进行特定的操作
- 文档:
- https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Proxy(opens new window)
- https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Reflect
5) setup 细节
- setup 执行的时机
- 在 beforeCreate 之前执行(一次),此时组件对象还没有创建
- this 是 undefined,不能通过 this 来访问 data/computed/methods / props
- 其实所有的 composition API 相关回调函数中也都不可以
- setup 的返回值
- 一般都返回一个对象:为模板提供数据,也就是模板中可以直接使用此对象中的所有属性/方法
- 返回对象中的属性会与 data 函数返回对象的属性合并成为组件对象的属性
- 返回对象中的方法会与 methods 中的方法合并成功组件对象的方法
- 如果有重名,setup 优先
- 注意:
- 一般不要混合使用:methods 中可以访问 setup 提供的属性和方法,但在 setup 方法中不能访问 data 和 methods
- setup 不能是一个 async 函数:因为返回值不再是 return 的对象,而是 promise,模板看不到 return 对象中的属性数据
- setup 的参数
- setup(props,context) / setup(props,{attrs,slots,emit})
- props:包含 props 配置声明且传入了的所有属性的对象
- attrs:包含没有在 props 配置中声明的属性的对象,相当于 this.$attrs
- slots:包含所有传入的插槽内容的对象,相当于 this.$slots
- emit:用来分发自定义事件的函数,相当于 this.$emit
6) reactive 与 ref-细节
- 是 Vue3 的 composition API 中 2 个最重要的响应式 API
- ref 用来处理基本类型数据,reactive 用来处理对象(递归深度响应式)
- 如果用 ref 对象/数组,内部会自动将对象/数组转换为 reactive 的代理对象
- ref 内部:通过给 value 属性添加 getter/setter 来实现对数据的劫持
- reactive 内部:通过使用 Proxy 来实现对对象内部所有数据的劫持,并通过 Reflect 操作对象内部数据
- ref 的数据操作:在 js 中要.value,在模板中不需要(内部解析模板时会自动添加.value)
7) 计算属性与监视
- computed 函数:
- 与 computed 配置功能一致
- 只有 getter
- 有 getter 和 setter
- watch 函数
- 与 watch 配置功能一致
- 监视指定的一个或多个响应式数据,一旦数据变化,就自动执行监视回调
- 默认初始时不执行回调,但可以通过配置 immediate 为 true,来指定初始时立即执行第一次
- 通过配置 deep 为 true,来指定深度监视
- watchEffect 函数
- 不用直接指定要监视的数据,回调函数中使用的哪些响应式数据就监视哪些响应式数据
- 默认初始时就会执行第一次,从而可以收集需要监视的数据
- 监视数据发生变化时回调
8) 生命周期
与 2.x 版本生命周期相对应的组合式 API
- setup()
- setup()
- onBeforeMount
- onMounted
- onBeforeUpdate
- onUpdated
- onBeforeUnmount
- onUnmounted
- onErrorCaptured
新增的钩子函数
组合式 API 还提供了以下调试钩子函数:
- onRenderTracked
- onRenderTriggered
09) 自定义 hook 函数
- 使用 Vue3 的组合 API 封装的可复用的功能函数
- 自定义 hook 的作用类似于 vue2 中的 mixin 技术
- 自定义 Hook 的优势:很清楚复用功能代码的来源,更清楚易懂
- 需求 1:收集用户鼠标点击的页面坐标
1 import { ref, onMounted, onUnmounted } from 'vue'
2 /*
3 收集用户鼠标点击的页面坐标
4 */
5 export const useMousePosition=()=> {
6 // 初始化坐标数据
7 const x = ref(-1)
8 const y = ref(-1)
9
10 // 用于收集点击事件坐标的函数
11 const updatePosition = (e: MouseEvent) => {
12 x.value = e.pageX
13 y.value = e.pageY
14 }
15
16 // 挂载后绑定点击监听
17 onMounted(() => {
18 document.addEventListener('click', updatePosition)
19 })
20
21 // 卸载前解绑点击监听
22 onUnmounted(() => {
23 document.removeEventListener('click', updatePosition)
24 })
25
26 return { x, y }
27 }
28
29 <template>
30 <div>
31 <h2>x: {{ x }}, y: {{ y }}</h2>
32 </div>
33 </template>
34
35 <script>
36 import { ref,setup } from 'vue'
37 /* 在组件中引入并使用自定义hook
38 自定义hook的作用类似于vue2中的mixin技术
39 自定义Hook的优势:很清楚复用功能代码的来源, 更清楚易懂*/
40
41 import useMousePosition from './hooks/useMousePosition'
42
43 export default {
44 const { x, y } = useMousePosition()
45 return {
46 x,
47 y
48 }
49 }
50 </script>