首页 > 编程语言 >vue2源码-十四、computed和watch的区别

vue2源码-十四、computed和watch的区别

时间:2023-04-24 21:45:16浏览次数:49  
标签:computed watch vm Watcher handler key 源码 属性

computed和watch的区别

computed watch的相同点。底层都会创建一个 watcher(用法的区别:computed 定义的属性可以在模板中使用,watch 不能在视图中使用)

  • computed 默认不会执行 只有取值的时候才会执行 内部会维护一个 dirty 属性,来控制依赖的值是否发生变化。默认计算属性需要同步返回结果(有个包,让computed变成异步的 0)。

  • watch 默认用户会提供一个回调函数,数据变化了就调用这个回调。我们可以监控某个数据的变化 数据变化了执行某些操作。

computed:

  1. 通过Object.defineProperty实现计算属性的属性劫持;
  2. 在组件实例中创建Watcher实例,用于监听计算属性的依赖项变化;
  3. 在Getter函数中进行依赖收集,即将当前Watcher实例添加到Dep实例中;
  4. 在Setter函数中不进行任何操作,因为计算属性不支持双向绑定;
  5. 通过缓存机制避免重复计算和浪费资源;
  6. 在依赖项发生变化时触发Watcher实例的更新,并通知相关组件进行视图更新。
export function initState (vm: Component) {
  // 省略部分代码
  const computed = options.computed
  if (computed) {
    for (const key in computed) {
      const userDef = computed[key]
      const getter = typeof userDef === 'function' ? userDef : userDef.get
      if (!getter || typeof getter !== 'function') {
        // 省略部分代码
      }
      // 创建Watcher实例,并将其添加到computedWatchers对象中,并设置该计算属性对应的Getter函数
      defineComputed(vm, key, getter, userDef) 
    }
  }
  // 省略部分代码
}

// 定义defineComputed函数,用于创建计算属性的Getter函数及Watcher实例
export function defineComputed (
  target: any,
  key: string,
  userDef: Object | Function,
  setter?: Function
) {
  if (typeof userDef === 'function') {
    sharedPropertyDefinition.get = createComputedGetter(key) // 创建计算属性的Getter函数
    sharedPropertyDefinition.set = setter // 设置计算属性的Setter函数(不支持双向绑定)
  } else {
    sharedPropertyDefinition.get = userDef.get
      ? userDef.cache !== false
        ? createComputedGetter(key) // 创建计算属性的Getter函数
        : userDef.get
      : noop
    sharedPropertyDefinition.set = userDef.set || noop
  }
  Object.defineProperty(target, key, sharedPropertyDefinition)
}

总结一下:

  1. 通过defineComputed函数定义计算属性,并创建计算属性对应的Watcher实例;
  2. createComputedGetter函数中创建计算属性的Getter函数,在其中调用计算属性对应Watcher实例的evaluate方法进行计算,并在needSpend时间过长等情况下设置dirty标记,表示计算结果已经失效,需要重新计算;
  3. 在计算属性的Getter函数中进行依赖收集,并将计算属性的Watcher实例添加到Dep.target对应的Watcher实例的依赖项中;
  4. 在计算属性的Setter函数中不进行任何操作,因为计算属性不支持双向绑定;
  5. 通过缓存机制避免重复计算和浪费资源;
  6. 在依赖项发生变化时触发计算属性的Watcher实例的更新,并通知相关组件进行视图更新。

watch:

  1. 通过initWatch函数遍历watch对象,为每个属性创建Watcher实例,并将其添加到异步更新队列中进行更新;
  2. 通过$watch方法手动创建观察者,内部调用createWatcher函数创建Watcher实例,并设置Watcher实例所依赖的数据项和回调函数等信息;
  3. Watcher类中承担了依赖收集和更新的核心功能;
  4. 在计算属性和watch中都需要为每个依赖项创建Watcher实例,但两者实现方式略有不同。
// 省略部分代码

export function initState (vm: Component) {
  // ...
  const { watch, computed } = options
  if (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)
      }
    }
  }
  // ...
}

// 省略部分代码

function createWatcher (
  vm: Component,
  expOrFn: string | Function,
  handler: any,
  options?: Object
) {
  if (isPlainObject(handler)) {
    options = handler
    handler = handler.handler
  }
  if (typeof handler === 'string') {
    handler = vm[handler]
  }
  return vm.$watch(expOrFn, handler, options)
}

// 省略部分代码

实现$watch方法:

export function initWatch (vm: Component, watch: Object) {
  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)
    }
  }
}

export function stateMixin (Vue: Class<Component>) {
  // ...
  Vue.prototype.$watch = function (
    expOrFn: string | Function,
    cb: any,
    options?: Object
  ): Function {
    const vm: Component = this
    if (isPlainObject(cb)) { // 如果cb是一个对象,则options为传入的第三个参数,cb取该对象的handler属性
      return createWatcher(vm, expOrFn, cb, options)
    }
    options = options || {}
    options.user = true // 将user标记设置为true,表示当前Watcher实例由用户手动创建
    const watcher = new Watcher(vm, expOrFn, cb, options)
    if (options.immediate) { // 如果设置了immediate选项,则立即执行回调函数
      cb.call(vm, watcher.value)
    }
    return function unwatchFn () {
      watcher.teardown()
    }
  }
}

标签:computed,watch,vm,Watcher,handler,key,源码,属性
From: https://www.cnblogs.com/dgqp/p/17351013.html

相关文章

  • Python学习笔记--json序列化时间报错-改源码
    问题:转换时间报错执行代码为:importjsonfromdatetimeimportdate,datetimed={"time1":date.today(),"time2":datetime.today()}res=json.dumps(d)#报错  TypeError:ObjectoftypedateisnotJSONserializable方案1:手动转换str()方案2:继承类......
  • Watch监听-示例
    packagecom.hw.curator;importorg.apache.curator.RetryPolicy;importorg.apache.curator.framework.CuratorFramework;importorg.apache.curator.framework.CuratorFrameworkFactory;importorg.apache.curator.framework.api.BackgroundCallback;importorg.apach......
  • 开源外卖系统源码解析:如何快速搭建属于自己的订餐平台?
    随着外卖市场的日益壮大,许多商家和个人都在考虑如何搭建一个属于自己的订餐平台。而在这个过程中,开源外卖系统源码无疑是一项不可或缺的资源。本文将以“开源外卖系统源码解析:如何快速搭建属于自己的订餐平台?”为主题,向您介绍外卖系统的相关内容,帮助您更好地了解如何利用现有的开源......
  • 一文详解RocketMQ-Spring的源码解析与实战
    摘要:这篇文章主要介绍SpringBoot项目使用rocketmq-springSDK实现消息收发的操作流程,同时笔者会从开发者的角度解读SDK的设计逻辑。本文分享自华为云社区《RocketMQ-Spring:实战与源码解析一网打尽》,作者:勇哥java实战分享。RocketMQ是大家耳熟能详的消息队列,开源项目......
  • 视频直播源码,android动画小飞机旋转效果
    视频直播源码,android动画小飞机旋转效果 //小飞机旋转动效果publicclassPlaneViewextendsView{  privatePaintpaint;  privateintwidth;  privateintheight;  privatefloatcurLength;  privatefloatallLength;  privatefloatmAnimato......
  • FutureTask源码分析
    1、Callable与FutureTask介绍1.1、Callable创建线程有两种方式,一种是继承Thread类,一种是实现Runnable接口重写run方法。其实Thread也实现了Runable接口。在Runable接口中,仅有一个无参无返回结果的run方法。Runable接口详情:1@FunctionalInterface2publicinterfa......
  • Map - TreeSet & TreeMap 源码解析
    Java7-TreeSet&TreeMap总体介绍前者仅仅是对后者做了一层包装,也就是说TreeSet里面有一个TreeMap(适配器模式)。因此本文将重点分析TreeMap。JavaTreeMap实现了SortedMap接口,也就是说会按照key的大小顺序对Map中的元素进行排序,key大小的评判可以通过其本身的自然顺序(natu......
  • Map - LinkedHashSet&Map源码解析
    上篇文章讲了HashMap。HashMap是一种非常常见、非常有用的集合,但在多线程情况下使用不当会有线程安全问题。大多数情况下,只要不涉及线程安全问题,Map基本都可以使用HashMap,不过HashMap有一个问题,就是迭代HashMap的顺序并不是HashMap放置的顺序,也就是无序。HashMap的这一缺点往往会......
  • 【深入浅出Spring原理及实战】「源码调试分析」深入源码探索Spring底层框架的的refres
    学习Spring源码的建议阅读Spring官方文档,了解Spring框架的基本概念和使用方法。下载Spring源码,可以从官网或者GitHub上获取。阅读Spring源码的入口类,了解Spring框架的启动过程和核心组件的加载顺序。阅读Spring源码中的注释和文档,了解每个类和方法的作用和用法。调试Spring源码,可以......
  • vue2源码-十三、nextTick在哪里使用?原理是什么?
    nextTick在哪里使用?原理是什么?nextTick内部采用了异步任务进行包装(多个nextTick调用会被合并成一次,内部会合并回调)最后在异步任务中批处理。主要应用场景就是异步更新(默认调度的时候就会添加一个·nextTick任务)用户为了获取最终的渲染结果需要在内部任务执行之后再执行用户逻......