查看: 142|回复: 0

Vue3组合式API响应式与watch监听实践

[复制链接]
发表于 1 小时前 | 显示全部楼层 |阅读模式
Vue3 的组合式 API 更适合把同一功能的变量、计算属性、监听器和方法放在一起,避免 Vue2 选项式 API 中 data、computed、methods 按类型分散导致的维护成本。原文以搜索和排序为例说明:Vue2 要把搜索数据、排序数据分别塞进 data,把计算逻辑塞进 computed,把方法塞进 methods;Vue3 则可以把搜索模块和排序模块物理隔离,代码复用性更强。本文整理其中可直接落地的组合式 API 核心语法与响应式实践,路由、Pinia 等部分原文未给出完整代码,暂不展开。

一、环境与项目入口

先配置 Node/npm 环境,确认版本后在指定目录创建 Vue 项目,执行 npm install 安装依赖,再用本地编辑器打开。项目入口 index.html 中保留 id 为 app 的挂载点,并通过 module 脚本引入 src/main.ts。main.ts 的核心逻辑是引入 createApp 和根组件 App.vue,然后挂载到 #app。
  1. import { createApp } from 'vue'
  2. import App from './App.vue'
  3. createApp(App).mount('#app')
复制代码

App.vue 由 template、script、style 三部分组成,分别承载组件结构、逻辑和样式。原文示例在 script 中导出组件名称,在 style 中演示了颜色、背景、阴影、圆角和内边距等基础样式。

二、setup 与 script setup

setup 是 Vue3 新增配置项,值是一个函数。它会在组件实例化阶段之前执行,因此 setup 不能直接访问选项式 API 中的 data、methods 等数据;反过来,选项式 API 可以访问 setup 返回的数据。原文特别指出,setup 执行早于选项式数据初始化,这是两者访问方向不同的原因。

常规写法是在 export default 中声明 setup,把 ref 创建的响应式数据和修改方法返回出去。更常用的是 script setup 语法糖,在 script 标签上声明 setup 后,顶层变量和函数可直接在模板中使用,不需要显式 return。
  1. import { ref } from 'vue'
  2. const message = ref('Hello Vue 3!')
  3. const changeMessage = () => {
  4.   message.value = 'You clicked the button!'
  5. }
复制代码

三、ref 与 reactive 的选择

ref 用于创建响应式数据,适合数字、字符串、布尔值等原始类型,也可以通过 .value 访问对象、数组、函数等任意类型的真实值;在模板中会自动解包。reactive 用于声明对象响应式,适合把一组相关字段组织成对象。原文建议当前主流做法是二者并用,但以 ref 为主、reactive 为辅。
  1. import { ref, reactive, toRefs } from 'vue'
  2. type PersonInfo = {
  3.   name: string
  4.   age: number
  5.   tel: string
  6. }
  7. const personRef = ref<PersonInfo>({
  8.   name: '超哥',
  9.   age: 18,
  10.   tel: '13800138000'
  11. })
  12. const person = reactive<PersonInfo>({
  13.   name: '超哥',
  14.   age: 18,
  15.   tel: '13800138000'
  16. })
  17. const { name, age, tel } = toRefs(person)
复制代码

reactive 对象不能直接整体替换,需要借助 Object.assign 合并,否则会丢失响应式引用。toRefs 的作用是把 reactive 对象的每个属性转换成仍然保持响应式的 ref,从而可以安全解构;它只能作用于 reactive,不能用于普通对象,也不能直接用于 ref。toRef 则用于单独取某一个属性,例如 toRef(state, 'name')。

四、computed 的派生状态与缓存

computed 适合从已有响应式状态推导新值,具备派生状态、自动依赖追踪和缓存三个特征。以 count 推导 doubleCount 为例,只要 count.value 没变化,多次访问 doubleCount.value 都不会重新执行计算函数;count.value 改变后才会重新计算。这与普通函数每次调用都执行不同。

原文还演示了可写计算属性:通过 get 把 firstName 和 lastName 组合成 fullName,通过 set 把赋给 fullName 的新值拆回源数据。
  1. import { ref, computed } from 'vue'
  2. const firstName = ref('John')
  3. const lastName = ref('Doe')
  4. const fullName = computed({
  5.   get() {
  6.     return firstName.value + ' ' + lastName.value
  7.   },
  8.   set(newValue) {
  9.     const names = newValue.split(' ')
  10.     firstName.value = names[0]
  11.     lastName.value = names[names.length - 1]
  12.   }
  13. })
  14. fullName.value = 'Jane Smith'
复制代码

五、watch 的监视方式与常见误区

watch 用于监视数据变化,可监视 ref 定义的数据、函数返回的值、reactive 定义的数据,以及包含上述内容的数组。实际开发中,如果只想拿到某个具体属性的新旧值,优先写成 getter 函数,例如 () => person.value.age,这样性能更好,也能拿到准确 oldVal。
  1. import { ref, watch } from 'vue'
  2. const person = ref({ name: '超哥', age: 18 })
  3. const stopWatch = watch(
  4.   () => person.value.age,
  5.   (newAge, oldAge) => {
  6.     console.log(`年龄从 ${oldAge} 变成了 ${newAge}`)
  7.     if (newAge >= 40) {
  8.       stopWatch()
  9.     }
  10.   }
  11. )
复制代码

如果直接 watch 一个 ref 对象,默认是浅监视,修改对象内部属性不会触发;需要手动开启 { deep: true }。但即使开启深度监视,newVal 和 oldVal 仍然是同一个对象引用,所以 newVal === oldVal 为 true。原文用房子地址作比喻:监视的是地址,内部装修变了,地址没变,回调里读到的已经是修改后的值。

reactive 对象的监视相当于自动开启深度监视,修改第一层属性或深层嵌套属性都能触发,不需要额外加 deep。监视多个属性时,把 getter 放进数组,回调参数也按数组解构。
  1. watch(
  2.   [() => person.value.name, () => person.value.age],
  3.   ([newName, newAge], [oldName, oldAge]) => {
  4.     console.log([[newName, newAge], [oldName, oldAge]])
  5.   }
  6. )
复制代码

watch 会返回一个停止监听的函数,调用它即可取消监听。原文示例在年龄达到 40 时执行 stopWatch(),停止后续回调。

六、watchEffect 的自动依赖追踪

watchEffect 会立即执行一次传入函数,并在执行过程中自动追踪用到的响应式依赖,依赖变化后重新执行。它不需要显式告诉 Vue 监视谁,适合逻辑中自然读取多个响应式变量的场景。
  1. import { ref, watchEffect } from 'vue'
  2. const water = ref(20)
  3. const pressure = ref(100)
  4. watchEffect(() => {
  5.   if (water.value > 60 || pressure.value > 200) {
  6.     console.log('警告:锅炉异常!')
  7.     console.log(`当前状态:水温${water.value},水压${pressure.value}`)
  8.   }
  9. })
复制代码

七、标签 ref、Props 与生命周期

标签 ref 用于引用 DOM 元素或组件实例。在 script setup 中创建 ref 并与模板中的 ref 绑定后,可以通过 .value 获取 DOM 引用,例如读取元素尺寸或输出 DOM 信息。

Props 是组件对外暴露的输入接口,用于父组件向子组件传递数据,体现 Vue 的单向数据流:数据拥有者负责修改,其他组件通过约定方式请求修改。子组件可用 defineProps 接收并约束类型。
  1. const x = defineProps<{ a: string }>()
  2. console.log(x)
复制代码

生命周期方面,script setup 本身相当于创建阶段。挂载阶段常用 onBeforeMount 和 onMounted,DOM 操作和请求通常放在 onMounted;更新阶段有 onBeforeUpdate 和 onUpdated;销毁阶段有 onBeforeUnmount 和 onUnmounted,适合清除定时器、解绑事件监听。父子组件顺序可记为:加载渲染时父先开始、子先结束;更新时父先发现、子先更新;销毁时父先动手、子先消失。

八、Hooks 的抽取时机

Hooks 是把某个功能的变量和方法单独定义在 ts 文件中,需要时直接导入,避免多个组件重复声明同一套逻辑。原文建议,当发现两个组件写了类似代码时,就是创建 Hooks 的最佳时机。由于原文该部分代码展示不完整,这里只保留其设计思路:先按功能聚合逻辑,再抽离为可复用模块,最后在组件中导入调用。
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 注册

本版积分规则

指导单位

江苏省公安厅

江苏省通信管理局

浙江省台州刑侦支队

DEFCON GROUP 86025

Hacking Group 021A

旗下站点

态势感知中心

应急响应中心

红盟安全

联系我们

官方QQ群:112851260

官方邮箱:security#ihonker.org(#改成@)

官方核心成员

关注微信公众号

Archiver|手机版|小黑屋| ( 沪ICP备2021026908号 )

GMT+8, 2026-10-24 13:13 , Processed in 0.024307 second(s), 28 queries , Gzip On.

Powered by ihonker.com

Copyright © 2015-现在.

  • 返回顶部