vue中computed和watch

本文详细介绍了Vue.js中的计算属性(computed)和侦听器(watch)的功能及使用方法。计算属性用于根据已有数据生成新的计算结果,并支持缓存,仅在依赖数据变化时重新计算。侦听器则用于监听属性变化并执行相应操作,支持异步处理。
computed(计算属性):

        作用:能够避免数据冗余,通过计算,可以在已有的data数据中生成新的变量,计算属性的变量将会被缓存

        触发时机:当依赖的响应式属性变化才会重新计算
示例:

<div id="app">{{fullName}}</div>

var vm = new Vue({
   el:'#app',
   data:{
     fristName:'zhang'
     lastName:'san'
  },
   computed:{
     // 仅读取
     fullName () {
      return this.fristName + ' ' + lastNmae
     },
     setName :{
    //读取和设置(getter、setter)
    get () {
         return this.fristNmae + ' ' lastName
    }
    set (v) {
      this.lastName = v + lastName
    }  
     }
  }
})
watch(侦听器)

        作用:监听属性的变化

        触发时机:当属性发生变化时触发(可以配合v-model等指令使用)

例:

     <h1>{{message}}</h1>
        <input type="text" v-model="message">
        <h1>{{person.name}}</h1>
        <input type="text" v-model="person.name">
  </div>

const app= new Vue({
            el : "#app",
            data : {
                message : "今天天气不错",
                person : {
                    name : "张三"
                }
            },

            watch : {
                // 参数1 : 修改后的数据
                // 参数2 : 修改前的数据
                message(nV, oV) {
                    // console.log(nV, oV);

                    console.log(this.message);
                },

                // person : {
                //     handler : function(nV, oV) {
                //         console.log(nV);
                //         console.log(oV);
                //     },
                //     deep : true
                // },

                "person.name"(nV, oV) {
                    console.log(nV);
                    console.log(oV);
                }
            }
        })

总结

计算属性

  • 支持缓存,只有依赖数据发生改变,才会重新进行计算
  • 不支持异步,当computed内有异步操作时无效,无法监听数据的变化
  • computed 属性值会默认走缓存,计算属性是基于它们的响应式依赖进行缓存的,也就是基于data中声明过或者父组件传递的props中的数据通过计算得到的值
  • 如果一个属性是由其他属性计算而来的,这个属性依赖其他属性,是一个多对一或者一对一,一般用computed
  • 如果computed属性属性值是函数,那么默认会走get方法;函数的返回值就是属性的属性值;在computed中的,属性都有一个get和一个set方法,当数据变化时,调用set方法

侦听器

  • 不支持缓存,数据变,直接会触发相应的操作;
  • watch支持异步;
  • 监听的函数接收两个参数,第一个参数是最新的值;第二个参数是输入之前的值;
  • 当一个属性发生变化时,需要执行对应的操作;一对多;
  • 监听数据必须是data中声明过或者父组件传递过来的props中的数据,当数据变化时,触发其他操作,函数有两个参数,
      immediate:组件加载立即触发回调函数执行,
      deep: 深度监听,为了发现对象内部值的变化,复杂类型的数据时使用,例如数组中的对象内容的改变,注意监听数组的变动不需要这么做。注意:deep无法监听到数组的变动和对象的新增,参考vue数组变异,只有以响应式的方式触发才会被监听到。
<think>我们正在讨论Vuecomputedwatch的原理。根据之前的引用,我们可以总结如下: 1. **计算属性(computed)原理**: - 计算属性是基于它们的依赖进行缓存的。只有在依赖发生变化时,才会重新计算。 - 在Vue内部,计算属性是通过Watcher来实现的,每个计算属性都有一个对应的Watcher实例。 - 这个Watcher实例在初始化时会将计算属性的getter函数作为自己的getter,并且设置一个lazy标志(表示惰性计算)。 - 当首次访问计算属性时,会执行getter函数并将结果缓存起来。当依赖发生变化时,Watcher会被标记为dirty,下次访问计算属性时会重新计算。 2. **监听属性(watch)原理**: - watch用于观察响应Vue实例上的数据变动。 - 在Vue内部,watch对应的是用户自定义的Watcher(user watcher)。 - 当创建一个watch时,Vue会为每一个被监听的数据属性创建一个Watcher实例,这个Watcher会在被监听的数据变化时执行回调函数。 - watch支持深度监听(deep)、立即执行(immediate)等选项。 下面我们详细展开。 ### 一、计算属性(computed)原理 在Vue初始化阶段,会遍历computed选项中的每一个属性,为每个属性创建一个Watcher实例,这个Watcher实例的配置如下: ```javascript // 伪代码 const watcher = new Watcher( vm, // 组件实例 getter, // 计算属性的getter函数 noop, // 回调函数,计算属性不需要,所以为空函数 { lazy: true } // 配置选项,lazy表示惰性计算 ) ``` 然后,Vue会使用`Object.defineProperty`在组件实例上定义计算属性。当访问计算属性时,会触发getter函数,其内部逻辑如下: 1. 如果Watcher的dirty标志为true(表示依赖已变化),则调用watcher.evaluate()重新计算,并将dirty设为false。 2. 如果Watcher的dirty标志为false,则直接返回缓存的值。 依赖收集的过程: - 在计算属性的getter执行期间,会访问它所依赖的响应式数据,这时会触发这些数据的getter,从而将当前的计算属性Watcher收集为依赖。 - 当依赖的数据变化时,会通知计算属性Watcher,但计算属性Watcher并不会立即重新计算,而是将自己标记为dirty。等到下次访问计算属性时才会重新计算。 ### 二、监听属性(watch)原理 在Vue初始化阶段,会遍历watch选项中的每一个属性,为每个被监听的表达式创建一个Watcher实例(user watcher)。这个Watcher的配置如下: ```javascript // 伪代码 const watcher = new Watcher( vm, expression, // 被监听的表达式,如函数名或字符串路径 callback, // 回调函数 options // 配置选项,如deep、immediate等 ) ``` 当被监听的数据发生变化时,Watcher的回调函数会被调用。如果设置了`immediate: true`,则会在初始化时立即执行一次回调函数。 深度监听的实现: - 当设置了`deep: true`时,Vue会递归遍历被监听对象的所有子属性,并在每个子属性上添加依赖。这样,任何子属性的变化都会触发回调。 ### 三、computedwatch的核心区别 1. **缓存机制**:computed具有缓存,只有依赖变化才重新计算;watch没有缓存,每次变化都执行回调。 2. **使用场景**:computed适合计算派生数据;watch适合执行副作用(如异步请求、操作DOM)。 3. **异步支持**:watch可以执行异步操作;computed则不行,因为计算属性必须同步返回结果。 ### 四、底层实现:Watcher类 Vue内部有三种Watcher: 1. **Render Watcher**:负责视图更新。 2. **Computed Watcher**:计算属性使用的Watcher。 3. **User Watcher**:用户通过`watch`选项或`$watch`API创建的Watcher。 Watcher的核心逻辑: - 在创建Watcher时,会执行一次getter函数(对于computed watcher是惰性的),从而进行依赖收集。 - 当依赖变化时,会触发Watcher的update方法。对于computed watcher,只是标记dirty;对于user watcher,则会执行回调。 ### 五、代码示例解析 ```javascript // 计算属性初始化伪代码 function initComputed(vm, computed) { const watchers = (vm._computedWatchers = {}) for (const key in computed) { const getter = computed[key] watchers[key] = new Watcher(vm, getter, noop, { lazy: true }) defineComputed(vm, key, getter) } } function defineComputed(target, key) { Object.defineProperty(target, key, { get() { const watcher = this._computedWatchers[key] if (watcher.dirty) { watcher.evaluate() // 重新计算,并清除dirty标志 } return watcher.value } }) } ``` ### § 相关问题 § 1. Vue的响应式系统中如何收集依赖? 2. Watcher类中的dirty标志有什么作用? 3. 如何实现深度监听(deep watch)? 4. computed属性可以异步返回值吗?为什么? 5. Vue3中computedwatch的实现与Vue2有什么不同? 以上内容根据引用[1][2][3][4]整理而成[^1][^2][^3][^4]。 </think>### VueComputedWatch的原理详解 #### 一、计算属性(Computed)原理 1. **依赖追踪** - 通过`Object.defineProperty`或`Proxy`建立响应式依赖关系 - 初始化时创建计算属性对应的`Watcher`实例(带`lazy: true`标志) - 首次访问时执行计算函数并缓存结果 2. **缓存机制** ```javascript // 伪代码实现 class ComputedWatcher { get() { if (this.dirty) { // 依赖变化时dirty=true this.value = this.getter() // 重新计算 this.dirty = false // 重置标记 } return this.value } } ``` 3. **更新流程** - 依赖数据变化 → 通知计算属性Watcher - 标记`dirty=true`(但不立即计算) - 下次访问时重新计算并更新缓存[^1][^2] #### 二、侦听器(Watch)原理 1. **监听实现** ```javascript // 伪代码:用户watcher创建 Vue.prototype.$watch = function(exp, cb, options) { new Watcher(vm, exp, cb, {...options, user: true}) } ``` 2. **Watcher分类** | 类型 | 触发时机 | 用途 | |------|----------|------| | Render Watcher | 模板依赖变化 | 视图更新 | | Computed Watcher | 依赖数据变化 | 计算属性 | | **User Watcher** | 特定数据变化 | watch监听[^2] | 3. **深度监听实现** - `deep:true`时递归遍历对象属性 - 为每个子属性添加侦听器 - 任意嵌套属性变化都会触发回调 #### 三、核心区别原理对比 | 特性 | Computed | Watch | |------|----------|-------| | **实现机制** | 惰性求值+缓存 | 主动侦听+回调 | | **响应触发** | 依赖变化时标记dirty | 数据变化立即执行 | | **返回值需求** | 必须返回结果 | 无需返回值 | | **异步支持** | 不推荐(破坏响应链) | 原生支持(Promise等) | | **底层Watcher** | lazy-watcher | user-watcher | #### 四、代码示例说明 ```javascript // Computed内部实现简化版 function defineComputed(target, key) { const watcher = new Watcher(target, getter, null, { lazy: true }) Object.defineProperty(target, key, { get() { if (watcher.dirty) { watcher.evaluate() // 重新计算 } return watcher.value } }) } // Watch监听属性变化 vm.$watch('obj.nested', (newVal, oldVal) => { console.log('深度属性变化', newVal) }, { deep: true, immediate: true }) ``` #### 五、性能优化要点 1. **Computed优势场景** - 复杂计算:$$ \sum_{i=1}^{n} data[i] \times factor $$ - 模板中的派生数据:`{{ formattedDate }}` - 避免重复计算:依赖未变时直接返回缓存 2. **Watch适用场景** - 异步操作:API请求、定时器 - 副作用操作:DOM操作、本地存储 - 非派生逻辑:`if (value > threshold) doSomething()` --- ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值