vue中Computed和Watch的区别

Vue面试之watch与computed的区别 最近在整理一些前端面试中经常被问到的问题,分为vue相关、react相关、js相关、react相关等等专题,可持续关注后续内容,会不断进行整理~ 阅读详情

computed

用法
var vm = new Vue({
  data: { a: 1 },
  computed: {
    // 仅读取
    aDouble: function () {
      return this.a * 2
    },
    // 读取和设置
    aPlus: {
      get: function () {
        return this.a + 1
      },
      set: function (v) {
        this.a = v - 1
      }
    }
  }
})
vm.aPlus   // => 2
vm.aPlus = 3
vm.a       // => 2
vm.aDouble // => 4

computed选项的属性值可以是一个函数,该函数默认为取值器getter,用于仅读取数据;还可以是一个对象,对象里面有取值器getter和存值器setter,用于读取和设置数据

源码分析

function initComputed (vm: Component, computed: Object) {
    const watchers = vm._computedWatchers = Object.create(null)
    const isSSR = isServerRendering()

    for (const key in computed) {
        const userDef = computed[key]
        const getter = typeof userDef === 'function' ? userDef : userDef.get
        if (process.env.NODE_ENV !== 'production' && getter == null) {
            warn(
                `Getter is missing for computed property "${key}".`,
                vm
            )
        }

        if (!isSSR) {
            // create internal watcher for the computed property.
          // 则创建一个watcher实例,并将当前循环到的的属性名作为键,
          // 创建的watcher实例作为值存入watchers对象中。
            watchers[key] = new Watcher(
                vm,
                getter || noop,
                noop,
                computedWatcherOptions
            )
        }

        if (!(key in vm)) {
            // 实例vm上设置计算属性
            defineComputed(vm, key, userDef)
        } else if (process.env.NODE_ENV !== 'production') {
          // 这些是判断是否重名的
            if (key in vm.$data) {
                warn(`The computed property "${key}" is already defined in data.`, vm)
            } else if (vm.$options.props && key in vm.$options.props) {
                warn(`The computed property "${key}" is already defined as a prop.`, vm)
            }
        }
    }
}

● 首先会在实例上创建一个_computedWatchers对象,这个对象会存放每个computed 类watcher
● 遍历computed对象上的每个属性,如果该属性是一个函数,则默认取值器getter是这个函数,如果该属性是一个对象,则取值器getter是对象的getter属性,如果没有取值器的话,会警告
● 对每一个computed key设置一个watcher,将setter传进去,。。。。
● 设置完之后,判断实例上有没有key这个属性,没有的话就设置在实例上,使用的是defineComputed函数

defineComputed函数

该函数接受3个参数,分别是:target、key和userDef。其作用是为target上定义一个属性key,并且属性key的getter和setter根据userDef的值来设置。下面我们就来看一下该函数的具体逻辑。

const sharedPropertyDefinition = {		
  enumerable: true,
  configurable: true,
  get: noop,
  set: noop
}

export function defineComputed (target,key,userDef) {
  const shouldCache = !isServerRendering()
  if (typeof userDef === 'function') {
    sharedPropertyDefinition.get = shouldCache
      ? createComputedGetter(key)
      : userDef
    sharedPropertyDefinition.set = noop
  } else {
    sharedPropertyDefinition.get = userDef.get
      ? shouldCache && userDef.cache !== false
        ? createComputedGetter(key)
        : userDef.get
      : noop
    sharedPropertyDefinition.set = userDef.set
      ? userDef.set
      : noop
  }
  if (process.env.NODE_ENV !== 'production' &&
      sharedPropertyDefinition.set === noop) {
    sharedPropertyDefinition.set = function () {
      warn(
        `Computed property "${key}" was assigned to but it has no setter.`,
        this
      )
    }
  }
  Object.defineProperty(target, key, sharedPropertyDefinition)
}

该函数的主要作用就是给computed属性设置一个具有缓存功能的setter函数,在createComputedGetter函数里面设置,但是在服务端渲染的情况下不需要缓存???

createComputedGetter函数
function createComputedGetter (key) {
    return function computedGetter () {
        const watcher = this._computedWatchers && this._computedWatchers[key]
        if (watcher) {
            watcher.depend()
            return watcher.evaluate()
        }
    }
}

这里面会返回一个函数,这个函数就是该计算属性值的getter,在这个函数中,会先拿出对应的watcher出来,在执行depend和evalute方法,也就是说,computed的缓存的奥秘就在watcher的这两个方法中
接着,我们在来回顾下watcher类的定义,了解computed的缓存

watcher类
export default class Watcher {
    constructor (vm,expOrFn,cb,options,isRenderWatcher) {
        if (options) {
            // ...
            this.computed = !!options.computed
            // ...
        } else {
            // ...
        }

        this.dirty = this.computed // for computed watchers
        if (typeof expOrFn === 'function') {
            this.getter = expOrFn
        }

        if (this.computed) {
            this.value = undefined
            this.dep = new Dep()
        }
    }

    evaluate () {
        if (this.dirty) {
            this.value = this.get()
            this.dirty = false
        }
        return this.value
    }

  // 收集dep
  addDep (dep: Dep) {
    const id = dep.id
    if (!this.newDepIds.has(id)) {
      this.newDepIds.add(id)
      this.newDeps.push(dep)
      if (!this.depIds.has(id)) {
        dep.addSub(this)
      }
    }
  }

    /**
     * Depend on this watcher. Only for computed property watchers.
     */
    depend () {
        if (this.dep && Dep.target) {
            this.dep.depend()
        }
    }

    update () {
        if (this.computed) {
            if (this.dep.subs.length === 0) {
                this.dirty = true
            } else {
                this.getAndInvoke(() => {
                    this.dep.notify()
                })
            }
        }
    }

    getAndInvoke (cb: Function) {
        const value = this.get()
        if (
            value !== this.value ||
            // Deep watchers and watchers on Object/Arrays should fire even
            // when the value is the same, because the value may
            // have mutated.
            isObject(value) ||
            this.deep
        ) {
            // set new value
            const oldValue = this.value
            this.value = value
            this.dirty = false
            if (this.user) {
                try {
                    cb.call(this.vm, value, oldValue)
                } catch (e) {
                    handleError(e, this.vm, `callback for watcher "${this.expression}"`)
                }
            } else {
                cb.call(this.vm, value, oldValue)
            }
        }
    }
}

● 对于watcher类来说,有专门针对computed 类型的wathcer的一些方法
● 会通过this.computed属性判断是不是一个computed类型的watcher
● 如果是的话,会创建一个deps数组来收集dep,同时,会设置一个this.dirty属性来判断是否使用缓存数据,如果dirty为false(表示数据不脏)的话,取缓存,dirty为true的话不取缓存
可以看到,在实例化Watcher类的时候,第四个参数传入了一个对象computedWatcherOptions = { computed: true },该对象中的computed属性标志着这个watcher实例是计算属性的watcher实例,即Watcher类中的this.computed属性,同时类中还定义了this.dirty属性用于标志计算属性的返回值是否有变化,计算属性的缓存就是通过这个属性来判断的,每当计算属性依赖的数据发生变化时,会将this.dirty属性设置为true,这样下一次读取计算属性时,会重新计算结果返回,否则直接返回之前的计算结果。
当调用watcher.depend()方法时,会将读取计算属性的那个watcher添加到计算属性的watcher实例的依赖列表中,当计算属性中用到的数据发生变化时,计算属性的watcher实例就会执行watcher.update()方法,在update方法中会判断当前的watcher是不是计算属性的watcher,如果是则调用getAndInvoke去对比计算属性的返回值是否发生了变化,如果真的发生变化,则执行回调,通知那些读取计算属性的watcher重新执行渲染逻辑。
当调用watcher.evaluate()方法时,会先判断this.dirty是否为true,如果为true,则表明计算属性所依赖的数据发生了变化,则调用this.get()重新获取计算结果最后返回;如果为false,则直接返回之前的计算结果。

watch

用法
var vm = new Vue({
  data: {
    a: 1,
    b: 2,
    c: 3,
    d: 4,
    e: {
      f: {
        g: 5
      }
    }
  },
  watch: {
    a: function (val, oldVal) {
      console.log('new: %s, old: %s', val, oldVal)
    },
    // methods选项中的方法名
    b: 'someMethod',
    // 深度侦听,该回调会在任何被侦听的对象的 property 改变时被调用,不论其被嵌套多深
    c: {
      handler: function (val, oldVal) { /* ... */ },
      deep: true
    },
    // 该回调将会在侦听开始之后被立即调用
    d: {
      handler: 'someMethod',
      immediate: true
    },
    // 调用多个回调
    e: [
      'handle1',
      function handle2 (val, oldVal) { /* ... */ },
      {
        handler: function handle3 (val, oldVal) { /* ... */ },
      }
    ],
    // 侦听表达式
    'e.f': function (val, oldVal) { /* ... */ }
  }
})
vm.a = 2 // => new: 2, old: 1

● 可以看出,watch对象的属性表示的是侦听的属性,这个属性也可以是侦听表达式
● 每个属性值可以直接是一个函数,函数参数是newVal和oldVal,当属性变化的时候会执行该函数
● 也可以是一个字符串,表示的是methods中的方法名
● 也可以是一个对象,配置一些属性,handler表示的是数据发生变化时要执行的函数

源码分析

function initWatch (vm, watch) {
  for (const key in watch) {
    const handler = watch[key]
    if (Array.isArray(handler)) {
      // 此时是多个回调的情况
      for (let i = 0; i < handler.length; i++) {
        createWatcher(vm, key, handler[i])
      }
    } else {
      createWatcher(vm, key, handler)
    }
  }
}
createWatcher函数
function createWatcher (
  vm: Component,  // 当前实例
  expOrFn: string | Function,  // 被侦听的属性表达式
  handler: any,  // watch选项中每一项的值
  options?: Object  // 用于传递给vm.$watch的选项对象
) {
  if (isPlainObject(handler)) {
    options = handler
    handler = handler.handler
  }
  // 这种写法对应handler是method中的方法
  if (typeof handler === 'string') {
    handler = vm[handler]
  }
  return vm.$watch(expOrFn, handler, options)
}
其实就是调用了vm.$watch这个方法,将监听的属性,回调函数作为参数
vm.$watch函数
Vue.prototype.$watch = function (expOrFn,cb,options) {
    const vm: Component = this
    if (isPlainObject(cb)) {
      // 从用户合起来传入的对象中把回调函数cb和参数options剥离出来
      return createWatcher(vm, expOrFn, cb, options)
    }
    options = options || {}
  // 用户手动调动该方法创建出来的
    options.user = true
    const watcher = new Watcher(vm, expOrFn, cb, options)
    if (options.immediate) {
      // 立即执行回调
      cb.call(vm, watcher.value)
    }
    return function unwatchFn () {
      // 取消观察,就是将所依赖的dep中将自己删除
      watcher.teardown()
    }
  }

computed和watch的区别

computed的缓存

computed的缓存说的是只有计算属性的依赖发生变化的时候,计算属性才会去重新计算它的值,也就是说,如果页面重新渲染而所依赖的数据没有变化的时候,计算属性不会重新计算,而方法则会重新执行
这对一些需要处理很多数据的计算属性来说,可以提高性能

对于computed:

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

对于watch:

● 不支持缓存,数据变化时,会触发相应的操作
● 支持异步监听,也就是可以再处理函数中使用异步操作
● 监听的函数接收两个参数,第一个参数是最新的值,第二个是变化之前的值
● 当一个属性发生变化时,就需要执行相应的操作
● 监听数据必须是data中声明的或者父组件传递过来的props中的数据,当发生变化时,会触发其他操作,函数有两个的参数:
○ immediate:组件加载立即触发回调函数
○ deep:深度监听,发现数据内部的变化,在复杂数据类型中使用,例如数组中的对象发生变化。需要注意的是,加上deep的话,数组内元素的变化或者对象属性值的变化,都能狗侦测的到。
当想要执行异步或者昂贵的操作以响应不断的变化时,就需要使用watch。

参考链接:
https://vue-js.com/learn-vue/instanceMethods/data.html#_1-vm-watch
https://www.yuque.com/cuggz/interview/hswu8g#d72e59d5f3d78b8cf5d8038e0e12803e

FlexibleButton嵌入式按键处理方案设计与实现 嵌入式系统中的按键处理是基础且关键的人机交互技术,其核心原理是通过GPIO电平检测结合状态机实现事件识别。FlexibleButton作为轻量级C语言按键处理库,采用硬件抽象层设计实现驱动解耦,支持单击、双击、长按等丰富事件类型。该方案通过集中式扫描策略和内置消抖算法,在RT-Thread和裸机环境中均能稳定运行,特别适合需要组合按键检测和低功耗优化的物联网设备。典型应用场景包括智能家居控制面板和工业HMI界面开发,其中硬件解耦架构和事件驱动模型显著提升代码复用率。 阅读详情

相关推荐

ENVI+SARscape实战:从哨兵一号数据到地震形变制图全流程解析

本文详细解析了使用ENVI和SARscape软件从哨兵一号数据生成地震形变图的全流程,包括数据获取、干涉处理、相位解缠等关键步骤。通过实战技巧和参数设置建议,帮助用户高效处理SAR数据,提升地震形变监测的精度和可视化效果。

weixin_30460489的博客 385

vue中的data,computed,methods,created,mounted

本文将介绍一下vue的基本结构中各分类的含义以及他们的执行顺序。(包括data,computed,methods,created,mounted)

qq_59541478的博客 6660

Unity XR Interaction Toolkit(二)手柄交互

XR Interaction 手柄事件和抓取设置

qq_36608844的博客 8496

vue中created、mounted、computed、methods区别与执行顺序

computed是在DOM执行完成后立马执行(如:赋值) created执行时挂载阶段还没有开始,模版还没有渲染成html,所以无法获取元素。created钩子函数主要用来初始化数据。 mounted钩子函数一般用来向后端发起请求,拿到数据后做一些业务处理。该函数在模版渲染完成后才被调用。DOM操作一般是在mounted钩子函数中进行。 methods方法有一定的触发条件,如click等。 watch用于检测vue实例上数据的变动 默认加载的时候先computed再watch,不执行methods;等触发某

qq_41611829的博客 6522

AI视频生成提示词工程:结构化公式与实战案例详解

在实际使用 AI 生成视频或图像时,最常遇到的困惑是:为什么我的提示词(Prompt)生成的画面总是不理想?是模型能力不足,还是我的描述有问题?答案往往是后者。提示词工程(Prompt Engineering)并非简单的“翻译”需求,而是一门需要结构化思维和精准描述的技能。它决定了 AI 能否准确理解你的意图,并生成符合预期的视觉内容。 本文将通过两个核心案例,深入拆解提示词的构建逻辑。第一个案例聚焦于“文生视频”,我们将学习如何将一个简单的想法,如“小猫在雪地里玩雪球”,通过结构化公式扩展为一段富有细节和

weixin_30800987的博客 563

常见消息队列:ActiveMQ、RabbitMQ、RocketMQ、Kafka的区别总结

常见消息队列:ActiveMQ、RabbitMQ、RocketMQ、Kafka的区别。

qq_30713721的博客 3471

vue 初始化方法 create,beforeMount,mount,computed,watch 方法执行顺序及使用场景

vue 存在一些基本属性和相关对象如果合理使用,可以避免代码混乱。

朝立的博客 4950

数学建模美赛O奖论文研读启示录——从模仿开始

数学建模美赛O奖论文哪些地方比较出彩?我们可以从中学到什么东西?可以从哪些地方进行模仿?我总结了一些O奖论文里我认为比较好的地方,跟大家一起学习交流!

小宇的修炼之路 5429

vue中computed,created,mounted的区别又忘记了。。总结一波

下班之后无聊的我,看看vue

水镜先生的博客 5963

模块学习(一)——编码电机

编码电机测速

qq_51406846的博客 7911

Lucene学习总结之二:Lucene的总体架构

Lucene总的来说是: 一个高效的,可扩展的,全文检索库。 全部用Java实现,无须配置。 仅支持纯文本文件的索引(Indexing)和搜索(Search)。 不负责由其他格式的文件抽取纯文本文件,或从网络中抓取文件的过程。 在Lucene in action中,Lucene 的构架和过程如下图, 说明Lucene是有索引和搜索的两个过程,包

forfuture1978的专栏 1万+

Vue之computed

前言 上篇文章就new Vue()过程中的基本处理流程有了了解,并对data和挂载部分进行了分析,本次就针对于computed进行具体的分析。 计算属性computed的提出三个问题点: Vue对于computed的具体处理 computed与data有何区别 computed是如何收集依赖的 computed computed的处理逻辑都是由initComputed而起,具体的处理...

玉案轩窗的博客 3873

知道LPDDR和DDR什么关系吗?

话不多说,先上大白话,一个网友总结的很好:简单地说DRR4是标准电压版的PC和服务器内存,LPDDR4属于手机等嵌入式移动设备使用的低电压低功耗内存,LPDDR4X则是LPDDR4的升级版,下面详细看看老哥的博客。LPDDR4是与DDR4内存等效的移动设备。。LPDDR4具有两个16位通道,从而使每个DIMM的总线总长度为32位。相比之下,DDR4每个DIMM具有64位通道。

叫好与叫座虽然不是对立面,但想在同一个作品中达到双重效果很难。 2806

Vue中computed和watch区别

watch和computed都是以Vue的依赖追踪机制为基础的,当某一个依赖型数据发生变化的时候,所有依赖这个数据的相关数据会自动发生变化,即自动调用相关的函数,来实现数据的变动。当依赖的值变化时,在watch中,可以做一些复杂的操作,而computed中的依赖,仅仅是一个值依赖于另一个值,是值上的依赖。最近找到一个VUE的文档,它将VUE的各个知识点进行了总结,整理成了《Vue 开发必须知道的36个技巧》。内容比较详实,对各个知识点的讲解也十分到位。

小陈的博客 1万+

Camera2Demo

在Camera2 的基础上进行封装,使得Camera2一堆初始化逻辑与Activity分离,降低耦合度,完成预览拍照并保存的操作

WINCC7.3破解文件

WINCC7.3硬件够破解文件,有需要的下载!WINCC7.3硬件够破解文件,有需要的下载!

Vue中computed和watch的区别

今天简单说一下computed和watch的区别。 先看代码,两者怎么实现的。 <div id="app"> <input type="text" v-model="firstName"><br> <input type="text" v-model="lastName"><br> <h2>{{fullName()}}</h2> </div> <scr

a_dragon_的博客 2万+

ET2012新版免狗免安装绿色软件

ET2012新版免狗免安祖昂绿色软件。,新界面,新视觉,

Vue Watch 和 Computed 的区别详解

test

陆小森_红齐飘飘的博客 4981

文档:亲测可用解决vnc灰屏,黑屏,鼠标大黑叉情况

主要解决vnc-viewer灰屏,黑屏,鼠标大黑叉等现象,详细见文档。

vue的computed和watch的区别

computed和watch都是vue框架中用于监听数据变化的属性

weixin_44063225的博客 1万+

Vue中的computed和watch的区别

Vue中的computed和watch的区别: 计算属性(computed) 官方对于计算属性的解释是: 对于任何复杂逻辑,你都应当使用计算属性。计算属性是基于它们的响应式依赖进行缓存的。只在相关响应式依赖发生改变时它们才会重新求值。 直接引用官方文档的例子: <div id="example"> <p>Original message: "{{ message }}"</p> <p>Computed reversed message: "{{ r

白小白的小白的博客 2569

Vue 中的 watch 和 computed 的区别

watch用于监听特定数据的变化,并在数据变化时执行相应的操作;可以监听某个数据的变化,也可以监听多个数据的变化;适合处理异步操作或复杂的数据变化逻辑;通过watch监听的数据发生变化时,可以执行一些异步操作、复杂逻辑计算或手动操作DOM 等;computedcomputed用于基于响应式依赖进行动态计算,并返回计算结果。依赖于响应式数据,只有当依赖的数据发生改变时,computed才会重新计算。适合进行简单的数据计算、筛选、排序等操作。由于computed。

小草莓的博客 3041

Vue中的watch以及watch和computed的区别

一、 watch是什么? 监测 Vue 实例变化的一个表达式或方法。回调函数得到的参数为新值和旧值,用一个函数取代。 简洁的说:watch的作用可以监控一个值的变换,并调用因为变化需要执行的方法。可以通过watch动态改变关联的状态。 二、应用 基本用法: 当firstName值变化时,watch监听到并且执行watch中的firstName函数 <div> <p>FullName: {{fullName}}</p> <p>FirstN

XinKun的博客 2234

Vue中watch和computed的区别

watch 使用watch可以监听当前data中的数据(变化),如下 var vm = new Vue({ //...... data: { name: "张三" }, watch: { "name": function() { //..... } } }) 上面的代码,我们在data中定义了...

pspxuan的博客 2051

vue中computed和watch的区别详解

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

前端挖掘机 5539

Vue中methods,watch和computed的区别

methods,watch和computed都是以函数为基础的,但各自却都不同。 一、作用机制上 1.watch和computed都是以Vue的依赖追踪机制为基础的,它们都试图处理这样一件事情:当某一个数据(称它为依赖数据)发生变化的时候,所有依赖这个数据的“相关”数据“自动”发生变化,也就是自动调用相关的函数去实现数据的变动。 2.对methods:methods里面是用来定义函数的,很显...

小胡的博客 1950

vue 中 computed 和 watch 的区别

computed和watch的核心区别在于“计算” vs “监听”computed是 “被动的”,专注于从现有数据派生新值,依赖缓存提升性能,适合同步、简单的计算逻辑。watch是 “主动的”,专注于监听数据变化并执行副作用(尤其是异步操作),适合复杂逻辑或需要主动触发的场景。实际开发中,应根据具体需求选择:数据派生优先用computed,异步操作或复杂副作用优先用watch。

jialiang808的博客 856

Vue 中的 watch 和 computed 的区别是什么?

理解computed和watch的区别是掌握Vue.js的关键之一。它们各自适用于不同的场景,合理使用可以提高代码质量和应用性能。在实际开发中,应该根据具体需求选择合适的特性,以便实现最佳的用户体验和代码维护性。

qq_38822390的博客 911

Vue中 computed 和 watch 区别及应用场景详解

在 vue 的项目中,我们时常会需要对 data 的改变做出处理,这时候就需要用到 watch 和 computed 这两个属性。既然都是数值改变触发改变的属性,他们之间又有什么异同呢? 1. 计算属性 computed 特点: 支持缓存,只有依赖数据发生改变,才会重新进行计算; 不支持异步,当 computed 内有异步操作时无效,无法监听数据的变化; computed 属性值会默认走缓存,计算属性是基于它们的响应式依赖进行缓存的。也就是基于 data 中声明过或者父组件传递的 props 中的数据通过

明天也要努力的博客 3629
上一篇: javascript执行上下文
Lin__hr
博客等级 码龄6年 5粉丝 25原创
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值