Vue2:怎么实现响应式双向绑定?

输入图片描述

一、vue2怎么实现双向绑定原理

在Vue2中,双向绑定的实现是通过Vue2的响应式系统和数据绑定机制来完成的。下面是Vue2实现双向绑定的简要原理:

  1. 数据劫持:当创建Vue实例时,Vue2会对data选项中的所有属性进行数据劫持。这通过使用Object.defineProperty()方法将每个属性转换为getter和setter,并在数据被访问或修改时触发相应的操作。

  2. Watcher监听器的创建:在编译模板时,Vue2会解析模板中的指令和表达式,并创建相应的Watcher监听器。每个Watcher与一个数据关联,负责监听该数据的变化,并更新相关的视图。

  3. 依赖收集:模板编译过程中,Watcher会自动收集所依赖的数据(如data、computed、props等),建立起数据与Watcher之间的关联关系。这样,当数据发生改变时,就能找到需要更新的对应Watcher,并通知其进行更新。

  4. 发布-订阅模式:当数据发生变化时,数据劫持会触发setter函数,并调用发布-订阅模式的调度中心。调度中心会根据依赖收集得到的相关Watcher列表,依次通知这些Watcher进行更新。

  5. 视图更新:每个被通知的Watcher都会执行相应的更新操作,比如重新渲染视图或更新DOM元素。这样,数据的变化能够实时反映到视图中。

需要注意的是,Vue会将多个数据的变化收集起来,在下一个事件循环或异步任务中进行批量更新。这样可以减少不必要的重复渲染和DOM操作,提高性能。

通过数据劫持、Watcher监听器和发布-订阅模式的协同工作,Vue能够实现数据与视图的自动同步更新,并提供高效的响应式系统。

这种设计模式的优势在于解耦合、灵活性和性能优化,使得Vue能够以简洁、高效的方式处理复杂的数据变化和视图更新。

输入图片描述

二、vue2有数据劫持了,为什么还要使用Watcher监听器?

虽然Vue2的数据劫持能够检测到数据的变化,但它仅仅负责将数据属性转换为getter和setter,并在数据被访问或修改时触发相应的操作。Watcher监听器的作用是通过观察数据的变化并更新相关的视图。

以下是Watcher监听器的几个主要作用:

  1. 依赖收集:Watcher会自动收集模板中使用到的响应式数据(如data、computed、props等),建立起数据与Watcher之间的关联关系。这样,当数据发生改变时,就能找到需要更新的对应Watcher,并通知其进行更新。

  2. 视图更新:当响应式数据发生变化时,Watcher会接收到通知,并触发相应的更新操作,比如重新渲染视图或更新DOM元素。

  3. 惰性求值:Watcher还支持计算属性(computed)和侦听器(watcher),它们可以根据数据的变化动态计算和派发其他的更新。

由于Vue2使用虚拟DOM的技术,直接操作真实DOM的代价较高,因此Vue2采用异步的方式来批量处理数据变化和视图更新。而Watcher在数据变化后进行异步调度,保证了组件更新的效率和性能。

总结起来,数据劫持只是Vue2实现响应式的一部分,而Watcher监听器则负责收集依赖、触发视图更新和实现计算属性等功能。Watcher是Vue2响应式系统中的重要组成部分,它确保了数据变化能够正确地反映到视图中,从而实现了双向绑定的效果。

三、vue2有数据劫持和Watcher监听器,为什么还要发布-订阅模式?

在Vue2中,除了数据劫持和Watcher监听器之外,还使用了发布-订阅模式(Pub-Sub Pattern)来进一步优化响应式系统的效率和灵活性。

发布-订阅模式是一种解耦合的设计模式,其中有一个调度中心(通常称为事件总线或消息中心),负责管理订阅者(观察者)和发布者(被观察者)之间的通信。当发布者的状态发生变化时,它会通知调度中心,然后调度中心再将相应的消息传递给所有订阅者。

在Vue2中,发布-订阅模式用于建立数据的变化与视图更新之间的联系,以实现组件中数据和视图的同步。

以下是发布-订阅模式在Vue2中的主要作用:

  1. 批量更新:当多个数据同时发生变化时,Vue2会将这些变化收集起来,并在下一个事件循环中进行批量更新,从而减少不必要的重复渲染。

  2. 异步更新:由于视图更新可能涉及到耗时的DOM操作,Vue2采用异步更新策略,即在下一个事件循环中执行视图更新。这样可以避免频繁的DOM操作,提高性能。

  3. 懒执行:Vue2对于计算属性(computed)和侦听器(watcher)采用了懒执行的方式,只有在真正需要获取计算属性的值或监听的数据发生变化时才会进行相应的求值和派发。

  4. 分发通知:发布-订阅模式通过调度中心将数据的变化通知给相关的Watcher,使其能够被正确触发和更新。

通过使用发布-订阅模式,Vue2能够更加高效地捕获数据的变化,并在适当的时候进行批量、异步的视图更新。这有效地减少了不必要的渲染和提高了性能。

需要注意的是,Vue2内部实现了自己的发布-订阅系统,并非完全遵循经典的发布-订阅模式。这也是Vue2能够提供高效而灵活的响应式系统的关键之一。

四、vue2是如何收集依赖的?

Vue2通过Watcher和Dep(Dependency)对象来进行依赖的收集。

在Vue2中,当模板编译过程中遇到绑定表达式(如{ { message }})或指令(如v-model、v-bind)时,会创建对应的Watcher对象,并建立与相关数据属性之间的关联。这些Watcher对象负责监听数据变化并更新视图。

同时,每个数据属性都有一个对应的Dep对象,用于存储依赖于该数据属性的Watcher对象。这个Dep对象会管理多个Watcher对象,并在数据发生变化时通知这些Watcher进行更新。

具体的依赖收集过程如下:

  1. 在编译阶段,当遇到绑定表达式或指令时,会创建一个Watcher对象,并将其添加到当前活动的Watcher栈中。

  2. 在访问数据属性值时,数据劫持会触发getter函数。此时,Dep.target(当前活动的Watcher对象)会被设置为这个Watcher。

  3. 当getter函数执行时,会将Dep.target(即Watcher对象)添加到该数据属性的Dep依赖列表中。

  4. 重复步骤2和3,直到整个模板编译完成,所有的依赖关系都被收集起来。

  5. 当数据属性发生变化时,数据劫持会触发setter函数,并通知对应Dep对象,然后Dep对象再通知注册的所有Watcher对象进行更新。

通过这种机制,一旦数据属性发生变化,Vue2能够准确地找到依赖于该数据属性的Watcher对象,并触发它们进行视图的更新。

需要注意的是,在模板编译过程中,只有被绑定的数据属性才会被收集依赖。而不被绑定的数据属性则不会触发getter函数,也就不会将对应的Watcher添加到Dep的依赖列表中。这样可以避免不必要的依赖收集和更新。

通过Watchers、Dep对象以及Vue2的响应式机制,实现了高效的依赖收集和视图更新,保证了数据与视图之间的同步。

五、实现一个简单的vue2响应式双向绑定原理的代码及解析

// 数据劫持
function defineReactive(data, key, value) {
    
    
  let dep = new Dep();

  Object.defineProperty(data, key, {
    
    
    enumerable: true,
    configurable: true,
    get() {
    
    
      if (Dep.target) {
    
    
        dep.addWatcher(Dep.target);
      }
      return value;
    },
    set(newValue) {
    
    
      if (newValue !== value) {
    
    
        value = newValue;
        dep.notify();
      }
    },
  });
}

// Watcher 监听器
class Watcher {
    
    
  constructor(vm, key, callback) {
    
    
    this.vm = vm;
    this.key = key;
    this.callback = callback;

    // 将当前Watcher实例指定给Dep类的静态属性target
    Dep.target = this;
    // 触发一次getter,进行依赖收集
    this.vm[this.key];
    Dep.target = null; // 重置Dep类的静态属性target
  }

  // 通过回调函数更新视图
  update() {
    
    
    this.callback.call(this.vm, this.vm[this.key]);
  }
}

// 发布-订阅模式的调度中心
class Dep {
    
    
  constructor() {
    
    
    this.watchers = [];
  }

  addWatcher(watcher) {
    
    
    this.watchers.push(watcher);
  }

  notify() {
    
    
    this.watchers.forEach((watcher) => {
    
    
      watcher.update();
    });
  }
}

// Vue类
class Vue {
    
    
  constructor(options) {
    
    
    this.data = options.data;

    // 实现数据劫持
    Object.keys(this.data).forEach((key) => {
    
    
      defineReactive(this.data, key, this.data[key]);
    });

    // 创建Watcher对象,监听数据变化
    new Watcher(this, 'message', this.updateMessage);

    // 测试数据变化后是否能自动更新视图
    setTimeout(() => {
    
    
      this.data.message = 'Hello, Vue!';
    }, 2000);
  }

  // 数据变化时的回调函数
  updateMessage(newValue) {
    
    
    console.log('Updated message:', newValue);
  }
}

// 测试
const vm = new Vue({
    
    
  data: {
    
    
    message: 'Hello, World!',
  },
});

在上述代码中,首先实现了defineReactive函数用于数据劫持。然后定义了Watcher类作为监听器,它负责收集依赖和触发更新。最后,创建了一个简单的Vue类,并在构造函数中进行数据劫持和创建Watcher对象。

当数据属性发生变化时,会触发相应的setter函数并通知Dep调度中心,然后调度中心会依次通知相关的Watcher对象进行更新。

通过该示例,可以看到当data.message的值变化时,会自动触发updateMessage方法,输出更新后的消息。

这个简单的示例演示了Vue2中数据劫持、Watcher监听器和发布-订阅模式的基本原理,但并不涵盖Vue完整的功能。Vue源码中实际的实现更为复杂和全面,包含了更多的细节和优化。但这个示例可以帮助理解它们的基本工作原理。

六、结语

牵手 持续为你分享各类知识和软件 ,欢迎访问、关注、讨论 并留下你的小心心❤

猜你喜欢

转载自blog.csdn.net/weixin_58582793/article/details/131305041
今日推荐