二、常用 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.valueconst x2 = person.job.j1.salaryconsole.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.pageXpoint.y = e.pageYconsole.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>