Vue3中的computed 与 watch 的区别

0 阅读3分钟

Vue3中的computed 与 watch 的区别

  • 当你想要合成一个新数据用来显示时,选 computed
  • 当你想要在数据变化时搞点事情(调接口、存本地、操作 DOM),选 watch

computed:计算属性

computed 主要用于数据的衍生与转换。它基于现有的响应式数据计算出一个新值,并且会自动缓存计算结果。只有当它依赖的数据源发生变化时,才会重新计算。

// 写法 1:函数形式(最常用,只读)
computed: {
  计算属性名() {
    // 依赖 this.xxx 数据
    return 计算结果;
  }
}

// 写法 2:对象形式(可读可写)
computed: {
  计算属性名: {
    get() { 
      return 计算结果; 
    },
    set(val) { 
      // 当你尝试修改这个计算属性时触发
    }
  }
}

应用场景:购物车总价

<template>
  <div>
    <h2>购物车</h2>
    <ul>
      <li v-for="(item, index) in list" :key="index">
        {{ item.name }} - ¥{{ item.price }} x 
        <button @click="item.count > 1 ? item.count-- : ''">-</button>
        {{ item.count }}
        <button @click="item.count++">+</button>
      </li>
    </ul>
    <!-- 这里直接使用计算属性 -->
    <p>总价:¥{{ totalPrice }}</p>
  </div>
</template>

<script>
export default {
  data() {
    return {
      list: [
        { name: '苹果', price: 10, count: 2 },
        { name: '香蕉', price: 5, count: 3 }
      ]
    };
  },
  computed: {
    // 只要 list 里任何商品的 price 或 count 变了,totalPrice 自动更新
    totalPrice() {
      console.log('computed 重新计算了'); // 验证缓存机制
      return this.list.reduce((sum, item) => sum + item.price * item.count, 0);
    }
  }
};
</script>

为什么用 computed 而不用 methods? 如果你在 methods 里定义一个 getTotalPrice() 函数,虽然也能算出结果,但 computed缓存。在上面的例子中,如果你在页面多处调用 totalPrice,computed 只会计算一次;而 methods 每调用一次就会执行一次函数,性能更低。


watch:侦听器

watch 主要用于观察某个数据的变化并执行特定的副作用。它关注的是 “数据变了之后要做什么”,而不是为了得到一个返回值。这里的 “副作用” 通常指:异步请求、DOM 操作、数据持久化等。

watch: {
  // 写法 1:简单监听
  数据名(newVal, oldVal) {
    // newVal 是新值,oldVal 是旧值
    // 执行逻辑...
  },
  
  // 写法 2:对象形式(开启深度监听、立即执行)
  数据名: {
    handler(newVal, oldVal) {
      // 逻辑...
    },
    deep: true,    // 深度监听:如果监听的是对象,内部属性变化也能触发
    immediate: true // 立即执行:页面加载完就先执行一次 handler
  }
}

搜索框防抖 :当用户输入停止后,延迟 500ms 再发送请求。

<template>
  <div>
    <h2>搜索电影</h2>
    <input type="text" v-model="searchText" placeholder="请输入关键词..." />
    <p v-if="loading">搜索中...</p>
    <ul v-else>
      <li v-for="movie in movies" :key="movie.id">{{ movie.title }}</li>
    </ul>
  </div>
</template>

<script>
export default {
  data() {
    return {
      searchText: '',
      movies: [],
      loading: false,
      timer: null
    };
  },
  watch: {
    // 监听 searchText 的变化
    searchText(newVal) {
      // 1. 清除上一次的定时器
      if (this.timer) clearTimeout(this.timer);
      
      // 2. 如果输入为空,清空列表直接返回
      if (!newVal) {
        this.movies = [];
        return;
      }

      this.loading = true;
      
      // 3. 设置新的定时器(防抖核心)
      this.timer = setTimeout(() => {
        this.fetchMovies(newVal);
      }, 500);
    }
  },
  methods: {
    fetchMovies(keyword) {
      // 模拟 API 请求
      console.log(`发送请求,关键词:${keyword}`);
      setTimeout(() => {
        this.movies = [
          { id: 1, title: keyword + '之传奇诞生' },
          { id: 2, title: keyword + '之王者归来' }
        ];
        this.loading = false;
      }, 1000);
    }
  }
};
</script>

为什么这里必须用 watch? 因为我们需要在数据变化后执行 “延迟” 和 “发请求” 这些异步操作,computed 内部必须是同步的 return,无法处理这种场景。

维度computedwatch
返回值必须有 return不需要 return
缓存有缓存,性能优先无缓存,变了就执行
异步不支持异步支持异步操作
场景多对一:一个值由多个值计算而来 (A+B=C)一对多:一个值变化影响多个逻辑 (A 变了,触发 B、C、D)