计算属性与监听器

计算属性

计算属性是基于它们的依赖进行缓存的属性,只有当依赖发生变化时,才会重新计算。

选项式 API 计算属性

export default {
  data() {
    return {
      message: 'Hello',
      firstName: 'John',
      lastName: 'Doe'
    }
  },
  computed: {
    // 基本计算属性
    reversedMessage() {
      return this.message.split('').reverse().join('')
    },
    
    // 完整写法,包含 getter 和 setter
    fullName: {
      get() {
        return `${this.firstName} ${this.lastName}`
      },
      set(newValue) {
        const [firstName, lastName] = newValue.split(' ')
        this.firstName = firstName
        this.lastName = lastName
      }
    }
  }
}

组合式 API 计算属性

import { ref, computed } from 'vue'

export default {
  setup() {
    const message = ref('Hello')
    const firstName = ref('John')
    const lastName = ref('Doe')
    
    // 基本计算属性
    const reversedMessage = computed(() => {
      return message.value.split('').reverse().join('')
    })
    
    // 完整写法,包含 getter 和 setter
    const fullName = computed({
      get() {
        return `${firstName.value} ${lastName.value}`
      },
      set(newValue) {
        const [firstNameValue, lastNameValue] = newValue.split(' ')
        firstName.value = firstNameValue
        lastName.value = lastNameValue
      }
    })
    
    return {
      message,
      firstName,
      lastName,
      reversedMessage,
      fullName
    }
  }
}

监听器

监听器用于监听数据变化并执行相应的副作用,如发送网络请求、更新 DOM 等。

选项式 API 监听器

export default {
  data() {
    return {
      count: 0,
      user: {
        name: 'John',
        age: 30
      }
    }
  },
  watch: {
    // 基本监听
    count(newValue, oldValue) {
      console.log(`count 从 ${oldValue} 变为 ${newValue}`)
    },
    
    // 深度监听
    user: {
      handler(newValue, oldValue) {
        console.log('user 对象发生变化', newValue, oldValue)
      },
      deep: true
    },
    
    // 立即执行
    count: {
      handler(newValue, oldValue) {
        console.log(`count: ${newValue}`)
      },
      immediate: true
    }
  }
}

组合式 API 监听器

import { ref, reactive, watch } from 'vue'

export default {
  setup() {
    const count = ref(0)
    const user = reactive({
      name: 'John',
      age: 30
    })
    
    // 监听 ref
    watch(count, (newValue, oldValue) => {
      console.log(`count 从 ${oldValue} 变为 ${newValue}`)
    })
    
    // 监听 reactive 对象
    watch(user, (newValue, oldValue) => {
      console.log('user 对象发生变化', newValue, oldValue)
    }, { deep: true })
    
    // 监听多个源
    watch([count, () => user.age], ([newCount, newAge], [oldCount, oldAge]) => {
      console.log(`count: ${newCount}, age: ${newAge}`)
    })
    
    // 立即执行
    watch(count, (newValue) => {
      console.log(`count: ${newValue}`)
    }, { immediate: true })
    
    return {
      count,
      user
    }
  }
}

watchEffect - 自动追踪依赖

import { ref, reactive, watchEffect } from 'vue'

export default {
  setup() {
    const count = ref(0)
    const user = reactive({
      name: 'John',
      age: 30
    })
    
    // 自动追踪所有依赖
    const stop = watchEffect(() => {
      console.log(`count: ${count.value}, name: ${user.name}`)
    })
    
    // 停止监听
    // stop()
    
    return {
      count,
      user
    }
  }
}

计算属性 vs 方法

特性 计算属性 方法
缓存 基于依赖缓存,只有依赖变化才重新计算 每次调用都会重新执行
语法 作为属性使用,无需括号 作为函数调用,需要括号
适用场景 基于现有数据派生新数据 执行复杂逻辑或异步操作

计算属性 vs 监听器

特性 计算属性 监听器
用途 基于依赖派生新数据 监听数据变化执行副作用
缓存 有缓存机制 无缓存机制
语法 声明式 命令式
适用场景 数据转换、过滤、格式化 异步操作、DOM 更新、网络请求

最佳实践

  • 优先使用计算属性处理数据派生,因为它有缓存机制,性能更好
  • 使用监听器处理副作用,如发送网络请求、更新 DOM 等
  • 对于简单的计算,使用方法可能更直观
  • 使用 watchEffect 可以自动追踪依赖,减少代码量
  • 避免在计算属性中执行异步操作或修改其他状态