文章目录
![在这里插入图片描述](https://img-blog.csdnimg.cn/img_convert/fae455218db5eda917e9014ebda40b8e.gif)
一、setup语法糖
之前我们在编写代码时每次都要编写setup()
,默认导出配置,还要返回变量和方法,有什么方法可以只写变量和方法,自动返回变量和方法?setup语法糖就可以帮助我们轻松解决这个麻烦。
下面为原本的代码:
<script>
export default {
setup() {
// 创建一个响应式对象
const state = reactive({
count: 0,
title: '计数器应用',
});
// 增加计数
function increment() {
state.count++;
}
// 重置计数
function reset() {
state.count = 0;
}
// 暴露定义的数据和函数
return {
state,
increment,
reset,
};
},
};
</script>
使用setup
语法糖后:
<script setup>
import { reactive } from 'vue';
// 创建一个响应式对象
const state = reactive({
count: 0,
title: '计数器应用',
});
// 增加计数
function increment() {
state.count++;
}
// 重置计数
function reset() {
state.count = 0;
}
</script>
观察可以发现我们去除掉了export default
、setup
函数和其中的返回语句,而我们仅仅是在 script
标签上添加了setup
就实现了同样的功能,可以看到代码变得更加简洁,并且不用再写返回内容了。
二、computed函数
computed
函数用于定义计算属性,它基于其他响应式状态自动计算其值,并且具有缓存机制,只有在依赖项变化时才会重新计算。
2.1 computed的基本用法
在 setup
中,使用 computed
创建计算属性。
使用步骤:
- 导入
computed
函数 - 在
computed
函数中传入一个getter
函数用来计算数据 - 函数返回计算好的数据,返回值为一个计算属性 ref
<template>
<p>是否有出版书籍:</p>
<span>{{ publishedBooksMessage }}</span>
</template>
<script setup>
// 1. 导入 computed 函数
import { reactive, computed } from 'vue'
const author = reactive({
name: 'John Doe',
books: [
'Vue 2 - Advanced Guide',
'Vue 3 - Basic Guide',
'Vue 4 - The Mystery'
]
})
// 2. 传入getter函数计算数据,返回的计算属性 publishedBooksMessage
const publishedBooksMessage = computed(() => {
return author.books.length > 0 ? 'Yes' : 'No'
})
</script>
2.2 computed vs methods
与直接使用方法生成数据不同,computed
属性会缓存其结果,除非其依赖的响应式数据发生变化,否则不会重新计算。这对于性能优化非常重要。
前面代码的判断书籍数量是否大于0当然也可以使用函数来实现:
<template>
<p>是否有出版书籍:</p>
<p>{{ calculateBooksMessage() }}</p>
</template>
<script setup>
...
// 通过函数实现
function calculateBooksMessage() {
return author.books.length > 0 ? 'Yes' : 'No'
}
</script>
两种方式在结果上确实是完全相同的,然而,不同之处在于使用computed
会基于其响应式依赖被缓存。计算属性publishedBooksMessage
仅会在其响应式依赖更新时才重新计算。这意味着只要 author.books
不改变,无论多少次访问 publishedBooksMessage
都会立即返回先前的计算结果,而不用重复执行 getter 函数。相比之下,方法调用总是会在重渲染发生时再次执行函数。
2.3 注意事项
- 避免直接修改计算属性值
由于从计算属性返回的值是一个“临时数据”,每当源状态发生变化时,就会创建一个新的数据。更改计算属性是没有意义的,因此计算属性的返回值应该被视为只读的,并且永远不应该被更改——应该更新它所依赖的源状态以触发新的计算。
- Getter 不应有副作用
计算属性的 getter
应只做计算而没有任何其他的副作用,这一点非常重要。举例来说,不要改变其他状态、在 getter
中做异步请求或者更改 DOM!一个计算属性的声明中描述的是如何根据其他值派生一个值。因此 getter
的职责应该仅为计算和返回该值。
比如说下面的代码:在 computed
中引入了副作用
import { ref, computed } from 'vue';
const counter = ref(0);
const thresholdReached = ref(false);
// 错误:在 computed getter 中引入副作用
const checkThreshold = computed(() => {
if (counter.value > 10) {
thresholdReached.value = true; // 副作用:修改了外部状态
}
return counter.value;
});
在这个例子中,checkThreshold
是一个 computed
计算属性,但它在 getter
中修改了 thresholdReached
变量。这是一个副作用,违反了 computed
的最佳实践原则。解决这个问题的方法是使用侦听器watch
函数来根据其他响应式状态的变更来创建副作用。
import { ref, computed, watch } from 'vue';
const counter = ref(0);
const thresholdReached = ref(false);
// 正确:纯粹的计算属性
const checkThreshold = computed(() => {
return counter.value;
});
// 使用 watch 监听 counter 的变化并执行副作用
watch(counter, (newVal) => {
if (newVal > 10) {
thresholdReached.value = true; // 副作用:修改外部状态
}
});
三、watch函数
watch
函数用于监听响应式数据的变化,并执行副作用操作(如异步请求、手动修改DOM等)。在某些场景下,watch
比computed
更适合处理复杂的副作用。
3.1 watch的基本用法
watch
允许我们在数据变化时执行某些操作,例如在用户输入时进行验证或发送请求。
使用步骤:
-
导入
watch
函数:从vue
中导入watch
函数。 -
定义响应式数据:使用
ref
或reactive
定义你想要监听的响应式数据。 -
调用
watch
函数:传入要监听的响应式数据或计算属性,以及一个回调函数。当数据变化时,回调函数会被触发。
<template>
<div>
<input v-model="message" placeholder="输入一些内容">
<p>输入内容:{{ message }}</p>
</div>
</template>
<script setup>
// 1. 导入 watch 函数
import { ref, watch } from 'vue';
// 2. 定义响应式数据
const message = ref('');
// 3. 监听 message 的变化,传入回调函数
watch(message, (newValue, oldValue) => {
console.log(`message changed from ${oldValue} to ${newValue}`);
});
</script>
代码分析:
模板部分的input
使用 v-model
实现双向绑定 message
,实时更新输入框的值。
当输入框的内容改变时,message
相应的也会改变,此时watch
函数监听到message
变化后也会相应的执行回调函数来输出log信息。
vue3 中说 watch 只能监视4种数据:
- ref定义的数据
- reactive 定义的数据
- 函数返回一个值(getter函数)
- 一个包含上述内容的数组
3.2 immediate和deep选项
watch
函数接受一个可选的第三个参数,可以用来配置监听的行为:
-
immediate
:是否立即执行回调,默认是false
。如果为true
,则在监听开始后立即执行一次回调。 -
deep
:是否深度监听对象内部的变化,默认是false
。如果为true
,则会深度监听对象及其嵌套属性的变化。
<script setup>
import { ref, watch } from 'vue';
const nestedObject = ref({ nested: { value: 1 } });
// 深度监听对象
watch(nestedObject, (newVal, oldVal) => {
console.log('嵌套对象已更改:', newVal);
}, { deep: true });
</script>
四、综合小Demo
下面是一个结合了 setup
、computed
和 watch
的小Demo,在我们之前写的计数器的基础上实现以下功能:
- 计数状态显示:根据当前计数值显示 “计数大于10” 或 “计数不大于10”。
- 超限提示:当计数值超过 10 时,弹出提示框提醒用户 “计数已超过10!”。
<template>
<div>
<h1>{{ title }}</h1>
<p>当前计数:{{ count }}</p>
<p>计数状态:{{ countStatus }}</p>
<button @click="increment">增加</button>
<button @click="reset">重置</button>
</div>
</template>
<script setup>
import { ref, computed, watch } from 'vue';
const title = ref('计数器');
const count = ref(0);
// 使用 computed 创建一个计算属性
const countStatus = computed(() => {
return count.value > 10 ? '计数大于10' : '计数不大于10';
});
// 监听 count 的变化
watch(count, (newVal) => {
if (newVal > 10) {
alert('计数已超过10!');
}
});
function increment() {
count.value++;
}
function reset() {
count.value = 0;
}
</script>
<style scoped>
button {
margin: 5px;
padding: 10px 20px;
background-color: #4caf50;
color: white;
border: none;
border-radius: 5px;
cursor: pointer;
}
button:hover {
background-color: #45a049;
}
p {
font-size: 18px;
}
</style>
代码分析:
-
ref
和 setup
:在setup
函数中使用ref
创建了title
和count
两个响应式变量。 -
computed
:通过computed
创建了countStatus
计算属性,用于动态显示计数的状态。 -
watch
:使用watch
监听count
变量,当其值大于10时,弹出提示框。
五、总结
setup
语法糖、computed
函数和 watch
函数是 Vue 3 组合式 API 的核心特性,提供了更灵活和高效的方式来组织组件逻辑。本文我们详细讲解了setup
语法糖、computed
函数和 watch
函数的基本使用语法和注意事项,通过合理使用这些特性,可以大大提升代码的可读性和可维护性。希望本文的内容对大家有所帮助☺️。
参考文章: