问题:啥叫“组合式API”?
答案:请看官方文档: https://v3.cn.vuejs.org/guide/composition-api-introduction.html
注意点1:
Vue3中写vue2的计算属性也是可以的,但是不建议混用。
注意点2:
Vue2中computed计算属性如何写
Vue3中computed计算属性如何写
项目目录
main.js
//引入的不再是Vue构造函数了,引入的是一个名为createApp的工厂函数
import { createApp } from 'vue'
import App from './App.vue'
//创建应用实例对象——app(类似于之前Vue2中的vm,但app比vm更“轻”)
const app = createApp(App)
//挂载
app.mount('#app')
App.vue
<template>
<Demo/>
</template>
<script>
import Demo from './components/Demo'
export default {
name: 'App',
components:{Demo},
}
</script>
Demo.vue
<template>
<h1>一个人的信息</h1>
姓:<input type="text" v-model="person.firstName">
<br>
名:<input type="text" v-model="person.lastName">
<br>
<span>全名:{{person.fullName}}</span>
<br>
全名:<input type="text" v-model="person.fullName">
</template>
<script>
import {reactive,computed} from 'vue'
export default {
name: 'Demo',
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 nameArr = value.split('-')
person.firstName = nameArr[0]
person.lastName = nameArr[1]
}
})
//返回一个对象(常用)
return {
person
}
}
}
</script>
结果展示:
Vue2中watch监视属性如何写
Vue3中watch监视属性如何写
注意点2:
Vue2中watch作为配置项执行定义一次,不能写多个watch,而vue3中就可以写多个watch配置项,比如监视多个属性变化
注意点3:
如果想实现监视多个属性可以配置数组,不用写多个watch,比如如图1,但是既然配置数组了,那我监视的多个属性如何用newValue,oldValue进行接收呢?答案看如图2,它会把监视所有属性的newValue放到一个数组中,同理可得oldValue对应的数组。
注意点4:
问题:如果我想实现深度监视或者配置immediate:true放哪里?
答案:watch是可以配置第三个参数的,如图
注意点5:
透露个小秘密,这个deep:true在vue3中有点小问题,后续会介绍并补充上。
注意点6:一个坑
如果你用reactive定义的响应式数据交给watch去监视,那么就会发现此处无法正确获取oldValue,但是你用ref定义的就不存在这个问题。举例,用reactive定义输出结果如图,会发现oldValue值不对。
let person = reactive({
name:'张三',
age:18
})
/*
情况三:监视reactive所定义的一个响应式数据的全部属性
1.注意:此处无法正确的获取oldValue
2.注意:强制开启了深度监视(deep配置无效)
*/
watch(person,(newValue,oldValue)=>{
console.log('person变化了',newValue,oldValue)
})
注意点7:针对注意点6中如果你改为ref定义,会发现控制台打印监视没结果,为啥?因为ref定义的响应式对象数据,最后还是会内部调用reactive,同时ref监视的是person.value才能输出打印,如果像如下代码是不会有任何打印的,除非改成监视person.value,改之后控制台能打印,但是oldValue还是失效的。
let sum = ref(0)
let msg = ref('你好啊')
let person = ref({
name:'张三',
age:18
})
//监视错误person
watch(person,(newValue,oldValue)=>{
console.log('person变化了',newValue,oldValue)
})
---------------------------------------------------------------
//监视正确 person.value
watch(person.value,(newValue,oldValue)=>{
console.log('person变化了',newValue,oldValue)
})
总结:
1)如果监听的是ref定义的基础属性(字符串或者数值),那么watch监听的第一个参数不要加.value,因为sum.value代表实际监听的是数值0这个参数,而0你咋监听,你监听的只能是RefImpl对象(或者说监听的是保存数据的一个结构)。
正确写法
watch(sum,(newValue,oldValue)=>{
console.log('sum变了',newValue,oldValue)
},{immediate:true})
--------------------------------------------------------------------------------------------
错误写法
watch(sum.value,(newValue,oldValue)=>{
console.log('sum变了',newValue,oldValue)
},{immediate:true})
2)监听整个person对象
如果使用ref定义的对象类型数据,那么watch监听的必须是person.value才正确,因为person.value实际是Proxy,也就是ref内部调用reactive封装的Proxy代理对象;而如果使用reactive定义的对象类型数据,那么watch监听的必须是person整个对象才是正确的;
思考问题:为啥监听person.value就不用开启深度监视?
答案:监听person.value 对应的是Proxy代理对象,属于内部隐式调用reactive,默认自动开启深度监视,而监听person对应的是RefImpl对象,必须手动开启深度监视
如果使用ref定义的对象类型数据
let person = ref({
name:'张三',
age:18,
job:{
j1:{
salary:20
}
}
})
//正确写法方式1:第一种监听person.value
watch(person.value,(newValue,oldValue)=>{
:console.log('person变化了',newValue,oldValue)
})
------------------------------------------------------------------------------------------
//正确写法方式2:第二种监听person,但是开启深度监视deep:true
watch(person,(newValue,oldValue)=>{
:console.log('person变化了',newValue,oldValue)
},{deep:true})
如果使用reactive定义的对象类型数据直接监视person对象就行,默认开启深度监视,所以不用设置deep:true
let person = ref({
name:'张三',
age:18,
job:{
j1:{
salary:20
}
}
})
watch(person,(newValue,oldValue)=>{
:console.log('person变化了',newValue,oldValue)
})
3)监听person对象的某个属性
推荐用reactive封装对象类型数据,不推荐ref封装对象类型数据。
所以这里咱们考虑使用reactive定义的对象类型数据,如果监听的是person对象中的基础属性,比如监听name属性(字符串或者数值),那么监听第一个参数请写成函数
//情况五:监视reactive所定义的一个响应式数据中的某些属性
watch([()=>person.name,()=>person.age],(newValue,oldValue)=>{
console.log('person的name或age变化了',newValue,oldValue)
})
如果监听的是person对象中job对象中的某个属性,那么必须设置{deep:true},否则深度监听无效
这是正确的
watch(()=>person.job,(newValue,oldValue)=>{
console.log('person的job变化了',newValue,oldValue)
},{deep:true}) //此处由于监视的是reactive素定义的对象中的某个属性,所以deep配置有效
--------------------------------------------------------------------------------------------
这是错误的
watch(()=>person.job,(newValue,oldValue)=>{
console.log('person的job变化了',newValue,oldValue)
})
注意点8:
用reactive定义的对象有多层嵌套的话,那么vue3默认开启深度监视,而vue2必须开启deep:true后才能识别深度监视。
注意点9:(和注意点12进行对比记忆)
用reactive定义的响应式对象数据,强制开启深度监视,无法关闭deep配置(前提是watch监视的是reactive定义的整个对象,如果监视的是person对象中job属性中的salary那就不好使了)
注意点10:
监视reactive定义的一个响应式数据对象中的某个属性,第一个参数必须写成函数且返回值(否则写别的无效),例如()=>person.name代表只监视name属性变化,这样写这个oldValue就是生效的。
注意点11:
问题:针对注意点10,如果我想监视多个属性呢?
注意点12:(和注意点9进行对比记忆)
如下代码如果想实现监听person.job,但是点击涨薪按钮后修改的是job里的salary发现没监听到?错误展示效果看如图:watch监视的特殊情况不生效案例.gif
错误原因在于没开启deep:true深度监视
let person = reactive({
name:'张三',
age:18,
job:{
j1:{
salary:20
}
}
})
//特殊情况
//错误写法,未配置deep:true
watch(()=>person.job,(newValue,oldValue)=>{
console.log('person的job变化了',newValue,oldValue)
}})
------------------------------------------------------------------
//正确写法,配置deep:true
watch(()=>person.job,(newValue,oldValue)=>{
console.log('person的job变化了',newValue,oldValue)
}},{deep:true}) //此处由于监视的是reactive素定义的对象中的某个属性,所以deep配置有效
注意点13:
项目中使用情况三方式多些,此处oldValue是无效的,但是如果你非想使用oldValue,那么可以把person中需要监听oldValue的属性单独用ref去包裹设置,因为ref方式中oldValue是生效的。
注意点14:
总结:坑其实有3个
第1个坑:监视reactive所定义的一个响应式数据对象person,无法正确的获取oldValue,且强制开启了深度监视(deep配置无效)
第2个坑:监视person对象中的name属性,比如情况四,那么oldValue就是好使的
第3个坑:针对特殊情况,如果监视person.job,而你修改的是person.job里的嵌套属性salary,那么必须开启深度监视deep:true后才能监听到。
项目目录
main.js
//引入的不再是Vue构造函数了,引入的是一个名为createApp的工厂函数
import { createApp } from 'vue'
import App from './App.vue'
//创建应用实例对象——app(类似于之前Vue2中的vm,但app比vm更“轻”)
const app = createApp(App)
//挂载
app.mount('#app')
App.vue
<template>
<Demo/>
</template>
<script>
import Demo from './components/Demo'
export default {
name: 'App',
components:{Demo},
}
</script>
Demo.vue
<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}}K</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',
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)
// },{deep:false}) //此处的deep配置无效
//情况四:监视reactive所定义的一个响应式数据中的某个属性
/* watch(()=>person.name,(newValue,oldValue)=>{
console.log('person的name变化了',newValue,oldValue)
}) */
//情况五:监视reactive所定义的一个响应式数据中的某些属性
/* watch([()=>person.name,()=>person.age],(newValue,oldValue)=>{
console.log('person的name或age变化了',newValue,oldValue)
}) */
//特殊情况
/* watch(()=>person.job,(newValue,oldValue)=>{
console.log('person的job变化了',newValue,oldValue)
},{deep:true}) //此处由于监视的是reactive素定义的对象中的某个属性,所以deep配置有效 */
//返回一个对象(常用)
return {
sum,
msg,
person
}
}
}
</script>
结果展示:
问题:watchEffect和watch区别?
答案:
1)watchEffect不告诉你它监视谁,回调中用到哪个属性我就自动监视哪个属性,跟watch函数区别是没有第一个参数
2)watchEffect默认开启立即执行属性immediate:true和深度监视deep:true
注意点2:
问题:watchEffect和computed区别?
答案:computed注重return的返回值,而watchEffect更注重过程
1.《vue3第二章》常用组合式 Composition API,包括setup、ref函数、reactive函数、vue3.0中的响应式原理、计算属性与监听属性
2.vue3知识点:setup
3.vue3知识点:ref函数
4.vue3知识点:reactive函数
5.vue3知识点:Vue3.0中的响应式原理和 vue2.x的响应式
6.vue3知识点:reactive对比ref
7.vue3知识点:计算属性与监视属性
8.vue3知识点:生命周期
9.vue3知识点:自定义hook函数
10.vue3知识点:toRef函数和toRefs函数