vue响应式原理3个步骤数据劫持、依赖收集、派发更新

发布于:2024-04-28 ⋅ 阅读:(40) ⋅ 点赞:(0)

Vue的响应式原理确实是基于数据劫持、依赖收集和派发更新这三个核心步骤实现的。以下是这三个步骤的详细解释:

数据劫持:

数据劫持是Vue响应式原理的第一步,其主要目的是在数据对象的属性上设置访问器属性(getter和setter)。这样,当数据被访问或修改时,Vue就能知道并作出相应的反应。

在Vue中,这通常是通过Object.defineProperty方法实现的。对于每一个数据属性,Vue都会为其添加一个getter和setter。当数据被读取时,getter会被触发,而当数据被修改时,setter会被触发。通过这种方式,Vue就能对数据的变化进行追踪。

2. 依赖收集:

依赖收集是Vue响应式原理的第二步。当组件访问某个数据属性时,Vue会创建一个Watcher实例,并将其添加到该属性的依赖列表中。Watcher实例实际上是一个观察者,它负责在数据变化时更新组件。

这样,当数据属性的值发生变化时,Vue就可以知道哪些组件依赖于这个数据,并通知这些组件进行更新。这就是依赖收集的核心思想。

3. 派发更新:

派发更新是Vue响应式原理的最后一步。当数据属性的值发生变化时,setter会被触发,并通知所有依赖于这个数据的Watcher实例进行更新。

每个Watcher实例都会收到一个通知,告诉它数据已经改变,然后它会根据新的数据重新计算并更新组件。这个过程会递归地发生在整个组件树中,以确保所有相关的组件都能得到更新。

总的来说,Vue的响应式原理就是通过数据劫持来追踪数据的变化,通过依赖收集来知道哪些组件依赖于某个数据,然后通过派发更新来通知这些组件进行更新。这种机制使得Vue能够自动地、高效地处理数据变化,并更新相关的视图。

以下是一个简化版的Vue响应式原理的示例代码,包括数据劫持、依赖收集和派发更新三个步骤的模拟实现。请注意,这个示例是为了演示原理,并没有实现Vue的所有功能和细节。

javascript

// 1. 数据劫持

function defineReactive(data, key, val) {

  // 确保val是对象,以便我们可以递归地转换它的属性

  let dep = new Dep();

  const observer = new Observer(val);

  Object.defineProperty(data, key, {

    enumerable: true,

    configurable: true,

    get() {

      // 添加依赖

      if (Dep.target) {

        dep.addSub(Dep.target);

      }

      return val;

    },

    set(newVal) {

      if (newVal === val) return;

      val = newVal;

      observer.update(); // 新值也需要被观察

      // 派发更新

      dep.notify();

    }

  });

}

// 依赖类

class Dep {

  constructor() {

    this.subs = [];

  }

  addSub(sub) {

    this.subs.push(sub);

  }

  notify() {

    this.subs.forEach(sub => sub.update());

  }

}

// 观察者类

class Watcher {

  constructor(vm, exp, cb) {

    this.cb = cb;

    this.vm = vm;

    this.exp = exp;

    this.value = this.get(); // 初始化执行getter,触发依赖收集

  }

  get() {

    Dep.target = this; // 将当前Watcher实例设为全局目标

    let value = this.vm[this.exp]; // 触发getter,进行依赖收集

    Dep.target = null; // 清理全局目标

    return value;

  }

  update() {

    this.run();

  }

  run() {

    let value = this.vm[this.exp];

    let oldVal = this.value;

    if (value !== oldVal) {

      this.value = value;

      this.cb.call(this.vm, value, oldVal);

    }

  }

}

// 观察者类,用于递归遍历对象的属性并设置getter/setter

class Observer {

  constructor(value) {

    this.value = value;

    this.walk(value);

  }

  walk(obj) {

    Object.keys(obj).forEach(key => {

      defineReactive(obj, key, obj[key]);

    });

  }

  update() {

    this.walk(this.value);

  }

}

// 示例使用

let vm = new Vue({

  data: {

    text: 'Hello Vue!'

  }

});

function updateText() {

  console.log('Text changed: ', vm.text);

}

// 创建观察者

new Watcher(vm, 'text', updateText);

// 修改数据触发更新

vm.text = 'Hello Vue.js!';

// Vue的模拟构造函数

function Vue(options) {

  this.data = options.data;

  observe(this.data);

}

// 初始化数据劫持

function observe(value, vm) {

  if (!value || typeof value !== 'object') {

    return;

  }

  new Observer(value);

}

在这个示例中:

defineReactive 方法用于定义属性的getter和setter,实现了数据劫持。

Dep 类用于存储依赖,并在数据变化时通知这些依赖。

Watcher 类代表依赖,当数据变化时,它会执行更新。

Observer 类用于递归地遍历对象的属性,并调用 defineReactive 方法。

最后,我们创建了一个模拟的 Vue 构造函数,并在构造函数中调用 observe 方法来开始数据的响应式处理。

注意,这个示例是为了教学目的而简化的,实际的Vue.js实现要复杂得多,并且包含了许多优化和边界情况的处理。

 


网站公告

今日签到

点亮在社区的每一天
去签到