Vue reactive函数实现流程详解

Vue Reactive函数实现流程详解

Vue.js 框架的核心就是数据驱动,同时也是以数据为中心来响应视图变化。然而,Vue.js 还支持响应式,因此当数据发生变化时,Vue.js 自动更新视图。

在 Vue.js 中,通过 getter 和 setter 函数来实现数据的响应式。

实现流程

Vue.js 的实现响应式的方式,是通过劫持数据对象的属性来达到的。在对象的 getter 方法中,进行依赖收集;在对象的 setter 方法中,进行依赖触发。

Observer

Vue.js 中通过 Observer 类来实现数据对象属性的劫持,Observer 的作用是将一个正常的 JavaScript 对象转化为一个响应式的对象。

Observer 类的主要工作是深度遍历所有属性,将它们变成响应式属性(即变为 getter 和 setter 函数)。

代码示例:

class Observer {
  constructor(value) {
    this.value = value;
    if (!Array.isArray(value)) {
      this.walk(value);
    }
  }
  // 遍历所有属性并进行监测
  walk(obj) {
    const keys = Object.keys(obj);
    for (let i = 0; i < keys.length; i++) {
      defineReactive(obj, keys[i]);
    }
  }
}

function defineReactive(obj, key) {
  let val = obj[key];
  // 递归实现深度遍历响应式设置
  const childOb = observe(val);
  Object.defineProperty(obj, key, {
    enumerable: true,
    configurable: true,
    get() {
      console.log(`访问${key}`);
      return val;
    },
    set(newVal) {
      if (newVal === val) {
        return;
      }
      val = newVal;
      console.log(`设置${key}的新值为${newVal}`);
      childOb && observe(newVal);
    },
  });
}

function observe(value) {
  if (typeof value !== "object") {
    return;
  }
  return new Observer(value);
}

Dep

Dep 类用于收集依赖和通知更新。在 defineReactive 函数中,每个属性的 getter 函数都会在对应的 dep 实例调用 depend() 方法进行依赖收集,并且每个属性的 setter 函数都会在 dep 派发通知,通知每个依赖进行更新。

在 dep 类中,定义了 dep 实例的 depend() 和 notify() 方法。

代码示例:

let watchId = 0;

// define a watcher class
class Watcher {
  constructor(vm, expOrFn, cb, options) {
    this.vm = vm;
    this.cb = cb;
    this.options = options;
    this.id = ++watchId;

    // apply options
    if (options) {
      this.deep = !!options.deep;
      this.user = !!options.user;
    }

    // get the expression
    if (typeof expOrFn === "function") {
      this.expOrFn = expOrFn;
    } else {
      this.getter = parsePath(expOrFn);
    }

    // trigger the watcher's initial update
    if (this.options.immediate) {
      this.cb();
    } else {
      this.value = this.get();
    }
  }

  get() {
    Dep.target = this;
    const value = this.getter
      ? this.getter(this.vm)
      : this.expOrFn(this.vm);
    Dep.target = undefined;

    return value;
  }

  update() {
    const oldValue = this.value;
    const value = this.get();

    if (this.deep) {
      traverseoldValue(oldValue);
      traversevalue;
    }

    this.value = value;
    this.cb.call(this.vm, value, oldValue);
  }
}

let uid = 0;

// define dep class
class Dep {
  constructor() {
    this.id = uid++;
    this.subs = [];
  }

  addSub(sub) {
    this.subs.push(sub);
  }

  removeSub(sub) {
    remove(this.subs, sub);
  }

  depend() {
    if (Dep.target) {
      Dep.target.addDep(this);
    }
  }

  notify() {
    const subs = this.subs.slice();
    for (let i = 0; i < subs.length; i++) {
      subs[i].update();
    }
  }
}

Dep.target = null;

function traverse(value) {
  _traverse(value, new Set());
}

function _traverse(value, visited) {
  const isA = Array.isArray(value);
  if (!isA && typeof value !== "object") {
    return;
  }
  if (value.__ob__) {
    const depId = value.__ob__.dep.id;
    if (visited.has(depId)) {
      return;
    }
    visited.add(depId);
  }
  if (isA) {
    let i = value.length;
    while (i--) _traverse(value[i], visited);
  } else {
    const keys = Object.keys(value);
    let i = keys.length;
    while (i--) _traverse(value[keys[i]], visited);
  }
}

示例说明

示例一

const vm = {
  name: "Vue",
  author: {
    name: "Evan",
    age: 26,
  },
};
observe(vm);
vm.author.age = 30;

以上示例中,我们定义了一个 data 对象,包含一个嵌套的对象,调用 observe 函数将 vm 对象变成响应式对象。然后修改嵌套对象的属性 age 的值,将值改为 30,此时触发了 setter 函数,并对 watcher 数组中的依赖进行通知更新。

示例二

const vm = {
  list: [1, 2, 3],
};
observe(vm);
vm.list.push(4);

以上示例中,我们定义了一个 data 对象,包含数组 list,调用 observe 函数将 vm 变成响应式对象。然后对数组 list 进行 push 操作,并没有调用任何方法来触发依赖更新,此时依赖应当也进行了更新。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:Vue reactive函数实现流程详解 - Python技术站

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

相关文章

  • vue中 v-for循环的用法详解

    Vue中v-for循环的用法详解 在Vue中,v-for是一种指令,用于循环渲染数据列表。本篇攻略将详细讲解Vue中v-for的用法。 基本用法 v-for指令需要用在具有多个子节点的元素上,它的语法格式为: <div v-for="(item, index) in items" :key="index">…

    Vue 2023年5月27日
    00
  • vue中filters 传入两个参数 / 使用两个filters的实现方法

    在 Vue 中,可以使用 filter 过滤器来处理模板中的数据。filter 在显示数据之前对其进行处理,比如对字符串格式化、将时间格式化等等。 在 Vue 中,我们可以定义一个 filter 来处理一些数据,此时这个 filter 就是一个全局的 filter。可以被任何组件进行调用。 现在我们来详细讲解“vue中filters 传入两个参数 / 使用两…

    Vue 2023年5月27日
    00
  • vue 解决数组赋值无法渲染在页面的问题

    当 Vue 绑定的数据是一个数组时,使用原生的赋值方式(例如 array[0] = newValue)并不会触发组件的重新渲染,因为 Vue 无法识别这种方式的数据变动。为了解决这种问题,需要使用 Vue 提供的特殊方法,或在代码层面做出一些调整。 下面是解决数组赋值无法渲染在页面的问题的完整攻略: 1. 使用特殊方法进行数组数据更新 Vue 提供了一些特殊…

    Vue 2023年5月28日
    00
  • Vue3后台管理系统之创建和配置项目

    下面是对“Vue3后台管理系统之创建和配置项目”的完整攻略: 一、安装Node.js和Vue CLI 在官网https://nodejs.org/下载并安装最新版的Node.js。 打开终端或命令行,运行以下命令安装Vue CLI: npm install -g @vue/cli 验证Vue CLI是否安装成功,运行以下命令: vue –version 如…

    Vue 2023年5月28日
    00
  • Vue.js中this如何取到data和method里的属性详解

    对于Vue.js项目中,我们经常需要在代码中引用Vue实例中的属性或方法。在此过程中,我们需要清楚地了解this关键字的作用和作用域。本文将详细讲解如何在Vue.js中获取data和method中的属性。 this关键字的作用和作用域 在Vue.js中,this关键字是指Vue实例的上下文。Vue实例中的属性和方法都可以通过this在相应的位置中访问。但是,…

    Vue 2023年5月28日
    00
  • Vue3中watch的用法与最佳实践指南

    Vue3中watch的用法与最佳实践指南 在Vue3中,watch是一个用于监听数据变化并进行相应处理的观察者函数。在实际开发中,watch可以提供非常方便的数据响应式处理,因此它是Vue3中非常重要的一部分。在本篇攻略中,我们将深入了解Vue3中watch的用法和最佳实践,以帮助您更好地使用Vue3。 基本用法 在Vue3中,我们可以通过watch选项来定…

    Vue 2023年5月29日
    00
  • Vue3中props和emit的使用方法详解

    下面我会详细讲解“Vue3中props和emit的使用方法详解”的完整攻略。 1. props的使用方法 1.1. 父组件如何向子组件传值? 在Vue3中,可以使用props来实现父组件向子组件传递数据。具体步骤如下: 在子组件中定义需要传入的属性名以及默认值(可选)。 <!– 子组件中定义props –> <template> …

    Vue 2023年5月27日
    00
  • vue下载文件以及文件重命名方式

    下面是关于 Vue 下载文件以及文件重命名方式的完整攻略。 1. 下载文件 在 Vue 中下载文件,通常需要用到 AJAX 请求和 Blob 对象的相关 API。 首先,我们需要在 Vue 组件中定义下载方法: methods: { downloadFile() { axios.get(‘http://example.com/downloads/exampl…

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