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事件的基本操作你知道吗

    当我们使用Vue构建应用程序时,事件处理是至关重要的一部分。Vue提供了许多内置的指令和事件,可以让我们轻松地处理用户操作并响应状态变化。在本篇攻略中,我们将深入探讨Vue中事件的基本操作,同时提供一些示例说明,帮助读者更好地理解。 Vue事件概述 在Vue中,我们可以使用v-on指令来监听DOM事件。该指令可以添加到任何可以触发事件的HTML元素上,例如按…

    Vue 2023年5月27日
    00
  • Vue用v-for给src属性赋值的方法

    针对“Vue用v-for给src属性赋值”的问题,可以采用以下两种方法进行实现。 方法一:使用计算属性 计算属性是 Vue 中的一个重要概念,它们可以将表达式封装为一个函数,通过计算得出最终值。使用计算属性可以将 v-for 循环中的数据动态绑定到 img 标签的 src 属性上。 <template> <div> <img v…

    Vue 2023年5月28日
    00
  • mpvue 单文件页面配置详解

    我来为你详细讲解“mpvue 单文件页面配置详解”的完整攻略。 mpvue 单文件页面配置详解 1. 简介 mpvue 是一款使用 Vue.js 开发小程序的前端框架,可在小程序原生 API 基础上,结合 Vue.js 语法规范进行开发。 在 mpvue 中,我们可以通过单文件组件(SFC)的形式,实现对小程序页面的开发和配置。通过配置 SFC 的 temp…

    Vue 2023年5月27日
    00
  • vue中实现路由跳转的三种方式超详细教程

    接下来我将为你详细讲解“vue中实现路由跳转的三种方式超详细教程”。 背景介绍 Vue是一款流行的JavaScript框架。Vue Router是Vue的官方路由管理器。在Vue中,可以使用Vue Router实现页面路由跳转。 本教程将介绍Vue Router中实现路由跳转的三种方式。这三种方式分别是: 使用<router-link>标签进行跳…

    Vue 2023年5月29日
    00
  • 基于Vue实现后台系统权限控制的示例代码

    基于Vue实现后台系统权限控制的示例代码,可分为以下几个步骤: 实现路由拦截 在Vue项目中,可以通过element-ui的router插件实现路由拦截。在路由配置文件中,通过设置meta字段的requireAuth属性来实现对需要授权的页面进行拦截。示例代码如下: import Vue from ‘vue’; import VueRouter from ‘…

    Vue 2023年5月27日
    00
  • 关于axios配置多个请求地址(打包后可通过配置文件修改)

    对于axios配置多个请求地址,并且需要通过配置文件进行修改,可以通过以下步骤来实现: 安装axios库 首先,需要安装axios库,在命令行中输入以下命令: npm install axios 创建config文件夹及相关配置文件 在项目根目录下创建config文件夹,并在其中创建不同环境的配置文件(如dev.js、prod.js)。以dev.js为例,假…

    Vue 2023年5月28日
    00
  • vue实现桌面向网页拖动文件的示例代码(可显示图片/音频/视频)

    十分感谢您的提问。下面是我对”vue实现桌面向网页拖动文件的示例代码(可显示图片/音频/视频)”的解答。 首先,我们需要了解一下Drag and Drop API的基本用法。该API是在HTML5中引入的,并允许用户将元素拖放到指定的目标位置上。接下来,我们将根据这一概念给出完整的攻略。 1. 实现基本的拖放功能 下面是一个基本的HTML结构,它包含了两个d…

    Vue 2023年5月28日
    00
  • 详解Vue2.0 事件派发与接收

    下面我会详细讲解“详解Vue2.0 事件派发与接收”的完整攻略。 什么是事件派发与接收 在Vue中,事件可以从父组件向子组件传递(事件派发),也可以从子组件向父组件传递(事件接收)。这种事件的传递机制,可以实现组件之间的通信和数据交互,非常强大。 事件派发 在父组件中,我们可以通过$emit方法派发事件,传递数据给子组件。代码示例如下: <templa…

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