最简单的企业网站网站开发详细流程
书接上文,这篇继续来学习vue3的核心语法,可以先看上一篇再来看这篇效果更好。
1. computed
computed
用于创建 计算属性,即基于其他响应式数据的值动态计算并缓存的属性。它的主要作用是优化性能和提高代码的可维护性,避免不必要的重复计算。
a. 基本语法
computed
是通过 computed()
函数创建的。在 setup()
函数中,你可以使用 computed
来定义计算属性。
import { computed, ref } from 'vue';export default {setup() {const count = ref(0);const doubledCount = computed(() => count.value * 2);return { count, doubledCount };}
};
解释:
count
是一个响应式数据,初始值为0
。doubledCount
是一个计算属性,它依赖于count
,并返回count
的两倍。- 当
count
的值发生变化时,doubledCount
会自动重新计算。
b. 计算属性的缓存
computed
的一个重要特性是 缓存。只有当计算属性依赖的响应式数据发生变化时,computed
才会重新计算,否则会直接返回上一次计算的结果。
import { computed, ref } from 'vue';export default {setup() {const count = ref(0);// 计算属性:返回 count 的平方const squaredCount = computed(() => {console.log('Computing squared count');return count.value * count.value;});return { count, squaredCount };}
};
在上面的例子中,如果你多次访问 squaredCount
,你会看到 “Computing squared count” 只会在 count
改变时打印一次,而不会在每次访问时都打印,这就是 computed
的缓存机制。
c. 使用 computed
的场景
-
计算派生状态:当你需要基于现有的响应式数据计算新的值时,
computed
很有用。例如,计算一个总价、折扣后价格、过滤后的列表等。const price = ref(100); const discount = ref(0.2);const discountedPrice = computed(() => price.value * (1 - discount.value));
-
优化性能:通过缓存计算结果,
computed
可以避免重复计算,提升性能。 -
条件渲染:你可以使用
computed
来做一些复杂的条件判断,而不需要在模板中使用大量的v-if
。
与 watch
的对比
watch
用于观察响应式数据的变化,并执行副作用操作,如更新外部状态或执行异步操作。它适合处理需要副作用的场景,而 computed
更适合处理计算派生状态并返回值。
import { watch, ref } from 'vue';const count = ref(0);// 使用 watch
watch(count, (newValue) => {console.log('Count changed:', newValue);
});// 使用 computed
const doubledCount = computed(() => count.value * 2);
watch
用于监听count
的变化,并在变化时执行某个副作用。computed
用于计算并返回派生值,在模板中或其他地方直接使用。
2. watch
在 Vue 3 中,watch
是用于 监听响应式数据的变化 并在数据变化时执行副作用操作的 API。它特别适用于处理那些需要在数据变化时执行的逻辑,例如异步请求、数据处理、或者与外部系统交互。
watch
的基本概念
- 监听:
watch
允许你监控一个或多个响应式数据的变化。 - 副作用:当被监听的数据发生变化时,
watch
会执行一个回调函数,你可以在回调中执行任何副作用操作(例如,发起异步请求、更新外部状态等)。 - 深度监听:
watch
也可以深度监听对象或数组的变化,适合用于嵌套对象或数组的更新。
a. 基本语法
watch
接受三个参数:
- 第一个参数:要观察的响应式数据或计算属性。
- 第二个参数:回调函数,监听到数据变化时会调用它。
- 第三个参数(可选):配置对象,可以设置一些附加选项,如
immediate
和deep
。
最简单的使用方式
import { ref, watch } from 'vue';export default {setup() {const count = ref(0);// 监听 count 的变化watch(count, (newValue, oldValue) => {console.log(`count changed from ${oldValue} to ${newValue}`);});return { count };}
};
解释:
watch
监听count
的变化,每当count
发生变化时,会调用回调函数。- 回调函数接收两个参数:
newValue
(新的值)和oldValue
(旧的值),可以在回调中执行任何副作用操作。
b. watch
的高级用法
监听多个响应式数据
你可以同时监听多个响应式数据,当它们中的任意一个变化时,回调都会被触发。
import { ref, watch } from 'vue';export default {setup() {const count = ref(0);const name = ref('Alice');// 同时监听 count 和 name 的变化watch([count, name], ([newCount, newName], [oldCount, oldName]) => {console.log(`count changed from ${oldCount} to ${newCount}`);console.log(`name changed from ${oldName} to ${newName}`);});return { count, name };}
};
深度监听对象或数组
默认情况下,watch
只会监听对象或数组的 引用变化,而不会递归监听其内部属性或元素。要实现深度监听,可以使用 deep: true
配置选项。
import { ref, watch } from 'vue';export default {setup() {const user = ref({name: 'Alice',age: 25});// 深度监听 user 对象的变化watch(user, (newValue, oldValue) => {console.log('User changed:', newValue);}, { deep: true });return { user };}
};
立即执行(immediate
)
watch
默认在被监听的数据发生变化时才会执行回调。如果你希望在监听开始时立即执行回调,可以使用 immediate: true
配置。
import { ref, watch } from 'vue';export default {setup() {const count = ref(0);// 监听 count 的变化,并立即执行回调watch(count, (newValue, oldValue) => {console.log(`count changed from ${oldValue} to ${newValue}`);}, { immediate: true });return { count };}
};
执行异步操作
watch
是非常适合用于执行异步操作的。比如,当某个数据变化时,你可能需要去发起一个 API 请求。
import { ref, watch } from 'vue';export default {setup() {const searchQuery = ref('');// 监听 searchQuery 的变化,发起一个异步操作watch(searchQuery, async (newQuery) => {if (newQuery) {const results = await fetch(`https://api.example.com/search?q=${newQuery}`);console.log(await results.json());}});return { searchQuery };}
};
2.1 watchEffect
的使用
watchEffect
是 watch
的一个变体,它会在组件挂载时立即执行,并且会自动追踪作用域内所有的响应式依赖。与 watch
主要是用于监听特定数据的变化不同,watchEffect
会自动检测你使用的所有响应式数据。
import { ref, watchEffect } from 'vue';export default {setup() {const count = ref(0);// watchEffect 会自动监听 count 的变化watchEffect(() => {console.log(`Count has changed: ${count.value}`);});return { count };}
};
watchEffect
和 watch
的区别:
watch
:用于精确监听一个或多个特定的数据源,并可以对变化做出响应。watchEffect
:自动追踪你在其作用域内使用的所有响应式数据,并在数据变化时重新执行回调。
c. watch
的使用场景
- 监听数据变化并执行副作用:例如,发起 API 请求、更新外部状态等。
- 异步操作:比如在数据变化时发起一个网络请求或进行复杂的数据处理。
- 表单验证:在表单字段变化时执行验证逻辑。
- 数据持久化:监听数据变化并将数据持久化到本地存储或服务器。
3. props
props
是父组件向子组件传递数据的一种方式。通过 props
,父组件可以将数据或参数传递给子组件,从而实现组件之间的通信。
a. 基本概念
- 父组件 -> 子组件:
props
是单向数据流(单向绑定)的机制,数据只能从父组件传递到子组件,子组件不能直接修改从父组件接收的props
。 - 类型验证:Vue 允许对传递的
props
进行类型验证和默认值设置,从而确保数据的正确性。
b. 基本使用
父组件
<script lang="ts" setup name="App">import Person from './components/Person.vue'import {reactive} from 'vue'import {type Persons} from './types'let persons = reactive<Persons>([{id:'e98219e12', name:'张三', age:18},{id:'e98219e13', name:'李四', age:19},{id:'e98219e14', name:'王五', age:20}])
</script>
子组件
<script lang="ts" setup name="Person">import {defineProps} from 'vue'// types中包含了Persons类型import {type Persons} from '@/types'// 第一种写法:仅接收// const props = defineProps(['list'])// 第二种写法:接收 + 限制类型// defineProps<{list:Persons}>()// 第三种写法:接收 + 限制类型 + 指定默认值 + 限制必要性// list后如果加?表示可传可不传,不加则必须传let props = withDefaults(defineProps<{list?:Persons}>(),{list:()=>[{id:'asdasg01',name:'小猪佩奇',age:18}]})console.log(props)
</script>
通过 props
,Vue 提供了一种简单而高效的组件通信方式,非常适合用于父子组件之间的数据传递和状态共享。
4. Hooks
- 什么是hook?—— 本质是一个函数,把setup函数中使用的Composition API进行了封装,类似于vue2.x中的mixin。
- 自定义hook的优势:复用代码, 让setup中的逻辑更清楚易懂。
示例代码:
useSum.ts
:
import {ref,onMounted} from 'vue'export default function(){let sum = ref(0)const increment = ()=>{sum.value += 1}const decrement = ()=>{sum.value -= 1}onMounted(()=>{increment()})//向外部暴露数据return {sum,increment,decrement}
}
useDog.ts
:
import {reactive,onMounted} from 'vue'
import axios,{AxiosError} from 'axios'export default function(){let dogList = reactive<string[]>([])// 方法async function getDog(){try {// 发请求let {data} = await axios.get('https://dog.ceo/api/breed/pembroke/images/random')// 维护数据dogList.push(data.message)} catch (error) {// 处理错误const err = <AxiosError>errorconsole.log(err.message)}}// 挂载钩子onMounted(()=>{getDog()})//向外部暴露数据return {dogList,getDog}
}
App.vue
:
<template><h2>当前求和为:{{sum}}</h2><button @click="increment">点我+1</button><button @click="decrement">点我-1</button><hr><img v-for="(u,index) in dogList.urlList" :key="index" :src="(u as string)"> <span v-show="dogList.isLoading">加载中......</span><br><button @click="getDog">再来一只狗</button>
</template><script lang="ts">import {defineComponent} from 'vue'export default defineComponent({name:'App',})
</script><script setup lang="ts">import useSum from './hooks/useSum'import useDog from './hooks/useDog'let {sum,increment,decrement} = useSum()let {dogList,getDog} = useDog()
</script>
是的,hook 可以帮助实现模块化开发,尤其在 Vue 3 中,结合 组合式 API (Composition API) 使用时,它极大地提高了代码的可复用性和模块化程度。
分析:
- 模块化:通过将数据获取和表单处理的逻辑分别提取到
useSum
和useDog
hook 中,逻辑更加清晰和模块化。 - 复用性:这两个 hook 可以在不同的组件中复用,而无需重复编写相同的代码。
- 解耦:不同的功能模块(如数据请求、表单验证等)被清晰地分离开来,组件只负责调用这些 hook,减少了组件内部的复杂度。
如果你能看到这里给你点个赞,如果对你有帮助的话不妨点赞支持一下~
参考:张天禹老师b站课程