Vue3 中watch和computed

Vue 3 中 computedwatch 深度解析

在 Vue 3 组合中,响应式工具的类型安全使用至关重要。以下是详细说明

一、watch 侦听器

1. 基础类型监听
<template>
  <div>实际参数1={{count}}div>
  <div>
    <button @click="count++">点击button>
  div>
template>

<script setup lang="ts">
import {
  reactive,
  ref,
  watch
} from "vue";

const count = ref<number>(0)
const state = reactive({items: [] as string[]});


watch(count, (newVal:number, oldVal:number) => {
  state.items.push(String(count.value))
  console.log('newVal, oldVal === ', newVal, oldVal)
})

watch(() => state.items, () => {
  console.log('state.items ===', state.items)
},{deep: true})

script>

2. 多源监听
<template>
  <div>实际参数1={{count}}div>
  <div>
    <button @click="count++">点击button>
  div>
template>

<script setup lang="ts">
import {
  reactive,
  ref,
  watch
} from "vue";

const count = ref<number>(0)
const state = reactive({items: [] as string[]});


watch(count, (newVal:number, oldVal:number) => {
  state.items.push(String(count.value))
  console.log('newVal, oldVal === ', newVal, oldVal)
})

watch(() => state.items, () => {
  console.log('state.items ===', state.items)
},{deep: true})

watch([count, state], ([newCount, newState]:[number, object], [oldCount, oldState]:[number, object])=> {
  console.log('[newCount, newState], [oldCount, oldState] =', newCount, newState, oldCount, oldState)
})
script>
3. 深度监听对象
<template>
  <div>实际参数1={{product}}div>
  <div>
    <button @click="product.price++">点击button>
  div>
template>

<script setup lang="ts">
import {
  reactive,
  watch
} from "vue";
interface Product {
  id: number,
  price: number,
  name: string,
  specs: {
    color: string,
    weight: number
  }
}

const product = reactive<Product>({
  id: 1,
  price: 131,
  name: 'Bwm',
  specs: {
    color: 'red',
    weight: 80
  }
})

watch(() => product.price, // 创建新引用触发深度监听
  (newProduct,oldProduct) => {
    console.log('newVal,oldVal === ', newProduct,oldProduct)
  },{deep: true})
script>


二、watchEffect 高级用法

watchEffect() 允许我们自动跟踪回调的响应式依赖,且会立即执行。

1. 基本用法
<template>
  <div>实际参数1={{product}}div>
  <div>
    <button @click="product.price++">点击button>
  div>
template>

<script setup lang="ts">
import {
  reactive,
  ref,
  watch, watchEffect
} from "vue";
interface Product {
  id: number,
  price: number,
  name: string,
  specs: {
    color: string,
    weight: number
  }
}

const product = reactive<Product>({
  id: 1,
  price: 131,
  name: 'Bwm',
  specs: {
    color: 'red',
    weight: 80
  }
})
const totalPrice = ref<number>(0)

watchEffect(()=> {
  totalPrice.value = product.price + 2
  console.log('totalPrice === ', totalPrice)
})
script>

2. 清理副作用
<template>
  <div>实际参数1={{product}}div>
  <div>
    <button @click="product.price+=10">点击button>
  div>
template>

<script setup lang="ts">
import {
  reactive,
  ref,
  watchEffect,
  onWatcherCleanup
} from "vue";

interface Product {
  id: number,
  price: number,
  name: string,
  specs: {
    color: string,
    weight: number
  }
}

const product = reactive<Product>({
  id: 1,
  price: 131,
  name: 'Bwm',
  specs: {
    color: 'red',
    weight: 80
  }
})
const totalPrice = ref<number>(0)

watchEffect(()=> {
  totalPrice.value = product.price + 2
  console.log('totalPrice === ', totalPrice)
  onWatcherCleanup(() => {
    console.log('onWatcherCleanup ===')
  })
})
script>

三、computed 计算属性

1. 对象类型计算
<template>
  <div>实际参数1={{totalPrice}}</div>
  <div>
    <button @click="product.price+=10">点击</button>
  </div>
</template>

<script setup lang="ts">
import {
  reactive,
  ref,
  computed
} from "vue";
interface Product {
  id: number,
  price: number,
  name: string,
  specs: {
    color: string,
    weight: number
  }
}

const product = reactive<Product>({
  id: 1,
  price: 131,
  name: 'Bwm',
  specs: {
    color: 'red',
    weight: 80
  }
})
const totalPrice = computed<number>(()=>product.price += 10)
</script>

2. 可写计算属性
<template>
  <div>{{fullName}}div>
  <div>姓名: <el-input v-model="fullName"/>div>
template>

<script setup lang="ts">
import {
  reactive,
  ref,
  computed
} from "vue";
const firstName = ref<string>('Jane')
const lastName = ref<string>('Smith')

const fullName = computed<string>({
  get() {
    return `${firstName.value} ${lastName.value}`
  },
  set(fullName:string) {
    const [newFirstName, newLastName] = fullName.split(' ')
    firstName.value = newFirstName
    lastName.value = newLastName
  }
})
script>

四、computed vs watch vs watchEffect 对比

特性 computed watch watchEffect
目的 派生值 响应变化执行操作 自动追踪依赖执行操作
返回值 ref对象 停止函数 停止函数
初始化执行 立即计算 可配置(immediate) 立即执行
依赖声明 自动 显式指定 自动
缓存
异步支持
清理机制
调试钩子
适合场景 数据转换/格式化 精确控制的操作 自动追踪依赖的副作用

总结

  1. computed

    • 用于派生状态
    • 具有缓存机制
    • 适合数据转换和格式化
    • 模板中优先使用
  2. watch

    • 用于执行副作用
    • 提供精确控制
    • 适合异步操作、DOM操作
    • 需要显式声明依赖
  3. watchEffect

    • 自动追踪依赖
    • 立即执行
    • 适合多个依赖的简单副作用
    • 提供清理机制

黄金法则

  • 需要派生值 → 用 computed
  • 需要响应变化执行操作 → 用 watchwatchEffect
  • 需要精确控制依赖 → 用 watch
  • 需要自动追踪多个依赖 → 用 watchEffect
  • 避免computed 中产生副作用
  • 总是在副作用中清理资源

通过合理选择和使用这些 API,可以构建出高效、可维护的 Vue 3 应用程序。

你可能感兴趣的:(Vue3,typescript,前端,vue.js,javascript,前端)