一、前言
大家都知道,现在Vue3的各个版本已经陆续发布了,并且有很多的团队已经着手各个库的开发与Vue2向Vue3的升级,我们当然也不能落后,所以赶紧将你手中的Vue2升级到Vue3,跟着本文一起学习新的API吧
升级的方法可以点击本文开头的文章,在上一篇文章中有个保姆级别的教程告诉大家如何升级
二、正文
Vue2每次都把整个Vue导入,例如Vue2的 main.js
文件中的代码
import Vue from 'vue' import App from './App.vue' Vue.config.productionTip = false new Vue({ render: h => h(App) }).$mount('#app')
但很明显我们的项目中不可能用到Vue所有的API,因此很多模块其实是没有用的
那么在Vue3中,对外暴露了很多的API供开发者使用,我们可以根据自己的需求,将所需要的API从Vue中导入。例如 main.js
中的代码
import { createApp } from 'vue'; import App from './App.vue' createApp(App).mount('#app')
利用了 import
和 export
的导入导出语法,实现了按需打包模块的功能,项目打包后的文件体积明显小了很多
这也是我们本文需要对 Vue3 API
进行详细了解的原因
(1)setup
setup
函数也是 Composition API
的入口函数,我们的变量、方法都是在该函数里定义的,来看一下使用方法
<template> <div id="app"> <p>{{ number }}</p> <button @click="add">增加</button> </div> </template> <script> // 1. 从 vue 中引入 ref 函数 import {ref} from 'vue' export default { name: 'App', setup() { // 2. 用 ref 函数包装一个响应式变量 number let number = ref(0) // 3. 设定一个方法 function add() { // number是被ref函数包装过了的,其值保存在.value中 number.value ++ } // 4. 将 number 和 add 返回出去,供template中使用 return {number, add} } } </script>
上述代码中用到了 ref
函数,下面会详细讲解,在这里你只需要理解它的作用是包装一个响应式的数据即可,并且你可以将 ref
函数包装过的变量看作是Vue2 data
中的变量
这样就简单实现了一个点击按钮数字加1的功能
在Vue2中,我们访问 data
或 props
中的变量,都是通过类似 this.number
这样的形式去获取的,但要特别注意的是,在setup中,this
指向的是 undefined
,也就是说不能再向Vue2一样通过 this
去获取变量了
那么到底该如何获取到 props
中的数据呢?
其实 setup
函数还有两个参数,分别是 props
、context
,前者存储着定义当前组件允许外界传递过来的参数名称以及对应的值;后者是一个上下文对象,能从中访问到 attr
、emit
、slots
其中 emit
就是我们熟悉的Vue2中与父组件通信的方法,可以直接拿来调用
(2)生命周期
Vue2中有 beforeCreate
、created
、beforeMount
、mounted
、beforeUpdate
等生命周期函数
而在Vue3中,这些生命周期部分有所变化,并且调用的方式也有所改变,下面放上一张变化图来简单了解一下
Vue2 | Vue3 |
beforeCreate | setup |
created | setup |
beforeMount | onBeforeMount |
mounted | onMounted |
beforeUpdate | onBeforeUpdate |
updated | onUpdated |
beforeDestory | onBeforeUnmount |
destoryed | onUnmounted |
Vue3的这些生命周期调用也很简单,同样是先从 vue
中导入,再进行直接调用
<template> <div id="app"></div> </template> <script> // 1. 从 vue 中引入 多个生命周期函数 import {onBeforeMount, onMounted, onBeforeUpdate, onUpdated, onBeforeUnmount, unMounted} from 'vue' export default { name: 'App', setup() { onBeforeMount(() => { // 在挂载前执行某些代码 }) onMounted(() => { // 在挂载后执行某些代码 }) onBeforeUpdate(() => { // 在更新前前执行某些代码 }) onUpdated(() => { // 在更新后执行某些代码 }) onBeforeUnmount(() => { // 在组件销毁前执行某些代码 }) unMounted(() => { // 在组件销毁后执行某些代码 }) return {} } } </script>
要特别说明一下的就是,setup
函数代替了 beforeCreate
和 created
两个生命周期函数,因此我们可以认为它的执行时间在beforeCreate
和 created
之间
(3)reactive
reactive
方法是用来创建一个响应式的数据对象,该API也很好地解决了Vue2通过 defineProperty
实现数据响应式的缺陷
用法很简单,只需将数据作为参数传入即可,代码如下
<template> <div id="app"> <!-- 4. 访问响应式数据对象中的 count --> {{ state.count }} </div> </template> <script> // 1. 从 vue 中导入 reactive import {reactive} from 'vue' export default { name: 'App', setup() { // 2. 创建响应式的数据对象 const state = reactive({count: 3}) // 3. 将响应式数据对象state return 出去,供template使用 return {state} } } </script>
(4)ref
在介绍 setup
函数时,我们使用了 ref
函数包装了一个响应式的数据对象,这里表面上看上去跟 reactive
好像功能一模一样啊,确实差不多,因为 ref
就是通过 reactive
包装了一个对象 ,然后是将值传给该对象中的 value
属性,这也就解释了为什么每次访问时我们都需要加上 .value
我们可以简单地把 ref(obj)
理解为这个样子 reactive({value: obj})
这里我们写一段代码来具体看一下
<script> import {ref, reactive} from 'vue' export default { name: 'App', setup() { const obj = {count: 3} const state1 = ref(obj) const state2 = reactive(obj) console.log(state1) console.log(state2) } } </script>
来看一下打印结果
注意: 这里指的
.value
是在setup
函数中访问ref
包装后的对象时才需要加的,在template
模板中访问时是不需要的,因为在编译时,会自动识别其是否为ref
包装过的
那么我们到底该如何选择 ref
和 reactive
呢?
建议:
- 基本类型值(
String
、Nmuber
、Boolean
等)或单值对象(类似像{count: 3}
这样只有一个属性值的对象)使用ref
- 引用类型值(
Object
、Array
)使用reactive
(5)toRef
toRef
是将某个对象中的某个值转化为响应式数据,其接收两个参数,第一个参数为 obj
对象;第二个参数为对象中的属性名
代码如下:
<script> // 1. 导入 toRef import {toRef} from 'vue' export default { setup() { const obj = {count: 3} // 2. 将 obj 对象中属性count的值转化为响应式数据 const state = toRef(obj, 'count') // 3. 将toRef包装过的数据对象返回供template使用 return {state} } } </script>
但其实表面上看上去 toRef
这个API好像非常的没用,因为这个功能也可以用 ref
实现,代码如下
<script> // 1. 导入 ref import {ref} from 'vue' export default { setup() { const obj = {count: 3} // 2. 将 obj 对象中属性count的值转化为响应式数据 const state = ref(obj.count) // 3. 将ref包装过的数据对象返回供template使用 return {state} } } </script>
乍一看好像还真是,其实这两者是有区别的,我们可以通过一个案例来比较一下,代码如下
<template> <p>{{ state1 }}</p> <button @click="add1">增加</button> <p>{{ state2 }}</p> <button @click="add2">增加</button> </template> <script> import {ref, toRef} from 'vue' export default { setup() { const obj = {count: 3} const state1 = ref(obj.count) const state2 = toRef(obj, 'count') function add1() { state1.value ++ console.log('原始值:', obj); console.log('响应式数据对象:', state1); } function add2() { state2.value ++ console.log('原始值:', obj); console.log('响应式数据对象:', state2); } return {state1, state2, add1, add2} } } </script>
我们分别用 ref
和 toRef
将 obj
中的 count
转化为响应式,并声明了两个方法分别使 count
值增加,每次增加后打印一下原始值 obj
和被包装过的响应式数据对象,同时还要看看视图的变化
ref:
可以看到,在对响应式数据的值进行 +1
操作后,视图改变了,原始值未改变,响应式数据对象的值也改变了,这说明 ref
是对原数据的一个拷贝,不会影响到原始值,同时响应式数据对象值改变后会同步更新视图
toRef:
可以看到,在对响应式数据的值进行 +1
操作后,视图未发生改变,原始值改变了,响应式数据对象的值也改变了,这说明 toRef
是对原数据的一个引用,会影响到原始值,但是响应式数据对象值改变后会不会更新视图
总结:
ref
是对传入数据的拷贝;toRef
是对传入数据的引用
ref
的值改变会更新视图;toRef
的值改变不会更新视图