Vue reactive函数实现流程详解

yizhihongxing

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日

相关文章

  • Vue2.0子同级组件之间数据交互方法

    当我们在Vue2.0中开发应用时,会遇到子组件之间需要传递数据的情况,这时候我们可以使用父子组件传参、eventBus、vuex、$attrs和$emit等方法来实现子组件之间的数据交互。 父子组件传参 父子组件之间传参是Vue2.0提供的最基本的数据交互方式,其核心思想是通过props属性将父组件的数据传递到子组件中,子组件通过props接收这些数据,从而…

    Vue 2023年5月28日
    00
  • vue 如何实现表单校验

    下面是 “Vue 如何实现表单校验” 的完整攻略。 使用 Vue.js 实现表单校验 Vue.js 做为一款流行的前端框架,提供了很好的表单校验的支持。Vue.js 2.x 版本提供了 “v-model” 指令和 “validate” 方法,可以让我们快速方便地实现表单校验。 下面将介绍两个示例,来详细讲解 Vue.js 如何实现表单校验。 示例一:基础校验…

    Vue 2023年5月27日
    00
  • 浅谈如何优雅处理JavaScript异步错误

    当我们在JavaScript中处理异步操作的时候,难免会遇到一些错误,如何优雅地处理这些错误是很重要的。以下是几条有用的攻略: 1. Promise捕获错误 在处理异步任务的时候,我们通常会使用Promise。我们可以通过Promise的catch方法来捕获Promise中的错误,然后进行处理。 fetch(‘https://api.example.com’…

    Vue 2023年5月28日
    00
  • 解析vue中的$mount

    下面为你详细讲解解析Vue中的$mount的完整攻略: 1.概述 $mount是Vue实例化后挂载到DOM节点的入口函数,用于手动挂载不适用于el选项的情况。 根据不同的使用场景,$mount呈现出不同的表现,如Static Rendering、Client Side Rendering、Server Side Rendering等. 在解析$mount之前…

    Vue 2023年5月27日
    00
  • Vue中的v-for列表循环示例详解

    针对“Vue中的v-for列表循环示例详解”,下面给出完整的攻略: 一、什么是v-for? v-for是Vue.js提供的一个用于循环渲染页面的指令,它可以循环遍历数据,生成对应的DOM元素,并将其渲染到页面上。 常见场景: 在数据较多的情况下,使用v-for可以更加方便的渲染数据; 使用v-for可以控制生成的DOM元素,可以动态增删改变以及数据操作等。 …

    Vue 2023年5月29日
    00
  • vue编译打包本地查看index文件的方法

    当我们使用Vue进行项目开发时,我们需要编译打包项目以便在浏览器环境中运行。但是,对于有些开发者来说,直接在浏览器中查看页面效果并不够方便,因此我们需要一种方法能够让我们在本地直接查看打包后的index文件效果。下面是具体的步骤: 步骤一:安装http-server http-server是一个简单的零配置命令行HTTP服务器,它可以让我们在本地快速启动静态…

    Vue 2023年5月28日
    00
  • Vue判断字符串(或数组)中是否包含某个元素的多种方法

    关于Vue中判断字符串或数组中是否包含某个元素的方法主要有以下几种方式: 字符串判断 includes ES6中新增了字符串方法includes,它返回一个布尔值,表示当前字符串是否包含传入的字符或字符串。 const str = ‘hello world’ console.log(str.includes(‘he’)) // true console.lo…

    Vue 2023年5月27日
    00
  • vue3中使用router4 keepalive的问题

    请跟我一起详细了解“vue3中使用router4 keepalive的问题”的完整攻略。 什么是keep-alive <keep-alive>是Vue.js提供的一个内置组件,它用于缓存组件,可以防止组件重复渲染以提高性能。<keep-alive>的最常用法是动态地根据路由渲染不同的组件,例如: <template> &l…

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