区别
Vue2
使用的是computed属性
Vue3
需要在setup函数中使用computed方法来编写一个计算属性
computed
方法1.接收一个getter函数,并为 getter 函数返回的值,返回一个不变的 ref 对象
<template>
<div>
{{ fullname }}
</div>
</template>
<script>
import { reactive,ref,toRef,toRefs ,computed} from 'vue';
export default {
setup(){
const name=ref("dls")
const age=ref(17)
const fullname=computed(()=>{
return name.value+age.value
})
return{
fullname
}
}
}
</script>
<style>
</style>
方法2.接收一个具有 get 和 set 的对象,返回一个可变的(可读写)ref 对象
<template>
{{ fullName }}
<button @click="changeName">change</button>
</template>
<script>
import { computed, reactive } from 'vue';
export default {
name: 'App',
setup() {
const names = reactive({
firstName: '冲啊',
lastName: '迪迦奥特曼'
});
// 会返回一个ref对象
const fullName = computed({
set(newValue) {
const tempNames = newValue.split(' ');
names.firstName = tempNames[0];
names.lastName = tempNames[1];
},
get() {
return names.firstName + names.lastName;
}
});
// 设置值
const changeName = () => {
fullName.value = fullName.value === '冲啊迪迦奥特曼' ? '神秘的 宇宙人' : '冲啊 迪迦奥特曼';
};
return {
fullName,
changeName
};
}
};
</script>
Watch
- watch需要侦听特定的数据源,并在回调函数中执行副作用
- 默认情况下它是惰性的,只有当被侦听的源发生变化时才会执行回调
- 与watchEffect的比较,watch允许我们懒执行副作用(第一次不会直接执行)
- 更具体的说明当哪些状态发生变化时,触发侦听器的执行
- 访问侦听状态变化前后的值
侦听单个数据源
watch侦听函数的数据源有两种类型:
- 一个getter函数:但是该getter函数必须引用可响应式的对象(比如reactive或者ref)
- 直接写入一个可响应式的对象,reactive或者ref(比较常用的是ref)
解构
() => ({ ...star })
//其实也就等同于,因为将star解构后还是需要让他包裹在对象中
//箭头函数无法识别这个{}是函数体还是对象
() =>{
return {...star}
}
同理内部包裹的是数组时:
是ref对象时:
<template>
<div>
<h1>
{{ star }}
</h1>
<button @click="change">change</button>
</div>
</template>
<script>
import { ref, reactive, watch } from 'vue';
export default {
setup() {
/**
* 一、如果ref响应式对象中的是基本数据类型
*/
const star = ref('123');
// const star = ref('123');
// 改变
const change = () => (star.value = star.value === 'coder' ? '123' : 'coder');
// 那么直接这样传入即可,拿到的是value值本身
watch(star, (newValue, oldValue) => {
console.log('newValue:', newValue, 'oldValue:', oldValue);
});
--------------------------------------------------------------------------------------
/**
* 二、如果ref对象中的是对象数据,那么也需要结构 () => ({...star.value})
如果不结构,拿到的是ref对象
*/
const star = ref({ name: '123', age: 14 });
// 改变
const change = () => (star.value.name = star.value.name === 'coder' ? 'pppp' : 'coder');
// 那么需要结构,如果不结构,拿到的是ref对象
watch(
() => ({ ...star.value }),
(newValue, oldValue) => {
console.log('newValue:', newValue, 'oldValue:', oldValue);
}
);
return {
star,
change
};
}
};
</script>
<style lang="scss" scoped></style>
是reactive对象时
<template>
<div>
<h1>
{{ star.name }}
</h1>
<button @click="change">change</button>
</div>
</template>
<script>
import { ref, reactive, watch } from 'vue';
export default {
setup() {
const star = reactive({ name: '123', age: 15 });
// 改变
const change = () => (star.name = star.name === 'coder' ? '123' : 'coder');
/**
* 一、如果直接传入reactive对象,那么打印出的也是两个reactive对象
*/
watch(star, (newValue, oldValue) => {
console.log('newValue:', newValue, 'oldValue:', oldValue);
});
---------------------------------------------------------------------------------------
/**
* 二、如果希望获得普通对象,结构一下
*/
watch(
() => ({ ...star }),
(newValue, oldValue) => {
console.log('newValue:', newValue, 'oldValue:', oldValue);
}
);
return {
star,
change
};
}
};
</script>
<style lang="scss" scoped></style>
因为在源码中,reactive对象和ref对象的处理方法不一样(ref中已经带有了.value后缀)
侦听多个数据源
<template>
<div>
<h1>
{{ star.name }}
</h1>
<button @click="change">change</button>
</div>
</template>
<script>
import { ref, reactive, watch } from 'vue';
export default {
setup() {
// ref对象
const coder = ref('know');
// reactive对象
const star = reactive({ name: '123', age: 15 });
// 改变
const change = () => {
star.name = 'coder';
coder.value = 'unKnow';
};
// 传入数组即可 // 对应结构
watch([coder, () => ({ ...star })], ([newCoder, newStar], [oldCoder, oldStar]) => {
console.log(newCoder, newStar, '----', oldCoder, oldStar);
});
return {
star,
change
};
}
};
</script>
<style lang="scss" scoped></style>
watch的选项
配置第三个参数 :
- deep : 是否深度监听
- immediate : 是否立即执行
<template>
<div>
<h1>
{{ info.name }}
</h1>
</div>
</template>
<script>
import { ref, reactive, watch } from 'vue'
export default {
setup() {
// reactive对象
const info = reactive({
name: '123',
age: 15,
friend: { name: '456' }
})
watch(
() => {
const obj = { ...info }
// 因为有两层,需要自己结构下,否则返回的是proxy
obj.friend = { ...obj.friend }
return obj
},
(newValue, oldValue) => {
console.log(newValue, oldValue)
},
{
// 如果有多层,需要加上deep
deep: true,
// 立即执行
immediate: true
}
)
return {
info
}
}
}
</script>
<style lang="scss" scoped></style>
WatchEffect
- 自动收集响应式数据的依赖
- watchEffect传入的函数会被立即执行一次,并且在执行的过程中会收集依赖
- 只有收集的依赖发生变化时,watchEffect传入的函数才会再次执行
区别
与 watch 不同,watchEffect 不需要明确地监视特定的数据,它会自动追踪在执行过程中访问的所有响应式数据,并在其中任何一个数据发生变化时触发。
用法
在最开始就会执行一次watcheffect,本质是要执行回调的函数,因此可以回调函数设置自己命名的函数(推荐用箭头函数)
<template>
<div>
{{ age }}-{{ name }}
<button @click="click">1</button>
<button @click="click2">2</button>
</div>
</template>
<script>
import { reactive,ref,watchEffect} from 'vue';
export default {
setup(){
const name=ref("dls")
const age=ref(17)
//如果里面没有写入name的话,执行name的变化不会触发监听
watchEffect(()=>{
console.log(age.value,name.value)
})
const click=()=>{
age.value++
}
const click2=()=>{
name.value="hhh"
}
return{
name,
age,
click,
click2
}
}
}
</script>
<style>
</style>
watchEffect的停止监听
假如,某些情况想要停止监听,那么可以获取watchEffect的返回值函数,调用该函数即可,这是因为 watchEffect 返回的函数是用于取消副作用的。
停止监听的方法通常与命名没有直接关系:它通常是由函数或对象的方法提供的。在 Vue 3 中,watchEffect 返回一个停止函数,这个函数通常被命名为你选择的变量名(例如,stop 或 unwatch),但实际的名称可以根据你的代码风格和偏好来选择。
<template>
<div>
{{ age }}-{{ name }}
<button @click="click">1</button>
<button @click="click2">2</button>
</div>
</template>
<script>
import { reactive,ref,watchEffect} from 'vue';
export default {
setup(){
const name=ref("dls")
const age=ref(17)
//要先声明一个值来获取它返回的函数
const xx=watchEffect(()=>{
console.log(age.value,name.value)
})
const click=()=>{
age.value++
if (age.value > 22) {
// 停止监听
xx();
}
}
const click2=()=>{
name.value="hhh"
}
return{
name,
age,
click,
click2
}
}
}
</script>
<style>
</style>
watchEffect清除副作用
什么是清除副作用
- 比如在开发中可能需要在侦听函数中执行网络请求,但是在网络请求还没有达到的时候,停止了侦听器,或者侦听器侦听函数被再次执行了
- 那么上一次的网络请求应该被取消掉,这个时候就可以清除上一次的副作用
在Vue 3中,"副作用" 指的是与组件生命周期之外的操作相关的行为。这通常包括对 DOM 的操作、数据请求、订阅外部事件或资源等。副作用通常在组件的生命周期钩子函数之外执行,比如在 created、mounted 或 updated 钩子函数内。
Vue 3 提供了 watch、watchEffect 和 onMounted 等函数来处理副作用。这些函数使你能够监视响应式数据的变化或执行特定的操作,而无需直接在模板中进行 DOM 操作或数据请求。
使用方法
- 在给watchEffect传入的函数被回调时,其实可以获取到一个参数:onInvalidate(自己命名的,和上面的stop方法类似)
- 当副作用即将重新执行 或者 侦听器被停止 时会执行该函数传入的回调函数
- 可以在传入的回调函数中,执行一些清除工作
例如:此时获取的参数就命名为了test
<template>
<div>
{{ age }}-{{ name }}
<button @click="click">1</button>
<button @click="click2">2</button>
</div>
</template>
<script>
import { reactive,ref,watchEffect} from 'vue';
export default {
setup(){
const name=ref("dls")
const age=ref(17)
const xx=watchEffect((res)=>{
console.log(age.value,name.value)
res(()=>{
console.log("去除副作用")
})
})
const click=()=>{
age.value++
if (age.value > 22) {
// 停止监听
xx();
}
}
const click2=()=>{
name.value="hhh"
}
return{
name,
age,
click,
click2
}
}
}
</script>
<style>
</style>
watchEffect的时机
- pre : 默认值,它会在元素 挂载 或者 更新 之前执行
- post : 元素 挂载 或者 更新 之后执行
- sync : 强制同步一起执行,效率很低,不推荐
和watch的区别
- wathc必须指定数据源,watchEffect自动收集依赖
- watch监听到改变,可以拿到改变前后的值,watchEffect只能拿到最新的值
- watch第一次默认不执行,watchEffect默认直接执行一次