一步一步实现Vue的响应式(对象观测)

实现Vue的响应式(对象观测)

什么是Vue的响应式?

Vue的响应式是指当Vue数据模型中的数据发生变化时,页面中涉及这些数据的部分会自动重新渲染并更新。Vue通过数据劫持方式实现响应式,也就是通过监听对象属性的变化来实现自动触发视图更新。

如何实现Vue的响应式?

Vue的响应式是基于Object.defineProperty()方法实现。该方法能够监听对象属性的变化,并在属性值变更时触发回调函数。

流程如下:

  1. 首先要创建一个Observer对象,采用递归的方式为所有属性添加getter和setter。getter负责收集依赖并返回属性值,setter则负责派发更新通知。
  2. 收集依赖,依赖就是指有哪些Watcher依赖于该属性。在getter中监听数据属性的访问者(Watcher),并将其添加到依赖里面去。
  3. 派发更新,当属性变更后,会在setter中通知对应的依赖进行更新操作。

下面通过两条简单的示例说明:

示例1:响应式对象

// 创建一个Observer对象,为person对象添加getter和setter方法
function Observer(obj) {
  if(!obj || typeof obj !== 'object') {
    return;
  }

  Object.keys(obj).forEach(key => {
    defineReactive(obj, key, obj[key]);
  });
}

// 为对象添加getter和setter方法,来实现响应式
function defineReactive(obj, key, val) {
  // 在getter中将Watcher添加到依赖数组中
  const dep = new Dep();

  Object.defineProperty(obj, key, {
    enumerable: true,
    configurable: true,
    get: function() {
      if (Dep.target) {
        dep.addDep(Dep.target);
      }
      return val;
    },
    set: function(newVal) {
      if (newVal === val) {
        return;
      }
      // 在setter中将值更新,并通知依赖更新
      val = newVal;
      dep.notify();
    }
  });
}

// 收集依赖
class Dep {
  constructor() {
    this.deps = [];
  }

  addDep(dep) {
    this.deps.push(dep);
  }

  notify() {
    this.deps.forEach(dep => {
      dep.update();
    });
  }
}

// 观察者
class Watcher {
  constructor(obj, key, cb) {
    // 相当于Vue中的v-model指令
    this.obj = obj;
    this.key = key;
    this.cb = cb;
    this.value = this.get(); // 将自己添加到Dep.target
  }

  get() {
    Dep.target = this;
    const value = this.obj[this.key]; // 触发getter,将当前Watcher添加到依赖中
    Dep.target = null;
    return value;
  }

  update() {
    this.value = this.get(); // 触发getter,更新value
    this.cb.call(this.obj, this.value); // 执行回调函数,更新data
  }
}

// 测试
const person = {
  name: 'Tom',
  age: 18
};

Observer(person);

new Watcher(person, 'name', function(newVal) {
  console.log(`Name changed to ${newVal}`);
});

person.name = 'Jerry'; // 触发name属性的setter方法

示例2:响应式数组

// 为数组添加getter和setter方法,来实现响应式
const arrayProto = Array.prototype;
const arrayMethods = Object.create(arrayProto);

const methodsToPatch = [
  'push',
  'pop',
  'unshift',
  'shift',
  'splice',
  'sort',
  'reverse'
];

methodsToPatch.forEach(function (method) {
  const original = arrayProto[method];
  Object.defineProperty(arrayMethods, method, {
    enumerable: false,
    configurable: true,
    writable: true,
    value: function mutator(...args) {
      const result = original.apply(this, args);
      const ob = this.__ob__;
      let inserted;
      // 每当数组发生变化时,需要将新加入的值变成一个响应式对象
      switch (method) {
        case 'push':
          inserted = args;
          break;
        case 'unshift':
          inserted = args;
          break;
        case 'splice':
          inserted = args.slice(2);
          break;
      }
      if (inserted) {
        ob.observeArray(inserted);
      }
      ob.dep.notify();
      return result;
    }
  });
});

// 创建一个Observer对象,为数组对象添加getter和setter方法
function observeArray(arr) {
  Object.setPrototypeOf(arr, arrayMethods);
  // 递归为数组元素添加响应式
  for (let i = 0, l = arr.length; i < l; i++) {
    observe(arr[i]);
  }
}

// 测试
const arr = [1, 2, 3];

observeArray(arr);

new Watcher(arr, function (newVal) {
  console.log(`Array changed to ${newVal}`);
});

arr.push(4); // 触发数组的push方法

总结

以上就是实现Vue的响应式(对象观测)的完整攻略。Vue采用Object.defineProperty()实现响应式数据绑定,核心思想是依靠数据劫持和发布-订阅模式来实现。

在实现过程中,需要创建Observer对象、收集依赖、派发更新、为数组添加getter和setter方法等步骤。通过上述两个示例,我们可以更加深入地理解Vue的响应式原理和实现方式,对于Vue应用的开发将有一定的帮助。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:一步一步实现Vue的响应式(对象观测) - Python技术站

(0)
上一篇 2023年5月27日
下一篇 2023年5月27日

相关文章

  • Vue组件中prop属性使用说明实例代码详解

    Vue组件中的prop属性用于向组件传递数据,是组件通信的一种方式。在使用prop属性时,需要在组件实例的props选项中声明传递的属性及其类型。本篇攻略将详细讲解Vue组件中prop属性的使用说明,并提供实例代码作为示例。 1. 声明prop属性 在Vue组件中使用prop属性需要在组件的props选项中声明传递的属性及其类型。例如下面的代码是声明一个名为…

    Vue 2023年5月27日
    00
  • element ui时间日期选择器el-date-picker报错Prop being mutated:”placement”解决方式

    问题描述: 在使用Element UI中的el-date-picker组件时,会出现如下错误: “Prop being mutated, vue will not trigger updates on this case” 同时在控制台中会出现如下警告: Avoid mutating a prop directly since the value will …

    Vue 2023年5月29日
    00
  • Vue中遍历数组的新方法实例详解

    下面我就为您详细讲解“Vue中遍历数组的新方法实例详解”。 介绍 在Vue 2.6.0版本以后,新增了一个数组方法v-for,它主要用于遍历一个数组并渲染每个数组元素。 v-for能够将一个数组映射为一组元素,并为每个元素执行一次模板,因此它的应用场景非常广泛,尤其在将复杂数据渲染到界面上时,更是体现了它的优势。 下面就重点介绍一下v-for在其中的应用。 …

    Vue 2023年5月28日
    00
  • vue data恢复初始化数据的实现方法

    当使用Vue.js时,有时候有必要恢复某些数据的值为初始化值,以便重新开始处理。Vue.js提供了一个简单的方法来实现这个功能。我们可以在Vue实例中定义一个data初始化方法,该方法将在Vue实例被实例化时被调用。然后,我们可以在需要恢复数据的时候调用这个方法来初始化数据。下面是实现方式的详细攻略: 步骤一:定义data初始化方法 在Vue实例中定义一个d…

    Vue 2023年5月28日
    00
  • Vue.js函数式组件的全面了解

    Vue.js函数式组件的全面了解 一、什么是函数式组件 函数式组件是指仅接收 props,并且没有像组件实例这样的状态(也就是 data 选项)的组件。这意味着函数式组件无法像普通组件那样维护自身的状态,但是,由于它们没有状态,所以它们渲染起来开销较小,执行效率更高。函数式组件是 Vue 2.3 新增的特性。 二、如何定义函数式组件 定义函数式组件很简单,直…

    Vue 2023年5月27日
    00
  • Vue实现倒计时小功能

    Vue实现倒计时小功能的完整攻略 在Vue中实现倒计时小功能需要以下几个步骤: 引入Vue组件和相关依赖:首先我们需要在标签中引入Vue.js的相关文件。 <head> <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script&gt…

    Vue 2023年5月29日
    00
  • vue中的过滤器及其时间格式化问题

    下面是关于 Vue 中过滤器的完整攻略,包括时间格式化问题。 什么是过滤器? Vue 中的过滤器是一种函数,用于改变数据的输出形式。可以在模板中使用管道符 | 的形式调用,对数据进行过滤和格式化,例如: {{ data | filter }} 其中 data 是需要过滤的数据,filter 是过滤器的名称,在 Vue 实例中定义。 过滤器有局限性,不能用于修…

    Vue 2023年5月27日
    00
  • vue中img src 动态加载本地json的图片路径写法

    在Vue中,我们使用img标签来展示图片,其中src属性用于指定图片的路径。如果需要动态加载本地json中的图片,我们可以使用相对路径来指定图片的位置。 具体操作步骤如下: 创建一个包含图片路径的json文件。比如说在项目根目录下新建一个名为“images.json”的文件,其内容为: { "imgPath": "./asset…

    Vue 2023年5月28日
    00
合作推广
合作推广
分享本页
返回顶部