vue2响应式原理----发布订阅模式

这篇具有很好参考价值的文章主要介绍了vue2响应式原理----发布订阅模式。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

很多人感觉vue2的响应式其实用到了观察者+发布订阅。我们先来看一下简单的发布订阅的代码:

// 调度中心
class Dep {
    static subscribes = {}
    // 订阅所有需求
    static subscribe (key, demand) {
      // 对需求分类收集
      if (!Dep.subscribes[key]) Dep.subscribes[key] = []
      Dep.subscribes[key].push(demand)
    }
    // 对所有订阅者发布通知
    static publish (key, age) {
      if (!Dep.subscribes[key]) return
      for (const demand of Dep.subscribes[key]) {
        demand(age)
      }
    }
  }
  // 找对象的猎手类
  class Watcher {
    constructor (name, age) {
      this.name = name // 名字
      this.age = age // 年龄
    }
    // 订阅,由调度中心将猎手需求分类并存放到全局
    subscribe (key, demand) {
      Dep.subscribe(key, demand)
    }
    // 发布,由调度中心将同分类下的需求全部触发
    publish (key, age) {
      Dep.publish(key, age)
    }
  }
  // 猎手注册
  const aa = new Watcher('aa', 18)
  const bb = new Watcher('bb', 20)
  // 猎手订阅自己感兴趣的人
  aa.subscribe('key', function (age) {
    if (age === aa.age) console.log(`我是aa,我们都是${age}`)
    else console.log(`我是aa,我们年龄不同`)
  })
  bb.subscribe('key', function (age) {
    if (age === bb.age) console.log(`我是bb,我们都是${age}`)
    else console.log(`我是bb,我们年龄不同`)
  })
  // 红娘注册
  const red = new Watcher('red', 35)
  // 红娘发布信息
  red.publish('key', 20)
  // 我是aa,我们年龄不同
  // 我是bb,我们都是20

从上面中发现一个重要的点,发布者和订阅者是根据key值来区分的,然后通过消息中心来中转的,他们家是是实现不知道对方是谁。
而观察者模式中观察者是一开始就知道自己观察的是谁。

上面其实就是简易版的vue原理中发布订阅那段,我们接下来看完整过程。

Vue2 的响应式

  1. 创建一个 Observer 对象,它的主要作用是给对象的每个属性添加 getter 和 setter 方法。
  2. 在 getter 和 setter 方法中分别进行依赖的收集和派发更新。
  3. 创建 Watcher 对象,用于监听数据的变化,当数据发生任何变化时,Watcher 对象会触发自身的回调函数。
  4. 在模板解析阶段,对模板中使用到的数据进行依赖的收集,即收集 Watcher 对象。
  5. 当数据发生变化时,Observer 对象会通知 Dep 对象调用 Watcher 对象的回调函数进行更新操作,即派发更新。
  6. 更新完毕后,Vue2 会进行视图的重新渲染,从而实现响应式。

下面是一个基于 Object.defineProperty 实现响应式的示例,仅供参考:

function observe(obj) {
  if (!obj || typeof obj !== 'object') {
    return;
  }
  Object.keys(obj).forEach(key => {
    // 尝试递归处理
    observe(obj[key]);
    let val = obj[key];
    const dep = new Dep(); // 新建一个依赖
    Object.defineProperty(obj, key, {
      enumerable: true,
      configurable: true,
      get() {
        if (Dep.target) {
          dep.depend(); // 收集依赖
        }
        return val;
      },
      set(newVal) {
        if (newVal === val) {
          return;
        }
        val = newVal;
        dep.notify(); // 派发更新
      }
    });
  });
}
 
// 依赖类
class Dep {
  constructor() {
    this.subs = [];
  }
  addSub(sub) {
    this.subs.push(sub);
  }
  removeSub(sub) {
    const index = this.subs.indexOf(sub);
    if (index !== -1) {
      this.subs.splice(index, 1);
    }
  }
  depend() {
    if (Dep.target) {
      Dep.target.addDep(this);
    }
  }
  notify() {
    this.subs.forEach(sub => sub.update());
  }
}
 
Dep.target = null;
 
// 观察者类
class Watcher {
  constructor(vm, expOrFn, callback) {
    this.vm = vm;
    this.getter = parsePath(expOrFn);
    this.callback = callback;
    this.value = this.get(); // 初始化,触发依赖
  }
  get() {
    Dep.target = this; // 设置当前依赖
    const value = this.getter.call(this.vm, this.vm); // 触发 getter
    Dep.target = null; // 清除当前依赖
    return value;
  }
  addDep(dep) {
    dep.addSub(this);
  }
  update() {
    const oldValue = this.value;
    this.value = this.get(); // 重新获取
    this.callback.call(this.vm, this.value, oldValue); // 触发回调
  }
}
 
// 解析路径
function parsePath(expOrFn) {
  if (typeof expOrFn === 'function') {
    return expOrFn;
  }
  const segments = expOrFn.split('.');
  return function(obj) {
    for (let i = 0; i < segments.length; i++) {
      if (!obj) {
        return;
      }
      obj = obj[segments[i]];
    }
    return obj;
  };
}
 
// 测试
const obj = { foo: 'foo', bar: { a: 1 } };
observe(obj);
new Watcher(obj, 'foo', (val, oldVal) => {
  console.log(`foo changed from ${oldVal} to ${val}`);
});
new Watcher(obj, 'bar.a', (val, oldVal) => {
  console.log(`bar.a changed from ${oldVal} to ${val}`);
});
 
obj.foo = 'FOO'; // 输出 `foo changed from foo to FOO`
obj.bar.a = 2; // 输出 `bar.a changed from 1 to 2`

以上代码中,函数 observe 用于递归遍历对象属性,把其进行劫持,包括收集依赖和派发更新;类 Dep 代表一个依赖,其中 addSub 用于添加订阅者实例,removeSub 用于移除订阅者实例,depend 用于收集依赖,即把当前依赖加到对应的订阅者中,notify 用于派发更新,即遍历所有订阅者,并触发其回调函数。类 Watcher 则代表一个订阅者,其中 getter 用于获取数据,callback 用于回调函数,addDep 用于添加依赖,即把当前订阅者添加到对应的依赖中,update 用于更新值,并触发相应的回调函数,如有必要。函数 parsePath 则用于解析路径字符串,返回对应属性的值。

例子中我们对对象 obj 进行了劫持,同时创建了两个观察者,分别对应 foo 和 bar.a 两个属性。当其中任意一个属性的值发生变化时,其对应的依赖都会被更新,从而触发其绑定的观订阅者的回调函数。

简单来说,在 Vue2 响应式系统中,当数据发生改变时,会触发 get 和 set 方法,get 方法会收集所有依赖该数据的 Watcher 对象,set 方法会通知 Dep 对象触发所有 Watcher 对象的回调函数进行更新。如此循环,实现了数据的响应式。

vue2响应式原理----发布订阅模式,vue,前端,js,前端,javascript,开发语言文章来源地址https://www.toymoban.com/news/detail-851318.html

到了这里,关于vue2响应式原理----发布订阅模式的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • js设计模式:发布订阅模式

    也称之为消息队列模式,或者pubsub模式 发布者发布消息(也可以理解为调用某函数),订阅者会收到消息,并且发布者可以将一些参数传递给订阅者。 是一种常用的参数传递方法,经典的pubsub.js,vue2中的$bus等都是用的这种模式。

    2024年02月19日
    浏览(31)
  • js设计模式——发布订阅模式

    一、概述 发布订阅模式是一种常用的设计模式,它定义了一种一对多的关系,让多个订阅者对象同时监听某一个主题对象,当主题对象发生变化时,它会通知所有订阅者对象,使它们能够自动更新 。 二、优缺点 1. 优点: 实现了发布者和订阅者之间的解耦,提高了代码的可

    2024年02月06日
    浏览(27)
  • 前端开发攻略---从源码角度分析Vue3的Propy比Vue2的defineproperty到底好在哪里。一篇文章让你彻底弄懂响应式原理。

    Vue的响应式到底要干什么? 无非就是要知道当你 读取 对象的时候,要知道它读了。要做一些别的事情 无非就是要知道当你 修改 对象的时候,要知道它改了。要做一些别的事情 所以要想一个办法, 把读取和修改的动作变成一个函数 ,读取和修改的时候分别调用对应的函数

    2024年04月17日
    浏览(36)
  • js 中单例模式、工厂模式、装饰模式、发布订阅模式、适配器模式、

    简单概述:将每个功能拆分到最小化,最后将小功能拼接到一起

    2024年02月11日
    浏览(32)
  • Vue2-全局事件总线、消息的订阅与发布、TodoList的编辑功能、$nextTick、动画与过渡

    🥔:高度自律即自由 更多Vue知识请点击——Vue.js 一种组件间通信的方式,适用于任意组件间通信。通俗理解就是一个定义在所有组件之外的公共嘎达,这个嘎达可以有vm或vc上的同款 $on、$off、$emit ,也可以让所有组件都访问到。要想实现这个事情,只能在 Vue.prototype 上添加

    2024年02月11日
    浏览(31)
  • Vue2.0 的响应式原理 私

    使用的Object.defineProperty()重新定义对象,给data的每个属性都添加了getter和setter方法。这时候会为对象的每个属性创建一个Dep实例  (依赖)。Dep实例可以订阅和通知相关的Watcher实例。,  这一步叫  数据劫持  或者 依赖收集 在数据发生更新后调用 set 时会通知发布者 notify

    2024年02月11日
    浏览(26)
  • 从Vue层面 - 解析发布订阅模式和观察者模式区别

    观察者模式和发布订阅模式作为日常开发中经常使用到的模式,我一直不能做到很好的区分。最近在看Vue的源码,里面设计到了观察者模式,比较感兴趣,就去学习了下,这里做个总结吧。 基于一个 事件中心 ,接收通知的对象是订阅者,需要先订阅某个事件,触发事件的对

    2024年02月15日
    浏览(27)
  • 202 vue2的响应式原理 通俗易懂!

    Object.defineProperty + 依赖追踪 。 在Vue实例化过程中,会 递归 地将 每个数据对象 的 属性 转换为 getter/setter ,并维护一个 依赖收集器(Dep) 。 每个属性 都有一个关联的 watcher ,当 数据发生 变化时 , watcher 会 被通知 并 更新视图 。 Vue 2.x 实现响应式数据: vue实例化 时,会

    2024年02月06日
    浏览(26)
  • Vue2和Vue3响应式原理实现的核心

    Vue.js 是一个开源的渐进式 JavaScript 前端框架,主要用于构建用户界面和单页应用程序(SPA)。Vue.js 可以轻松地与其他库或现有项目集成使用,并被认为是开发响应式数据驱动的现代 Web 应用的一种有效方式。 Vue.js 的核心特点: 响应式数据绑定:Vue.js 可以通过对数据进行双

    2024年02月08日
    浏览(33)
  • 从Vue2到Vue3【七】——Vue2中响应式原理的实现及其缺陷

    内容 链接 从Vue2到Vue3【零】 Vue3简介 从Vue2到Vue3【一】 Composition API(第一章) 从Vue2到Vue3【二】 Composition API(第二章) 从Vue2到Vue3【三】 Composition API(第三章) 从Vue2到Vue3【四】 Composition API(第四章) 从Vue2到Vue3【五】 新的组件(Fragment、Teleport、Suspense) 从Vue2到Vue3【六

    2024年02月15日
    浏览(32)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包