平时做技术实践时,很多问题不是概念不会,而是细节没串起来。拿“vue3常用的API采用简介”来说,它看着像小点,放到项目里常会牵出环境、配置、兼容性和维护成本。下面按实际采用顺序,把思路、关键写法和容易踩坑的地方讲清楚,便于大家直接对照操作。
结合项目来看,vue3.x已经发布了这么久,相关的生态也慢慢起来了,包括vite这个新的打包工具,在vue3.0学习过程中有一些实用性的api对比,希望能在开发中给大家做个示范,准确的采用对应的api去完成我们的项目开发
生命周期的变更
结合项目来看,要特别说明一下的就是,setup 函数代替了 beforeCreate 和 created 两个生命周期函数,所以我们能够认为它的执行时间在beforeCreate 和 created 之间
| Vue2 | Vue3 |
|---|---|
| beforeCreate | setup |
| created | setup |
| beforeMount | onBeforeMount |
| mounted | onMounted |
| beforeUpdate | onBeforeUpdate |
| updated | onUpdated |
| beforeDestory | onBeforeUnmount |
| destoryed | onUnmounted |
从实现思路看,了解过vue3的小伙伴儿都知道,现在采用都会用到setup函数,关于在setup函数操作数据,我们用例子说明会好一点
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>
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
落到代码里,我们在vue2.x中拿到元素标签是用 ref,vue3.x我们要拿到元素标签怎么办呢?
<template>
<div>
<div ref="el">div元素</div>
</div>
</template>
<script>
import { ref, onMounted } from 'vue'
export default {
setup() {
// 创建一个DOM引用,名称必须与元素的ref属性名相同
const el = ref(null)
// 在挂载后才能通过 el 获取到目标元素
onMounted(() => {
el.value.innerHTML = '内容被修改'
})
// 把创建的引用 return 出去
return {el}
}
}
</script>
拿到元素的操作一共分为以下几个步骤:
- 先给目标元素的 ref 属性设置一个值,假设为 el
- 结合项目来看,然后在 setup 函数中调用 ref 函数,值为 null,同时赋值给变量 el,这里需留意,该变量名必须与我们给元素设置的 ref 属性名相同
- 把对元素的引用变量 el 得到(return)出去
补充:设置的元素引用变量只有在组件挂载后才能访问到,所以在挂载前对元素进行操作都是无效的
当然如果我们引用的是一个组件元素,那么获得的将是该组件的实例对象
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>
上面又有个ref,又有个toRef,不是冲突了吗?两个有不一样的功效:
<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 是对原数据的一个引用,会影响到原始值,但是响应式数据对象值改变后会不会更新视图
toRefs
从实现思路看,将传入的对象里所有的属性的值都转化为响应式数据对象,该函数兼容一个参数,即 obj 对象
<script>
// 1. 导入 toRefs
import {toRefs} from 'vue'
export default {
setup() {
const obj = {
name: '前端印象',
age: 22,
gender: 0
}
// 2. 将 obj 对象中属性count的值转化为响应式数据
const state = toRefs(obj)
// 3. 打印查看一下
console.log(state)
}
}
</script>
得到的是一个对象,对象里包含了每一个包装过后的响应式数据对象
shallowReactive
理解这一步时,听这个API的名称就知道,这是一个浅层的 reactive,难道意思就是原本的 reactive 是深层的呗,没错,这是一个用来性能优化的API
<script>
<template>
<p>{{ state.a }}</p>
<p>{{ state.first.b }}</p>
<p>{{ state.first.second.c }}</p>
<button @click="change1">改变1</button>
<button @click="change2">改变2</button>
</template>
<script>
import {shallowReactive} from 'vue'
export default {
setup() {
const obj = {
a: 1,
first: {
b: 2,
second: {
c: 3
}
}
}
const state = shallowReactive(obj)
function change1() {
state.a = 7
}
function change2() {
state.first.b = 8
state.first.second.c = 9
console.log(state);
}
return {state}
}
}
</script>
理解这一步时,首先我们点击了第二个按钮,改变了第二层的 b 和第三层的 c,虽然值发生了改变,但是视图却没有进行更新;
我们点击了第一个按钮,改变了第一层的 a 时,整个视图进行了更新;
在这个场景下,由此可说明,shallowReactive 了第一层属性的值,一旦发生改变,则更新视图
shallowRef
在这个场景下,这是一个浅层的 ref,与 shallowReactive 一样是拿来做性能优化的,配合triggerRef,调用它就能够立马更新视图,其接收一个参数 state,即需更新的 ref 对象
理解这一步时,shallowReactive 是对象第一层的数据变化用来驱动视图更新,那么 shallowRef 则是 .value 的值的变化来更新视图的
<template>
<p>{{ state.a }}</p>
<p>{{ state.first.b }}</p>
<p>{{ state.first.second.c }}</p>
<button @click="change">改变</button>
</template>
<script>
import {shallowRef, triggerRef} from 'vue'
export default {
setup() {
const obj = {
a: 1,
first: {
b: 2,
second: {
c: 3
}
}
}
const state = shallowRef(obj)
console.log(state);
function change() {
state.value.first.b = 8
state.value.first.second.c = 9
// 修改值后立即驱动视图更新
triggerRef(state)
console.log(state);
}
return {state, change}
}
}
</script>
toRaw
toRaw 方法是用来拿到 ref 或 reactive 对象的原始数据的
<script>
import {reactive, toRaw} from 'vue'
export default {
setup() {
const obj = {
name: '前端印象',
age: 22
}
const state = reactive(obj)
const raw = toRaw(state)
console.log(obj === raw) // true
}
}
</script>
在这个场景下,上述代码就证明了 toRaw 方法从 reactive 对象中拿到到的是原始数据,所以我们就能够很便于的借助修改原始数据的值而不更新视图来做一些性能优化了
注意: 补充一句,当 toRaw 方法接收的参数是 ref 对象时,需加上 .value 才能拿到到原始数据对象
markRaw
落到代码里,markRaw 方法能够将原始数据标记为非响应式的,即采用 ref 或 reactive 将其包装,仍无法实现数据响应式,其接收一个参数,即原始数据,同时得到被标记后的数据。即使我们修改了值也不会更新视图了,即没有实现数据响应式
<template>
<p>{{ state.name }}</p>
<p>{{ state.age }}</p>
<button @click="change">改变</button>
</template>
<script>
import {reactive, markRaw} from 'vue'
export default {
setup() {
const obj = {
name: '前端印象',
age: 22
}
// 通过markRaw标记原始数据obj, 使其数据更新不再被追踪
const raw = markRaw(obj)
// 试图用reactive包装raw, 使其变成响应式数据
const state = reactive(raw)
function change() {
state.age = 90
console.log(state);
}
return {state, change}
}
}
</script>
watchEffect
watchEffect 它与 watch 的区别主要有以下几点:
- 不需手动传入依赖
- 每次初始化时会执行一次回调函数来自动拿到依赖
- 无法拿到到原值,只能得到变化后的值
<script>
import {reactive, watchEffect} from 'vue'
export default {
setup() {
const state = reactive({ count: 0, name: 'zs' })
watchEffect(() => {
console.log(state.count)
console.log(state.name)
/* 初始化时打印:
0
zs
1秒后打印:
1
ls
*/
})
setTimeout(() => {
state.count ++
state.name = 'ls'
}, 1000)
}
}
</script>
结合项目来看,没有像 watch 方法一样先给其传入一个依赖,而是直接指定了一个回调函数
理解这一步时,组件初始化时,将该回调函数执行一次,自动拿到到需检测的数据是 state.count 和 state.name
根据以上特征,我们能够自行选择采用哪一个器
getCurrentInstance
实际处理时,我们都知道在Vue2的任何一个组件中想要拿到当前组件的实例能够借助 this 来得到,而在Vue3中我们大量的代码都在 setup 函数中运行,同时且在该函数中 this 指向的是undefined,那么该如何拿到到当前组件的实例呢?这时能够用到另一个方法,即 getCurrentInstance
<template>
<p>{{ num }}</p>
</template>
<script>
import {ref, getCurrentInstance} from 'vue'
export default {
setup() {
const num = ref(3)
const instance = getCurrentInstance()
console.log(instance)
return {num}
}
}
</script>
从实现思路看,instance 中重点关注 ctx 和 proxy 属性,这两个才是我们想要的 this。能够看到 ctx 和 proxy 的内容十分类似,只是后者相对于前者外部包装了一层 proxy,由此可说明 proxy 是响应式的
useStore
落到代码里,在Vue2中采用 Vuex,我们都是借助 this.$store 来与拿到到Vuex实例,但上一部分说了原本Vue2中的 this 的拿到方式不一样了,同时且我们在Vue3的 getCurrentInstance().ctx 中也没有发现 $store 这个属性,那么如何拿到到Vuex实例呢?这就要借助 vuex 中的一个方法了,即 useStore
// store 文件夹下的 index.js
import Vuex from 'vuex'
const store = Vuex.createStore({
state: {
name: '前端印象',
age: 22
},
mutations: {
……
},
……
})
// example.vue
<script>
// 从 vuex 中导入 useStore 方法
import {useStore} from 'vuex'
export default {
setup() {
// 获取 vuex 实例
const store = useStore()
console.log(store)
}
}
</script>
然后下面就能够像之前一样正常采用 vuex 了
落到代码里,以上就是vue3常用的API采用简介的详细内容,更多关于vue 常用的api采用的资料请关注脚本之家其它相关文章!
- WebStorm无法正确识别Vue3组合式API的解决方案
- 详解vue3.0 的 Composition API 的一种采用方法
- vue3.0 的 Composition API 的采用示例
- Vue3新特性之在Composition API中采用CSS Modules
- 精读《Vue3.0 Function API》
- 浅谈Vue3.0新版API之composition-api入坑指南
- 详解Vue3 Composition API中的提取和重用逻辑
- 浅谈Vue3 Composition API如何替换Vue Mixins