目录
[get () ------ 读取](#get () —— 读取)
[set () ------ 修改](#set () —— 修改)
[watch情况一 ------ 监视ref定义的基本类型数据](#watch情况一 —— 监视ref定义的基本类型数据)
[watch情况二 ------ 监视ref定义的对象类型数据](#watch情况二 —— 监视ref定义的对象类型数据)
[watch情况三 ------ 监视reactive定义的对象类型数据](#watch情况三 —— 监视reactive定义的对象类型数据)
[watch情况四 ------ 监视ref或reactive定义的对象类型数据的某一个数据](#watch情况四 —— 监视ref或reactive定义的对象类型数据的某一个数据)
[watch情况五 ------ 监视上四种情况的多个数据](#watch情况五 —— 监视上四种情况的多个数据)
[Vue3 的生命周期](#Vue3 的生命周期)
[自定义 Hook 命名规则](#自定义 Hook 命名规则)
对上一篇博客的语法补充
computed
作用: 根据已有数据计算出新数据
核心特点
- 依赖数据变,它就自动变
- 姓 / 名一改,全名自动更新
- 有缓存,不重复计算
- 数据没变时,直接用上次结果,性能高
- 像变量一样使用
- 直接写
{``{ fullName }},不用加括号调用- 可以只可读,也可以可读可写
python<template> <div class="person"> 姓:<input type="text" v-model="firstName" /> <br> 名:<input type="text" v-model="lastName" /> <br> 全名:<span>{{ fullName }}</span> <br> <button @click="changeFullName">全名改为:li-si</button> </div> </template> <script setup lang="ts"> import { ref, computed } from 'vue' const firstName = ref('zhang') const lastName = ref('san') const fullName = computed({ get() { return firstName.value + '-' + lastName.value }, set(val) { let [f, l] = val.split('-') firstName.value = f || '' lastName.value = l || '' } }) function changeFullName() { fullName.value = 'li-si' } </script>get () ------ 读取
- 页面显示
fullName时- 依赖数据(firstName/lastName)变化时
触发get()并返回计算好的全名
set () ------ 修改
手动给**
fullName赋值** 时触发set(),把新值拆回去,更新原来的姓和名原始数据
手动改动名字,跟着改变
自动改名(点击按钮)
watch
- 作用:监视数据的变化
- 特点: Vue3 中的 watch 只能监视以下四种数据:
- ref 定义的数据
- reactive 定义的数据
- 函数返回一个值**(** getter 函数)
- 一个包含上述内容的数组
watch情况一 ------ 监视ref定义的基本类型数据
语法格式:
watch(要监听的数据,回调函数)直接写数据名,监视value值变化
回调函数:
newValue:修改后的新值oldValue:修改前的旧值
pythonlet sum = ref(0) // 方法 function changeSum(){ sum.value += 1 } // 监视情况一 const stopWatch = watch(sum,(newValue,oldValue)=>{ console.log('sum变化了',newValue,oldValue) if(newValue >= 10){ stopWatch() } })
watch调用后返回一个停止函数 ,接收存到变量stopWatch; 调用stopWatch()即可取消监听- 数值≥10 之后,再点击按钮不再触发 watch 打印
watch情况二 ------ 监视ref定义的对象类型数据
直接写数据名,监视的是对象的地址值
若想监视对象内部的数据,要手动开启深度监视
需要注意的是若修改的是 ref 定义的对象中的属性, newValue 和 oldValue 都是新值,因为它们是同一个对象
若修改整个 ref 定义的对象, newValue 是新值, oldValue 是旧值,因为不是同一个对象了
pythonconst person = ref({ name: '张三', age: 18 }) // 修改姓名 function changeName() { person.value.name += '~' } // 修改年龄 function changeAge() { person.value.age += 1 } // 直接替换整个对象 function changePerson() { person.value = { name: '李四', age: 90 } } // 监视情况二 // ref存对象默认只监听地址;改内部属性需开启deep深度监听 watch(person, (newVal, oldVal) => { console.log('person变化了', newVal, oldVal) }, { deep: true })
ref包裹对象,直接改属性 (name/age) 不会触发 watch,加deep:true开启深度监听- 直接赋值**
person.value = {}**替换对象地址,不加 deep 也能触发监听watch情况三 ------ 监视reactive定义的对象类型数据
默认开启了深度监视
python// reactive对象,watch默认自带深度监听 const person = reactive({ name: '张三', age: 18 }) // 多层嵌套对象 const obj = reactive({ a: { b: { c: 666 } } }) // 方法 function changeName() { person.name += '~' } function changeAge() { person.age += 1 } function changePerson() { Object.assign(person, { name: '李四', age: 80 }) } function test() { obj.a.b.c = 888 } // 监视情况三 // 直接监听reactive对象:默认开启深度监听,修改任意层级属性均可触发watch,无需deep:true watch(person, (newVal, oldVal) => { console.log('person变化了', newVal, oldVal) }) watch(obj, (newVal, oldVal) => { console.log('Obj变化了', newVal, oldVal) })监听对象单个属性规则:
- 属性为基础类型:监听源必须写成
()=>person.name函数形式- 属性为子对象:推荐统一写成函数;想监听子对象内部再加**
deep:true**- 整对象直接监听:
reactive默认深度,ref对象需手动**deep:true**watch情况四 ------ 监视ref或reactive定义的对象类型数据的某一个数据
注意点如下:
- 若该属性值不是****对象类型,需要写成函数形式。
- 若该属性值是依然****是对象类型,需要关注对象内部(可直接编,也可写成函数,建议写成函数 ),需要手动开启深度监视
python// 响应式对象(包含嵌套对象 car) const person = reactive({ name: '张三', age: 18, car: { c1: '奔驰', c2: '宝马' } }) function changeName() { person.name += '~' } function changeAge() { person.age += 1 } function changeC1() { person.car.c1 = '奥迪' } function changeC2() { person.car.c2 = '大众' } function changeCar() { person.car = { c1: '雅迪', c2: '爱玛' } } // 监视情况四 // 监听对象内的基础类型属性:必须写成函数 // watch(() => person.name, (newVal, oldVal) => {}) // 监听对象内的对象类型属性:推荐写成函数 + deep:true watch(() => person.car, (newVal, oldVal) => { console.log('car 变化了', newVal, oldVal) }, { deep: true })监听对象里的普通属性 → 必须写成函数:
javascriptwatch(() => person.name, () => {})监听对象里的子对象
- 必须写成函数
- 想监听内部属性变化(c1/c2)→ 必须加
deep: truewatch情况五 ------ 监视上四种情况的多个数据
pythonconst person = reactive({ name: '张三', age: 18, car: { c1: '奔驰', c2: '宝马' } }) // 修改方法 function changeName() { person.name += '~' } function changeAge() { person.age += 1 } function changeC1() { person.car.c1 = '奥迪' } function changeC2() { person.car.c2 = '大众' } function changeCar() { person.car = { c1: '雅迪', c2: '爱玛' } } // 情况五:同时监听多个数据 watch([() => person.name, person.car], (newVal, oldVal) => { console.log('数据变化了', newVal, oldVal) }, { deep: true })同时监听
name和car,任意一个变化都会触发监听
watchEffect
立即运行一个函数,同时响应式地追踪其依赖,并在依赖更改时重新执行该函数
与watch相比
- 都能监听响应式数据的变化,不同的是监听数据变化的方式不同
- watch :要明确指出监视的数据
3. watchEffect :不用明确指出监视的数据(函数中用到哪些属性,那就监视哪些属
性)
pythonconst temp = ref(0) const height = ref(0) function changePrice() { temp.value += 10 } function changeSum() { height.value += 1 } // 监视情况五 // watch:需明确指定监听的数据 watch([temp, height], (value) => { const [newTemp, newHeight] = value if (newTemp >= 50 || newHeight >= 20) { console.log('联系服务器') } }) // watchEffect:自动追踪依赖,无需指定监听数据 const stopWatch = watchEffect(() => { if (temp.value >= 50 || height.value >= 20) { console.log('联系服务器') } // 满足条件后停止监听 if (temp.value === 100 || height.value === 50) { stopWatch() } })总的来说
- watch:手动指定监听谁
- watchEffect:自动识别用到的响应式数据,代码更简洁
props
props = 父组件给子组件传数据的通道
- 父组件:把数据传出去
- 子组件:用 props 接收
父组件
python<template> <!-- 向子组件传参 --> <Child :name="name" :age="age" :car="car" /> </template> <script setup lang="ts"> import Child from './Child.vue' const name = '张三' const age = 18 const car = { c1: '奔驰', c2: '宝马' } </script>子组件
python<template> <div> {{ props.name }}|{{ props.age }}|{{ props.car.c1 }} </div> </template> <script setup lang="ts"> import { withDefaults } from 'vue' // =====写法1:数组写法 // const props = defineProps(['name','age','car']) // =====写法2:对象写法 /* const props = defineProps({ name: { type: String, required: true }, age: { type: Number, default: 1 }, car: { type: Object, default: () => ({c1:'雅迪',c2:'爱玛'}) } }) */ // =====写法3:TS泛型+withDefaults(这个是vue常用) interface User { name: string age?: number car?: { c1: string; c2: string } } const props = withDefaults(defineProps<User>(), { age: 1, car: () => ({ c1: '雅迪', c2: '爱玛' }) }) </script>三种用法总结
- 数组:只接收参数,不能限制类型、默认值,临时快速开发用
- 对象 :JS 项目标配,可配置
type/required/default,对象默认值必须函数返回- withDefaults + 泛型:TS 项目,精准类型约束 + 默认值,企业最常用
生命周期
Vue 组件实例在创建时要经历一系列的初始化步骤 ,在此之前,调用特定的函数 ,让开发者有机会在特定阶段运行自己的代码,这些特定的函数就是生命周期 钩子
- 生命周期整体分为四个阶段,分别是:创建、挂载、更新、销毁 ,每个阶段都有两个钩子,一前一后
Vue3 的生命周期
- 创建阶段: setup
- 挂载阶段: onBeforeMount 、 onMounted
- 更新阶段: onBeforeUpdate 、 onUpdated
- 卸载阶段: onBeforeUnmount 、 onUnmounted
- 常用的钩子: onMounted (挂载完毕)、 onUpdated (更新完毕)、 onBeforeUnmount (卸载之前)
钩子示例代码
python<template> <div class="person"> <h2>当前求和为:{{ sum }}</h2> <button @click="changeSum">点我 sum+1</button> </div> </template> <script setup lang="ts"> import { ref, onBeforeMount, onMounted, onBeforeUpdate, onUpdated, onBeforeUnmount, onUnmounted } from 'vue' // 响应式数据 const sum = ref(0) // 方法 function changeSum() { sum.value += 1 } console.log('setup') // 生命周期钩子 onBeforeMount(() => { console.log('挂载之前') }) onMounted(() => { console.log('挂载完毕') }) onBeforeUpdate(() => { console.log('更新之前') }) onUpdated(() => { console.log('更新完毕') }) onBeforeUnmount(() => { console.log('卸载之前') }) onUnmounted(() => { console.log('卸载完毕') }) </script>
自定义hook
一个普通函数,里面可以用 Vue3 的响应式、生命周期、watch 等所有 API
优势:
- 复用代码
- 让组件更干净
- 逻辑单独维护
使用流程
首先新建一个hook的ts文件
TypeScript// 自定义 Hook:useSum import { ref } from 'vue' export default function useSum() { // 数据 const sum = ref(0) // 方法 const addSum = () => { sum.value++ } // 把数据和方法 return 出去,给组件用 return { sum, addSum } }然后在组件里使用
TypeScript<template> <div> <h2>求和:{{ sum }}</h2> <button @click="addSum">+1</button> </div> </template> <script setup lang="ts"> // 引入自定义 hook import useSum from '@/hooks/useSum' // 直接使用 const { sum, addSum } = useSum() </script>自定义 Hook 命名规则
必须以 use 开头
- useSum
- useMouse
- useRequest
- useScroll
下一篇介绍一下vue的两个生态以及组件之间的连接方式


