一步一步实现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 中Virtual Dom被创建的方法

    Vue 中 Virtual DOM 的创建过程非常重要,它是 Vue 对于前端工程化方案的核心支持,下面将详细讲解 Vue 中 Virtual DOM 被创建的方法。 创建 Virtual DOM 的主要方法 Vue 中创建 Virtual DOM 的过程主要通过以下两个步骤: 通过 render 函数生成 VNode 树 在 Vue 中,通过 render…

    Vue 2023年5月28日
    00
  • vue实现微信公众号h5跳转小程序的示例代码第1/3页

    实现微信公众号H5页面跳转小程序可以使用微信JS-SDK中的wx.miniProgram.navigateTo()方法,下面是Vue实现微信公众号H5跳转小程序的示例代码: 引入微信JS-SDK 首先需要在index.html中引入微信JS-SDK的js文件,其地址为: 调用wx.miniProgram.navigateTo() 在组件中调用wx.miniP…

    Vue 2023年5月27日
    00
  • Vue Router的手写实现方法实现

    让我们来详细讲解“Vue Router的手写实现方法实现”的完整攻略。 什么是Vue Router Vue Router 是Vue.js的官方路由管理器,它和Vue.js的核心深度集成,可以非常方便地实现单页应用的路由功能。 使用Vue Router可以实现以下功能: 动态路由匹配 嵌套路由 命名路由 视图过渡效果 状态管理 Vue Router手写实现 V…

    Vue 2023年5月28日
    00
  • vue3获取ref实例结合ts的InstanceType问题

    获取ref实例是Vue3中常用的一种方式,可以用来访问组件内部的数据和方法。在TypeScript环境下,获取ref实例需要注意InstanceType问题。下面是一份完整的攻略,分为以下几个步骤: 步骤一:创建组件 首先我们需要创建一个Vue3组件,用来演示获取ref实例的过程。这里以一个简单的计数器组件为例: <template> <d…

    Vue 2023年5月27日
    00
  • vue3+vite+ts使用monaco-editor编辑器的简单步骤

    使用vue3+vite+ts并集成monaco-editor编辑器需要经过以下步骤: 步骤一:创建vue3项目 使用vue-cli可以创建一个基础的vue3项目,安装vue-cli: npm install -g @vue/cli 然后执行以下命令创建vue3项目: vue create my-app –preset vite/vue 其中my-app是项…

    Vue 2023年5月28日
    00
  • echarts报错:Error in mounted hook的解决方法

    下面是关于”echarts报错:Error in mounted hook的解决方法”的完整攻略。 什么是“echarts报错:Error in mounted hook”的问题? 当使用 echarts 绘制图表时,有时在控制台会看到一个错误提示:“Error in mounted hook”,通常提示中还会包含一些错误信息,比如:“Cannot read…

    Vue 2023年5月27日
    00
  • 一文详解如何在vue中实现文件预览功能

    下面我将详细讲解如何在Vue中实现文件预览功能。 一、需求 在Web应用中,我们可能需要上传文件,并在上传后进行预览,以便用户确认上传的文件是正确的。因此,我们需要实现文件预览功能。 二、方案 在Vue中实现文件预览功能,通常有以下两种方案: 方案一:使用第三方插件 Vue社区中已经有许多第三方插件实现了文件预览的功能。我们可以通过npm安装相应插件,并根据…

    Vue 2023年5月28日
    00
  • Element table 上下移需求的实现

    接下来我将为您提供实现Element UI表格上下移需求的攻略。这个需求的主要目标是:在Element UI表格中,为用户提供左侧上下移动按钮,使其在表格中对表格行进行上下移动排序。 1. 准备工作 首先,我们需要安装 element-ui 和 lodash,如果您的项目中已经安装了这两个依赖包,可以省略此步骤。 npm install element-ui…

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