computed与watch的区别

文章详细介绍了Vue.js中computed和watch的用法,包括它们在数据监听、属性影响、异步操作和缓存方面的差异。computed用于监听并计算多个数据,支持缓存,而watch更适合执行异步操作或开销较大的场景,且初始时不监听,可通过immediate属性改变此行为。

一、computed与watch

在之前的练习中本人碰到computed来监听某个数据变化。我们都知道computed与watch都是可以监听数据变化,但具体要怎么区别它们呢?

1.1 watch

1.1.1 watch的简单执行

<template>
  <div class="stylebg">
    <div class="mt22">
      watch的运用
    </div>
    <div>
      姓:<el-input v-model="firstName"></el-input>
    </div>
    <div>
      名:<el-input v-model="secondName"></el-input>
    </div>
    <div>
      {{fullName}}
    </div>
  </div>
</template>

<script>
export default {
  data(){
    return{
      firstName:'1',
      secondName:'2',
      fullName:''
    }
  },
  watch:{
     firstName(fValue){
       console.log('执行了watch')
       this.fullName = fValue + this.secondName
     },
    secondName(sValue){
      this.fullName = this.firstName + sValue
    },
  }
}
</script>

<style scoped>
  .mt22{
    margin:22px 0;
  }
</style>

运行截图
在这里插入图片描述

1.2.1 watch的特点

根据上述代码我们可以总结出watch以下5种特点:

  1. 监听一个数据,一个属性影响多个属性
  2. 不使用return返回
  3. 第一次加载不会监听watch里的属性名
  4. 支持异步操作,既可以监听到异步
  5. 不支持缓存

1.3.1 watch的首次加载

从上述1.2的代码运行中,可以得出我们第一次加载页面的时候,watch是不会监听watch里的属性名,那么有没有一种方法是可以接触这种限制呢?在1.3.1会进一步的说明

1.3.1.1 watch如何实现首次加载

我们把1.2的代码进一步地改进,需要在属性名里面定义immediate:true的相关属性,再把需要所赋的最新值,写入内部属性的方法handler( )中,即可在第一次加载监听watch的某个属性名。

<template>
  <div class="stylebg">
    <div class="mt22">
      watch的运用
    </div>
    <div>
      姓:<el-input v-model="firstName"></el-input>
    </div>
    <div>
      名:<el-input v-model="secondName"></el-input>
    </div>
    <div>
      {{fullName}}
    </div>
  </div>
</template>

<script>
export default {
  data(){
    return{
      firstName:'1',
      secondName:'2',
      fullName:'',
    }
  },
  watch:{
    firstName: {
        // 监听数据
      handler(fValue) {
          console.log('执行了watch')
          this.fullName = fValue + this.secondName
      },
      deep: true, // 深度监听
      immediate:true, //当 watch 一个变量的时候,初始化时并不会执行你需要在created的时候手动调用一次。添加immediate属性,这样初始化的时候也会触发
    },

    secondName(sValue){
      this.fullName = this.firstName + sValue
    }
  }

}
</script>

<style scoped>
  .mt22{
    margin:22px 0;
  }
</style>

运行截图
在这里插入图片描述

从运行截图中我们可以看出首次加载页面的时候会执行firstName属性名中的内部代码(从控制台打印出‘执行了watch’的字符串以及界面显示了fullname的字符串可以看出)

2.1 computed

2.1.1 computed的简单运行

<template>
  <div class="stylebg">
    <div class="mt22">
      computed的运用
    </div>
    <div>
      姓:<el-input v-model="firstName2"></el-input>
    </div>
    <div>
      名:<el-input v-model="secondName2"></el-input>
    </div>
    <div>
      {{fullName2}}
    </div>
  </div>
</template>

<script>
export default {
  data(){
    return{
      firstName2:'1',
      secondName2:'2',
      // fullName2:''
    }
  },
  computed:{
    fullName2(){
      console.log('执行了computed')
      return this.firstName2 + this.secondName2
    }
  },
}
</script>

<style scoped>
  .mt22{
    margin:22px 0;
  }
</style>

运行截图
在这里插入图片描述

2.2.1 computed的特点

根据上述代码我们可以总结出computed以下5种特点:

  1. 监听多个数据,多个属性影响一个属性
  2. 必须使用return返回
  3. 第一次加载会监听computed里的属性名
  4. 无法支持异步操作,既不能监听到异步操作里面的数据变化
  5. 支持缓存,可以避免每次取值时需要重新计算。

2.2.2 computed中设置属性名问题

在编写代码中遇到一个问题,因为computed在运行时是会return把计算的数值直接返回到变量fullName2中,而在data中又同时有fullName2的空字符串,执行代码的时候会发生错误,那么为什么会发生这种错误呢?主要是牵扯到属性名重命名的问题。

2.2.2.1 为什么在data与compted中的属性不能重定义,会报错?

在执行把数据挂在vm实例中会参考下述的vue源码

 	function initState (vm) {
       vm._watchers = [];
       var opts = vm.$options;
       if (opts.props) { initProps(vm, opts.props); }
       if (opts.methods) { initMethods(vm, opts.methods); }
       if (opts.data) {
         initData(vm);
       } else {
         observe(vm._data = {}, true /* asRootData */);
       }
       if (opts.computed) { initComputed(vm, opts.computed); }
       if (opts.watch && opts.watch !== nativeWatch) {
         initWatch(vm, opts.watch);
       }
     }

可以看出上述,代码会把props等通过方法挂载在vm实例上面,执行顺序为props→methods→data→computed→watch。所以若属性名相同的话,后面的值会把前面的值覆盖。在实际运行中会直接抛出属性名重命名的错误。
在书写代码需要避免属性名重复书写。

二、computed与watch的区别

在经过第一章computed与watch的运行介绍,可以列出它们的区别:

watchcomputed
监听一个数据,一个属性影响多个属性监听多个数据,多个属性影响一个属性
不使用return返回必须使用return返回
第一次加载不会监听watch里的属性名(未设置immediate:true情况下)第一次加载会监听computed里的属性名
支持异步操作,既可以监听到异步无法支持异步操作,既不能监听到异步操作里面的数据变化
不支持缓存支持缓存,可以避免每次取值时需要重新计算。

三、computed与watch一般会使用到的场景

watch: 如需要执行异步操作或者开销比较大的场景,搜索框输入一个字符串会调用API去动态获取相应的数据列表。
computed: 如购物车勾选多件商品,此时勾选每件商品的价钱属性影响总价的属性。

<think> 我们正在讨论Vue中的computedwatch。用户要求详细解释这两者的区别。根据提供的引用,我们可以总结如下: ### 1. **基本概念** - **computed**:计算属性,用于声明式地定义依赖其他属性的值。它会根据依赖关系进行缓存,只有在依赖发生变化时才会重新计算[^2][^4]。 - **watch**:侦听器,用于观察和响应Vue实例上的数据变动。当数据变化时,执行异步操作或开销较大的操作[^2][^4]。 ### 2. **核心区别** | 特性 | computed | watch | |--------------------|-----------------------------------|------------------------------------| | **缓存机制** | 有缓存,依赖不变则直接返回缓存值 | 无缓存,每次变化都会执行回调 | | **异步支持** | 不支持异步操作 | 支持异步操作 | | **写法要求** | 必须返回一个值(使用return) | 不需要返回值,可执行任意操作 | | **触发时机** | 依赖变化时重新计算 | 侦听的具体数据变化时触发 | | **配置选项** | 无 | 有`immediate`和`deep`等选项 | | **性能影响** | 高效(减少不必要的计算) | 可能影响性能(特别是深度监听) | ### 3. **原理分析** - **computed**:通过`ReactiveEffect`实现依赖收集,初始化时不会立即执行,而是在首次访问时计算并缓存结果。后续访问时若依赖未变则直接返回缓存值[^3]。 - **watch**:基于Vue的响应式系统,底层调用`doWatch`函数。可配置`immediate`立即执行或`deep`深度监听嵌套对象的变化[^3][^4]。 ### 4. **使用场景对比** - **computed适用场景**: - 一个属性受多个属性影响(如:商品总价 = 单价 × 数量)[^2][^4]。 - 数据格式化(如:拼接用户全名 `firstName + lastName`)[^5]。 - 需要缓存的复杂计算(如:过滤大型列表)[^4]。 - **watch适用场景**: - 数据变化需执行异步操作(如:搜索框输入变化后发起API请求)[^2][^4]。 - 数据变化影响多条逻辑(如:表单字段变化触发验证)[^2]。 - 监听引用类型的变化(需开启`deep:true`)[^4]。 ### 5. **代码示例** #### computed示例 ```javascript // 拼接全名 const fullName = computed(() => { return `${firstName.value} ${lastName.value}`; // 返回计算值 }); ``` #### watch示例 ```javascript // 监听搜索词变化并发起请求 watch(searchText, (newVal) => { fetchResults(newVal); // 可包含异步逻辑 }, { immediate: true }); // 立即执行一次 ``` #### 深度监听对象 ```javascript watch(userInfo, (newVal) => { console.log('用户信息变更:', newVal); }, { deep: true }); // 监听嵌套属性变化 ``` ### 6. **注意事项** - **computed避免副作用**:计算属性应纯函数化,不要在其中执行异步或修改外部状态[^4]。 - **watch性能优化**:监听大型对象时可使用具体路径替代`deep`,如`() => obj.prop`[^3]。 - **响应式依赖**:`computed`依赖必须是响应式数据(如`ref`或`reactive`),否则不会触发更新[^5]。 > **总结**:`computed`用于派生数据,`watch`用于响应变化。选择依据:**是否需要缓存?是否涉及异步?** [^2][^3]。 §§相关问题§§ 1. 为什么computed不支持异步操作而watch支持? 2. 如何在Vue3中实现computed的setter? 3. 深度监听(`deep:true`)可能引发哪些性能问题? [^1]: 在 Vue 3 的开发中,computedwatch 是两个非常实用的特性 [^2]: computedwatch区别 [^3]: Vue3 中 computed watch 的深度解析 [^4]: computedwatch应用场景 [^5]: Computedwatch </think> ### Vue 3 中 computed watch 的深度解析 #### 1. **核心定位本质差异** - **computed**:**声明式派生数据**[^3] 本质是**计算属性**,通过依赖关系自动推导新值(如:全名 = 姓氏 + 名字)[^5]。 示例: ```javascript const fullName = computed(() => `${firstName.value} ${lastName.value}`); ``` 输出始终是输入值的纯函数映射:$f(x,y) = x + y$[^1]。 - **watch**:**命令式响应逻辑**[^3] 本质是**侦听器**,观测数据变化后执行副作用操作(如:网络请求、DOM操作)。 示例: ```javascript watch(searchKeyword, (newVal) => fetchResults(newVal)); ``` #### 2. **关键特性对比** | 特性 | computed | watch | |---------------------|-----------------------------------|------------------------------------| | **缓存机制** | ✅ 依赖未变时返回缓存值[^2] | ❌ 每次变化都触发回调 | | **异步支持** | ❌ 必须同步返回结果[^2] | ✅ 支持异步操作(如axios请求)[^4] | | **返回值要求** | ✅ 必须有 return 语句[^2] | ❌ 无返回值要求 | | **配置选项** | ❌ 无 | ✅ `immediate`(立即执行)、`deep`(深度监听)[^4] | | **性能影响** | 高效(减少重复计算)[^4] | 开销大(需主动追踪变化) | #### 3. **数学描述差异** - **computed** 是纯函数: $$ \text{output} = f(\text{dep}_1, \text{dep}_2, ..., \text{dep}_n) $$ 其中输出**仅依赖输入**,无外部状态影响[^3]。 - **watch** 是过程式操作: $$ \Delta \text{data} \rightarrow \text{execute}( \text{callback} ) $$ 关注**数据变化量** $\Delta \text{data}$ 引发的动作[^3]。 #### 4. **最佳实践场景** | 场景描述 | 推荐方案 | 原因说明 | |---------------------------|----------------|--------------------------------------------------------------------------| | **派生显示数据** | computed [^4] | 如商品总价计算:$ \text{总价} = \sum (\text{单价} \times \text{数量}) $ | | **数据格式化** | computed [^4] | 如日期格式化:`computed(() => dayjs(date).format('YYYY-MM-DD'))` | | **异步任务(如API请求)** | watch [^2] | 监听搜索关键词变化后发起请求 | | **数据关联操作** | watch [^2] | 如表单字段变化时触发多字段联动验证 | | **深度监听对象变化** | watch + deep [^4] | 需观测嵌套对象内部属性修改时 | #### 5. **原理级差异** - **computed**: 基于Vue响应式系统的**依赖追踪**,建立属性间拓扑关系图: ```mermaid graph LR A[firstName] --> C[computed fullName] B[lastName] --> C ``` 仅当A或B变化时触发C重新计算[^3]。 - **watch**: 通过**主动订阅机制**观测数据: ```mermaid sequenceDiagram 数据源->>watcher: 发布变化 watcher->>回调函数: 执行指定操作 ``` #### 6. **典型错误用法** ```javascript // ❌ 错误:在computed中执行异步操作 const invalidComputed = computed(async () => { const res = await fetchData(); // 违反同步原则[^2] return res; }); // ✅ 正确:用watch处理异步 watch(data, async (newVal) => { const res = await fetchData(newVal); // 异步操作合法[^4] }); ``` > **核心定律**: > 需要**衍生新数据** → 选 `computed`;需要**响应数据变化执行操作** → 选 `watch`[^3][^4]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值