二、常用 Composition API
7. 计算属性与监视
7.1 computed函数
- 与Vue2.x中computed配置功能一致
- 写法
<template>
<h1>一个人的信息</h1>
姓:<input type="text" v-model="person.firstName">
<br>
<br>
名:<input type="text" v-model="person.lastName">
<br>
<br>
<span>全名:{{ person.fullName }}</span>
<br>
全名:<input type="text" v-model="person.fullName">
</template>
<script>
import {reactive, computed} from 'vue'
export default {
name: 'Demo',
/* computed: {
fullName(){
return this.person.firstName + '-' + this.person.lastName
}
}, */
//数据
setup(){
//数据
let person = reactive({
firstName: '张',
lastName: '三',
})
//计算属性——简写(没有考虑计算属性被修改的情况)
/* person.fullName = computed(() => {
return person.firstName + '-' + person.lastName
}) */
//计算属性——完整写法(考虑读和写)
person.fullName = computed({
get(){
return person.firstName + '-' + person.lastName
},
set(value){
const newArr = value.split('-')
person.firstName = newArr[0]
person.lastName = newArr[1]
}
})
//返回一个对象(常用)
return {
person,
}
}
}
</script>
7.2 watch函数
- 与Vue2.x中watch配置功能一致
- 两个小“坑”:
- 监视reactive定义的响应式数据时:oldValue无法正确获取、强制开启了深度监视(deep配置失效)。
- 监视reactive定义的响应式数据中某个属性时:deep配置有效。
<template>
<h2>当前求和为:{{ sum }}</h2>
<button @click="sum++">点我+1</button>
<hr>
<h2>当前的信息为:{{ msg }}</h2>
<button @click="msg+='!'">修改信息</button>
<hr>
<h2>姓名:{{ person.name }}</h2>
<h2>年龄:{{ person.age }}</h2>
<h2>薪资:{{ person.job.j1.salary }}</h2>
<button @click="person.name += '~'">修改姓名</button>
<button @click="person.age++ ">增长年龄</button>
<button @click="person.job.j1.salary++">涨薪</button>
</template>
<script>
import {ref, reactive ,watch} from 'vue'
export default {
name: 'Demo',
//Vue2
//watch: {
//简写
/* sum(newValue, oldValue){
console.log('sum的值变化了!', newValue, oldValue);
} */
//完整
/* sum: {
immediate: true,
deep: true,
handler(newValue, oldValue){
console.log('sum的值变化了!', newValue, oldValue);
}
} */
//},
//数据
setup(){
//数据
let sum = ref(0)
let msg = ref('你好啊')
let person = reactive({
name: '张三',
age: 18,
job: {
j1: {
salary: 20
}
}
})
//情况一:监视ref所定义的响应式数据
watch(sum, (newValue, oldValue) => {
console.log('sum变了',newValue, oldValue);
}, {immediate: true})
//情况二:监视ref所定义的多个响应式数据
watch([sum, msg], (newValue, oldValue) => {
console.log('sum或msg变了', newValue, oldValue);
},{immediate: true})
/* 情况三:监视reactive所定义的一个响应式数据的全部属性
1. 注意:此处无法正确的获取oldValue
2. 注意:强制开启了深度监视(deep配置无效)
*/
watch(person, (newValue, oldValue) => {
console.log('person变化了', newValue, oldValue);
})
//情况四:监视reactive所定义的一个响应式数据中的某个属性
watch(() => person.age, (newValue, oldValue) => {
console.log('person年龄变化了', newValue, oldValue);
})
//情况五:监视reactive所定义的一个响应式数据中的某些属性
watch([() => person.age, () => person.name], (newValue, oldValue) => {
console.log('person年龄或姓名变化了', newValue, oldValue);
})
//特殊情况
watch(() => person.job, (newValue, oldValue) => {
console.log('person的job变化了', newValue, oldValue);
}, {deep: true}) //此处由于监视的是reactive定义的对象中的某个属性,所以deep配置有效
//返回一个对象(常用)
return {
sum,
msg,
person,
}
}
}
</script>
7.3 watchEffect函数
- watch的套路是:既要指明监视的属性,也要指明监视的回调。
- watchEffect的套路是:不用指明监视哪个属性,监视的回调中用到哪个属性,那就监视哪个属性。
- watchEffect有点像computed:
- 但computed注重的计算出来的值(回调函数的返回值),所以必须要写返回值。
- 而watchEffect更注重的是过程(回调函数的函数体),所以不用写返回值。
//watchEffect所指定的回调中用到的数据只要发生变化,则直接重新执行回调。
watchEffect(() => {
const x1 = sum.value
const x2 = person.job.j1.salary
console.log('watchEffect所指定的回调执行了');
})
8. 生命周期
- Vue3.0中可以继续使用Vue2.x中的生命周期钩子,但有有两个被更名:
beforeDestroy
改名为beforeUnmount
destroyed
改名为unmounted
- Vue3.0也提供了 Composition API 形式的生命周期钩子,与Vue2.x中钩子对应关系如下:
beforeCreate
===>setup()
created
=======>setup()
beforeMount
===>onBeforeMount
mounted
=======>onMounted
beforeUpdate
===>onBeforeUpdate
updated
=======>onUpdated
beforeUnmount
==>onBeforeUnmount
unmounted
=====>onUnmounted
<template>
<h2>当前求和为:{{ sum }}</h2>
<button @click="sum++">点我+1</button>
</template>
<script>
import {onBeforeMount, ref, onMounted, onBeforeUpdate, onUpdated, onBeforeUnmount, onUnmounted} from 'vue'
export default {
name: 'Demo',
//通过配置项的形式使用生命周期钩子
beforeCreate(){
console.log('---beforeCreate---');
},
created(){
console.log('---created---');
},
beforeMount(){
console.log('---beforeMount---');
},
mounted(){
console.log('---mounted---');
},
beforeUpdate(){
console.log('---beforeUpdate---');
},
updated(){
console.log('---updated---');
},
beforeUnmount() {
console.log('---beforeUnmount---');
},
unmounted(){
console.log('---unmounted---');
},
//数据
setup(){
console.log('---setup---');
//数据
let sum = ref(0)
//通过组合式API的形式去使用生命周期钩子
onBeforeMount(() => {
console.log('---onBeforeMount---');
})
onMounted(() => {
console.log('---onMounted---');
})
onBeforeUpdate(() => {
console.log('---onBeforeUpdate---');
})
onUpdated(() => {
console.log('---onUpdated---');
})
onBeforeUnmount(() => {
console.log('---onBeforeUnmount---');
})
onUnmounted(() => {
console.log('---onUnmounted---');
})
//返回一个对象(常用)
return {
sum,
}
}
}
</script>
9. 自定义hook函数
- 什么是hook?
——本质是一个函数,把setup函数中使用的Composition API进行了封装。 - 类似于vue2.x中的mixin。
- 自定义hook的优势:复用代码,让setup中的逻辑更清楚易懂。
usePoint.js
import { reactive, onMounted, onBeforeUnmount } from 'vue'
export default function (){
//实现鼠标打点的相关数据
let point = reactive({
x: 0,
y: 0
})
//实现鼠标打点的方法
function savePoint(e){
point.x = e.pageX
point.y = e.pageY
console.log(e.pageX, e.pageY);
}
//实现鼠标打点的相关的生命周期钩子
onMounted(() => {
window.addEventListener('click', savePoint)
})
onBeforeUnmount(() => {
window.removeEventListener('click', savePoint)
})
return point
}
App.vue
<template>
<button @click="isShowDemo = !isShowDemo">切换隐藏/显示</button>
<Demo v-if="isShowDemo"></Demo>
<hr>
<Test></Test>
</template>
<script>
import {ref} from 'vue'
import Demo from './components/Demo.vue'
import Test from './components/Test.vue'
export default {
name: 'App',
components: {Demo, Test},
setup(){
let isShowDemo = ref(true)
return {isShowDemo}
}
}
</script>
components/Demo.vue
<template>
<h2>当前求和为:{{ sum }}</h2>
<button @click="sum++">点我+1</button>
<hr>
<h2>当前点击时鼠标的坐标为: x: {{point.x}}, y: {{point.y}}</h2>
</template>
<script>
import { ref } from 'vue'
import usePoint from '../hooks/usePoint'
export default {
name: 'Demo',
//数据
setup(){
//数据
let sum = ref(0)
let point = usePoint()
//返回一个对象(常用)
return {
sum,
point
}
}
}
</script>
10. toRef
-
作用:创建一个 ref 对象,其value值指向另一个对象中的某个属性。
-
语法:
const name = toRef(person,'name')
-
应用: 要将响应式对象中的某个属性单独提供给外部使用时。
-
扩展:
toRefs
与toRef
功能一致,但可以批量创建多个 ref 对象,语法:toRefs(person)
APP.vue
<template>
<h2>姓名:{{ name }}</h2>
<h2>年龄:{{ age }}</h2>
<h2>薪资:{{ job.j1.salary }}</h2>
<button @click="name += '~'">修改姓名</button>
<button @click="age++ ">增长年龄</button>
<button @click="job.j1.salary++">涨薪</button>
</template>
<script>
import {ref, reactive, toRef, toRefs} from 'vue'
export default {
name: 'Demo',
//数据
setup(){
//数据
let person = reactive({
name: '张三',
age: 18,
job: {
j1: {
salary: 20
}
}
})
// const name1 = person.name
// console.log('%%%', name1);
// const name2 = toRef(person, 'name')
// console.log('###', name2);
const x = toRefs(person)
console.log('@@@', x);
//返回一个对象(常用)
return {
// name: toRef(person, 'name'),
// age: toRef(person, 'age'),
// salary: toRef(person.job.j1, 'salary')
...toRefs(person)
}
}
}
</script>