Vue3.X 新特性 Composition Api
Composition API 也叫组合式API,是Vue3.x的新特性。
没有Composition API
之前vue相关业务的代码需要配置到option
的特定的区域,中小型项目是没有问题的,但是在大型项目中会导致后期的维护性比较复杂,同时代码可复用性不高。Vue3.x中的composition-api
就是为了解决这个问题而生的。
compositon-api提供了以下几个函数:
setup
ref
reactive
watchEffect
watch
computed
toRefs
生命周期的hooks
一、setup() 函数
是 vue3 中,专门为组件提供的新属性。它为我们使用 vue3 的 Composition API 新特性提供了统一的入口。
1、setup()函数执行时机
setup
函数会在 beforeCreate
之后、created
之前执行,即在创建组件之前执行。由于在执行 setup
时尚未创建组件实例,因此在 setup
选项中没有 this
。这意味着,除了props
之外,你将无法访问组件中声明的任何属性——本地状态、计算属性或方法。
2、setup() 函数中的参数
setup() 函数的第一个参数是 props
,组件接收的 props
数据可以在 setup()
函数内访问到。setup
函数中的 props
是响应式的,当传入新的 prop
时,它将被更新。
props: {
title: String
}
setup(props) {
console.log(props.title)
}
因为 props
是响应式的,你不能使用 ES6 解构,因为它会消除 prop
的响应性。如果需要解构 prop
,可以通过使用 setup
函数中的 toRefs
来安全地完成此操作。
import { toRefs } from ‘vue‘
setup(props) {
const { title } = toRefs(props)
console.log(title.value)
}
setup() 的第二个参数是context(上下文对象),它是一个上下文对象,可以通过 context 来访问Vue的实例 this 。
setup(props, context) {
context.attrs
context.slots
context.parent
context.root
context.emit
context.refs
}
二、ref() 函数
根据给定的值来创建一个响应式的数据对象,返回值是一个对象,且只包含一个.value
属性。
ref用来定义响应式的 字符串、 数值、 数组、Bool类型
<template>
{{title}}
</template>
<script lang="ts">
import { ref } from 'vue';
export default {
name:'App',
setup(){
const title=ref("hello world");
return{
title,
}
}
}
如果要更改ref里面的值,需要更改value属性
// 创建响应式数据对象 count,初始值为 0
const count = ref(0)
// 如果要访问 ref() 创建出来的响应式数据对象的值,必须通过 .value 属性才可以
console.log(count.value) // 输出 0
// 让 count 的值 +1
count.value++
// 再次打印 count 的值
console.log(count.value) // 输出 1
注意:新的 ref 会覆盖旧的 ref,示例代码如下:
// 创建 ref 并挂载到 reactive 中
const c1 = ref(0)
const state = reactive({
c1
})
// 再次创建 ref,命名为 c2
const c2 = ref(9)
// 将 旧 ref c1 替换为 新 ref c2
state.c1 = c2
state.c1++
console.log(state.c1) // 输出 10
console.log(c2.value) // 输出 10
console.log(c1.value) // 输出 0
在setup()
函数内,由ref()
创建的响应式数据返回的是对象,所以需要用.value
来访问; 在setup()
函数外部则不需要.value
,模板中直接访问即可。
三、reactive() 函数
接收一个普通的对象,返回出一个响应式对象。(用来定义响应式的对象)
在Vue2.x的版本中,我们只需要在 data() 中定义一个数据就能将它变为响应式数据,在 Vue3.0 中,需要用 reactive函数或者 ref 来创建响应式数据
<template>
{{userInfo.username}}----{{userInfo.age}}
</template>
<script lang="ts">
import { reactive } from 'vue';
export default {
name:'App',
setup(){
const userInfo=reactive({
username:"张三",
age:18
})
return{
userInfo,
}
}
}
四、toRefs 解构响应式对象数据
把一个响应式对象转换成普通对象,并且该普通对象上的每个属性节点,都是 ref() 类型的响应式数据
由于每次调用都要使用 userInfo.属性
的形式比较麻烦,想到了使用解构的方法,即 return
里面 userInfo
写成 ...userInfo
,但是这样写虽然第一次数据可以显示,但是数据失去了响应式,当数据改变的时候页面数据不会跟着更新,
所以vue3 中可以使用toRefs
来解决这个问题
<template>
{{username}}----{{age}}
</template>
<script lang="ts">
import { toRefs,reactive } from 'vue';
export default {
name:'App',
setup(){
const userInfo=reactive({
username:"张三",
age:18
})
return{
...toRefs(userInfo),
}
}
}
五、computed() 计算属性
用来创建计算属性,返回值是一个 ref() 实例。
// 创建一个 ref 响应式数据
const count = ref(1)
// 根据 count 的值,创建一个响应式的计算属性 plusOne
// 它会根据依赖的 ref 自动计算并返回一个新的 ref
const plusOne = computed(() => count.value + 1)
console.log(plusOne.value) // 输出 2
plusOne.value++ // error
六、readonly() 只读
传入一个对象(响应式或普通)或 ref,返回一个原始对象的只读代理。一个只读的代理是“深层的”,对象内部任何嵌套的属性也都是只读的。
传入普通对象等返回只读代理。传入普通数值或字符串不能变成只读,例如 readonly(‘abc’)
import { reactive, readonly } from "vue";
export default {
name: "Readonly",
setup() {
const original = reactive({ count: 0 });
const copy = readonly(original);
setInterval(() => {
original.count++;
copy.count++; // 报警告,Set operation on key "count" failed: target is readonly. Proxy {count: 1}
}, 1000);
return { original, copy };
},
};
七、watch侦听器
1、具有一定的惰性lazy 第一次页面展示的时候不会执行,只有数据变化的时候才会执行
2、参数可以拿到当前值和原始值
3、可以侦听多个数据的变化,用一个侦听起承载
<template>
<input type="text" v-model="name">
</template>
<script lang="ts">
import { ref,watch } from 'vue';
export default {
name:'App',
setup(){
const name = ref('leilei')
watch(name, (curVal, prevVal) => {
console.log(curVal, prevVal)
})
return{
name,
}
}
}
对引用类型进行监听-----
<template>
Name: <input v-model="name" /> englishName: <input v-model="englishName" />
</template>
<script lang="ts">
import { reactive,watch,toRefs } from 'vue';
setup() {
const nameObj = reactive({name: 'leilei', age: 18})
监听一个数据
watch(() => nameObj.name, (curVal, prevVal) => {
console.log(curVal, prevVal)
})
监听多个数据
watch([() => nameObj.name, () => nameObj.age], ([curName, curEng], [prevName, curEng]) => {
console.log(curName, curEng, '----', prevName, curEng)
setTimeout(() => {
stop()
}, 5000)
})
const { name, englishName } = toRefs(nameObj)
}
watch也可以变为非惰性的 立即执行的 添加第三个参数 immediate: true
watch([() => nameObj.name, () => nameObj.name], ([curName, curEng], [prevName, curEng]) => {
console.log(curName, curEng, '----', prevName, curEng)
setTimeout(() => {
stop()
}, 5000)
}, {
immediate: true
})
八、watchEffect侦听器
没有过多的参数 只有一个回调函数
1、立即执行,没有惰性,页面的首次加载就会执行。
2、自动检测内部代码,代码中有依赖 便会执行
3、不需要传递要侦听的内容 会自动感知代码依赖,不需要传递很多参数,只要传递一个回调函数
4、不能获取之前数据的值 只能获取当前值
5、一些=异步的操作放在这里会更加合适
const stop = watchEffect(() => {
console.log(nameObj.name)
setTimeout(() => {
stop()
}, 5000)
})
watch 与 watchEffect 的不同:
watch 初次渲染不执行
watch 侦听的更具体 watch可以访问侦听数据变化前后的值
watch 侦听单个数据源侦听的数据可以是个 reactive 创建出的响应式数据(拥有返回值的 getter 函数),也可以是个 ref
九、Provider Inject 组件传值
通常,当我们需要将数据从父组件传递到子组件时,我们使用 props。想象一下这样的结构:你有一些深嵌套的组件,而你只需要来自深嵌套子组件中父组件的某些内容。在这种情况下,你仍然需要将 prop 传递到整个组件链中,这可能会很烦人。
对于这种情况,我们可以使用 provide 和 inject 对父组件可以作为其所有子组件的依赖项提供程序,而不管组件层次结构有多深。这个特性有两个部分:父组件有一个 provide 选项来提供数据,子组件有一个 inject 选项来开始使用这个数据。
非组合式api中的写法:
<!-- src/components/MyMap.vue -->
<template>
<MyMarker />
</template>
<script>
import MyMarker from ‘./MyMarker.vue‘
export default {
components: {
MyMarker
},
provide: {
location: ‘North Pole‘,
geolocation: {
longitude: 90,
latitude: 135
}
}
}
</script>
<!-- src/components/MyMarker.vue -->
<script>
export default {
inject: [‘location‘, ‘geolocation‘]
}
</script>
组合式api中的写法:
Provider:
在 setup() 中使用 provide 时,我们首先从 vue 显式导入 provide 方法。这使我们能够调用 provide 时来定义每个 property。
provide 函数允许你通过两个参数定义 property:
property 的 name (
property 的 value
使用 MyMap 组件,我们提供的值可以按如下方式重构:
<!-- src/components/MyMap.vue -->
<template>
<MyMarker />
</template>
<script>
import { provide } from ‘vue‘
import MyMarker from ‘./MyMarker.vue
export default {
components: {
MyMarker
},
setup() {
provide(‘location‘, ‘North Pole‘)
provide(‘geolocation‘, {
longitude: 90,
latitude: 135
})
}
}
</script>
Inject:
在 setup() 中使用 inject 时,还需要从 vue 显式导入它。一旦我们这样做了,我们就可以调用它来定义如何将它暴露给我们的组件。
inject 函数有两个参数:
要注入的 property 的名称
一个默认的值 (可选)
使用 MyMarker 组件,可以使用以下代码对其进行重构:
<!-- src/components/MyMarker.vue -->
<script>
import { inject } from ‘vue‘
export default {
setup() {
const userLocation = inject(‘location‘, ‘The Universe‘)
const userGeolocation = inject(‘geolocation‘)
return {
userLocation,
userGeolocation
}
}
}
</script>
Provider Inject 响应性
父组件:
import {
provide,
ref,
reactive
} from ‘vue‘
setup() {
const location = ref(‘北京‘)
const geolocation = reactive({
longitude: 90,
latitude: 135
})
const updateLocation = () => {
location.value = ‘上海‘
}
provide(‘location‘, location);
provide(‘geolocation‘, geolocation);
return {
updateLocation
}
}
<button @click="updateLocation">改变location</button>
子组件:
import { inject } from ‘vue‘
export default {
setup() {
const userLocation = inject(‘location‘, ‘The Universe‘)
const userGeolocation = inject(‘geolocation‘)
return {
userLocation,
userGeolocation
}
}
}
</script>