iis服务器助手广告广告
返回顶部
首页 > 资讯 > 前端开发 > JavaScript >深入理解Vue响应式原理及其实现方式
  • 627
分享到

深入理解Vue响应式原理及其实现方式

Vue响应式原理Vue响应式原理实现 2023-05-19 20:05:09 627人浏览 薄情痞子
摘要

目录Vue的响应式Vue2的响应式原理vue3的响应式原理深入理解响应式1.数据初始化2.对象的数据劫持Vue的响应式 用过Vue这个框架的人应该都知道,数据驱动是Vue框架的核心,

Vue的响应式

用过Vue这个框架的人应该都知道,数据驱动是Vue框架的核心,数据双向绑定是它的一大特色,根据官方的解释,我们可以比较清晰地去知道响应式的简单原理。

Vue2的响应式原理

当你把一个普通的 javascript 对象传入 Vue 实例作为 data 选项,Vue 将遍历此对象所有的 property,并使用 Object.defineProperty 把这些 property 全部转为 getter/setter。Object.defineProperty 是 ES5 中一个无法 shim 的特性,这也就是 Vue 不支持 IE8 以及更低版本浏览器的原因。

这些 getter/setter 对用户来说是不可见的,但是在内部它们让 Vue 能够追踪依赖,在 property 被访问和修改时通知变更。这里需要注意的是不同浏览器在控制台打印数据对象时对 getter/setter 的格式化并不同,所以建议安装 vue-devtools 来获取对检查数据更加友好的用户界面。

每个组件实例都对应一个 watcher 实例,它会在组件渲染的过程中把“接触”过的数据 property 记录为依赖。之后当依赖项的 setter 触发时,会通知 watcher,从而使它关联的组件重新渲染。

Vue3的响应式原理

实现原理:

通过Proxy(代理): 拦截对象中任意属性的变化,包括:属性值的读写,属性的增加,属性的删除等。

通过Reffect(反射): 对源对象的属性进行操作

new Proxy(data,{
  //拦截读取属性值
  get(target, prop){
    return Reflect.get(target, prop)
  },
  //拦截设置属性值或添加新属性
  set(target, prop, value){
    return Reflect.set(target, prop, value)
  },
  //拦截删除属性
  deleteProperty(target, prop){
    return Reflect.deleteProperty(target, prop)
  }
})

Vue2和Vue3的响应式原理其实有异曲同工之妙,但是Vue3的proxy封装性和独立性相对更强更灵活一些,但是我们看到的这些只是最简单的,也是最基础的一个响应式原理,如果要更深入地去了解Vue是如何利用这一原理去实现框架中的各种双向绑定和数据渲染操作,我们可以对它的源码进行分析。

深入理解响应式

1.数据初始化

new Vue({
  el: "#app",
  router,
  store,
  render: (h) => h(App),
});

这段代码,大家一定非常熟悉。这就是 Vue 实例化的过程 从 new 操作符,咱们可以看出 Vue 其实就是一个构造函数,没啥特别的,传入的参数就是一个对象,我们叫做 options(选项)。

// src/index.js
import { initMixin } from "./init.js";
// Vue就是一个构造函数 通过new关键字进行实例化
function Vue(options) {
  // 这里开始进行Vue初始化工作
  this._init(options);
}
// _init方法是挂载在Vue原型的方法 通过引入文件的方式进行原型挂载需要传入Vue
// 此做法有利于代码分割
initMixin(Vue);
export default Vue;

因为在 Vue 初始化可能会处理很多事情,比如数据处理,事件处理,生命周期处理等等,所以划分不同文件引入利于代码分割。

// src/init.js
import { initState } from "./state";
export function initMixin(Vue) {
  Vue.prototype._init = function (options) {
    const vm = this;
    // 这里的this代表调用_init方法的对象(实例对象)
    //  this.$options就是用户new Vue的时候传入的属性
    vm.$options = options;
    // 初始化状态
    initState(vm);
  };
}

initMixin 把_init 方法挂载在 Vue 原型 供 Vue 实例调用。

// src/state.js
import { observe } from "./observer/index.js";
export function initState(vm) {
  // 获取传入的数据对象
  const opts = vm.$options;
  if (opts.props) {
    initProps(vm);
  }
  if (opts.methods) {
    initMethod(vm);
  }
  if (opts.data) {
    // 初始化data
    initData(vm);
  }
  if (opts.computed) {
    initComputed(vm);
  }
  if (opts.watch) {
    initWatch(vm);
  }
}
// 初始化data数据
function initData(vm) {
  let data = vm.$options.data;
  //   实例的_data属性就是传入的data
  // vue组件data推荐使用函数 防止数据在组件之间共享
  data = vm._data = typeof data === "function" ? data.call(vm) : data || {};
  // 把data数据代理到vm 也就是Vue实例上面 我们可以使用this.a来访问this._data.a
  for (let key in data) {
    proxy(vm, `_data`, key);
  }
  // 对数据进行观测 --响应式数据核心
  observe(data);
}
// 数据代理
function proxy(object, sourceKey, key) {
  Object.defineProperty(object, key, {
    get() {
      return object[sourceKey][key];
    },
    set(newValue) {
      object[sourceKey][key] = newValue;
    },
  });
}

①通过这段代码,就可以得到一个平时开发Vue项目的时候对于我们有很大帮助的信息,即关于数据初始化的顺序依次是 prop>methods>data>computed>watch。关于我们能否在data里面去调用prop的值的问题,如果知道数据渲染的顺序,就迎刃而解了。

②另外通过这段源码,我们还可以获得一个信息,data是用了函数function封装,而不是对象Object,就是为了避免数据在组件间共享,这样我们每个组件才能有独立的变量作用域。

2.对象的数据劫持

对象数据的劫持,其实很好理解,代码中通过递归的方式,把对象中的每个参数都添加了对应的监听器,所以当对象数据发生变化的时候自然就会触发监听器。

这里我们可以得到一个信息,对象只有在初始化阶段的时候进行了监听标记,当我们后续为对象新增参数的时候,必须通过Vue提供的内置函数 s e t 和 set和 set和delete才能对对象参数进行动态操作,不然直接通过Object.xxx去新增参数,这个时候是不具备双向绑定的效果的。

// src/obserber/index.js
class Observer {
  // 观测值
  constructor(value) {
    this.walk(value);
  }
  walk(data) {
    // 对象上的所有属性依次进行观测
    let keys = Object.keys(data);
    for (let i = 0; i < keys.length; i++) {
      let key = keys[i];
      let value = data[key];
      defineReactive(data, key, value);
    }
  }
}
// Object.defineProperty数据劫持核心 兼容性在ie9以及以上
function defineReactive(data, key, value) {
  observe(value); // 递归关键
  // --如果value还是一个对象会继续走一遍odefineReactive 层层遍历一直到value不是对象才停止
  //   思考?如果Vue数据嵌套层级过深 >>性能会受影响
  Object.defineProperty(data, key, {
    get() {
      console.log("获取值");
      return value;
    },
    set(newValue) {
      if (newValue === value) return;
      console.log("设置值");
      value = newValue;
    },
  });
}
export function observe(value) {
  // 如果传过来的是对象或者数组 进行属性劫持
  if (
    Object.prototype.toString.call(value) === "[object Object]" ||
    Array.isArray(value)
  ) {
    return new Observer(value);
  }
}

数组的监听

// src/obserber/index.js
import { arrayMethods } from "./array";
class Observer {
  constructor(value) {
    if (Array.isArray(value)) {
      // 这里对数组做了额外判断
      // 通过重写数组原型方法来对数组的七种方法进行拦截
      value.__proto__ = arrayMethods;
      // 如果数组里面还包含数组 需要递归判断
      this.observeArray(value);
    } else {
      this.walk(value);
    }
  }
  observeArray(items) {
    for (let i = 0; i < items.length; i++) {
      observe(items[i]);
    }
  }
}

数组的监听,是对数组的每个元素进行判断,如果数组中还包含数组则需要递归进行监听,如果非数组元素则直接对数组进行监听设置的操作。

因为对数组下标的拦截太浪费性能 对 Observer 构造函数传入的数据参数增加了数组的判断。

// src/obserber/index.js
class Observer {
  // 观测值
  constructor(value) {
    Object.defineProperty(value, "__ob__", {
      //  值指代的就是Observer的实例
      value: this,
      //  不可枚举
      enumerable: false,
      writable: true,
      configurable: true,
    });
  }
}

最后为了方便我们对数组的操作,Vue对数组的一些常用方法进行了重写,当我们调用这些方法,Vue底层会为我们自动添加对应的监听器,不用让我们再次去对元素进行数据渲染和绑定。

// src/obserber/array.js
// 先保留数组原型
const arrayProto = Array.prototype;
// 然后将arrayMethods继承自数组原型
// 这里是面向切片编程思想(aop)--不破坏封装的前提下,动态的扩展功能
export const arrayMethods = Object.create(arrayProto);
let methodsToPatch = [
  "push",
  "pop",
  "shift",
  "unshift",
  "splice",
  "reverse",
  "sort",
];
methodsToPatch.forEach((method) => {
  arrayMethods[method] = function (...args) {
    //   这里保留原型方法的执行结果
    const result = arrayProto[method].apply(this, args);
    // 这句话是关键
    // this代表的就是数据本身 比如数据是{a:[1,2,3]} 那么我们使用a.push(4)  this就是a  ob就是a.__ob__ 这个属性就是上段代码增加的 代表的是该数据已经被响应式观察过了指向Observer实例
    const ob = this.__ob__;
    // 这里的标志就是代表数组有新增操作
    let inserted;
    switch (method) {
      case "push":
      case "unshift":
        inserted = args;
        break;
      case "splice":
        inserted = args.slice(2);
      default:
        break;
    }
    // 如果有新增的元素 inserted是一个数组 调用Observer实例的observeArray对数组每一项进行观测
    if (inserted) ob.observeArray(inserted);
    // 之后咱们还可以在这里检测到数组改变了之后从而触发视图更新的操作--后续源码会揭晓
    return result;
  };
});

到此这篇关于深入理解Vue响应式原理及其实现方式的文章就介绍到这了,更多相关Vue响应式原理内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!

--结束END--

本文标题: 深入理解Vue响应式原理及其实现方式

本文链接: https://www.lsjlt.com/news/213698.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • 深入理解Vue响应式原理及其实现方式
    目录Vue的响应式Vue2的响应式原理Vue3的响应式原理深入理解响应式1.数据初始化2.对象的数据劫持Vue的响应式 用过Vue这个框架的人应该都知道,数据驱动是Vue框架的核心,...
    99+
    2023-05-19
    Vue响应式原理 Vue响应式原理实现
  • 深入理解Vue3响应式原理
    目录响应式原理手写实现1、实现Reactive2、实现依赖的收集和触发effect影响函数收集/添加依赖触发依赖3、移除/停止依赖衍生类型1、实现readonly2、实现shallo...
    99+
    2022-12-19
    vue3响应式原理精讲 vue3 响应式 vue 响应式原理
  • Vue深入讲解数据响应式原理
    目录响应式是什么如何实现数据响应式实现对象属性拦截通用的劫持方案总结响应式是什么 简而言之就是数据变页面变 如何实现数据响应式 在Javascript里实现数据响应式一般有俩种方案,...
    99+
    2022-11-13
  • Vue响应式原理深入分析
    目录1.响应式数据和副作用函数2.响应式数据的基本实现3.设计一个完善的响应式系统1.响应式数据和副作用函数 (1)副作用函数 副作用函数就是会产生副作用的函数。 function ...
    99+
    2022-12-30
    Vue响应式原理 Vue响应式框架 Vue响应式数据
  • 深入理解Vue的数据响应式
    目录1. ES语法的getter和setter2. ES语法的 defineProperty3. Vue对数据的代理和监听4. Vue的数据响应式1. ES语法的getter和set...
    99+
    2022-11-12
  • 一文带你深入理解Vue3响应式原理
    目录 响应式原理2.0的不足reactive和effect的实现effect track trigger测试代码递归实现reactive总结 响应式原理 Vue2...
    99+
    2022-11-13
    vue3响应式原理精讲 vue3 响应式 vue 响应式原理
  • vue响应式原理与双向数据的深入解析
    了解object.defineProperty 实现响应式 清楚 observe/watcher/dep 具体指的是什么 了解 发布订阅模式 以及其解决的具体问题 在Javascri...
    99+
    2022-11-12
  • VUE响应式原理的实现详解
    目录总结前言 相信vue学习者都会发现,vue使用起来上手非常方便,例如双向绑定机制,让我们实现视图、数据层的快速同步,但双向绑定机制实现的核心数据响应的原理是怎么样的呢,接下来让我...
    99+
    2022-11-13
  • 图解Vue 响应式流程及原理
    目录阅读本文能够帮助你什么?一、组件化流程1. 整个new Vue阶段做了什么?2. 普通dom元素如何渲染到页面?3. 组件如何渲染到页面?4. Vue组件化简化流程二、响应式流程...
    99+
    2022-11-13
  • 详解VUE响应式原理
    目录1、响应式原理基础2、核心对象:Dep与Watcher3、收集依赖与更新依赖3.1 收集依赖3.2 更新依赖4、源码调试4.1 测试的页面代码1、对象说明2、Dep与Watche...
    99+
    2022-11-12
  • go 原子操作的方式及实现原理全面深入解析
    目录什么是原子操作?原子操作的使用场景是什么?原子操作是怎么实现的?x86 LOCK 的时候发生了什么原子操作有什么特征?go 里面有哪些原子操作?增减(Add)比较并交换(Comp...
    99+
    2023-05-16
    go 原子操作方式原理 go 原子操作
  • Vue响应式原理模拟实现原理探究
    目录前置知识数据驱动数据响应式的核心原理Vue 2.xVue 3.x发布订阅和观察者模式发布/订阅模式观察者模式Vue响应式原理模拟实现VueObserver对data中的属性进行监...
    99+
    2022-11-13
  • 深入浅析vue处理响应式数据的方法
    本篇文章带大家学习vue,聊聊vue如何处理响应式数据?希望对大家有所帮助!关于vue的响应式数据,你可能有很多疑惑。比如为什么要改用proxy比如为什么有reactive和ref两个api比如vue是如何实现响应式的?其实这些在源码中,都...
    99+
    2023-05-14
    前端 Vue.js
  • Vue双向数据绑定与响应式原理深入探究
    目录一、双向数据绑定和数据响应式是相同的吗二、双向数据绑定的原理三、数据响应式的原理与实现一、双向数据绑定和数据响应式是相同的吗 不相同,原因如下: 响应式是指通过数据区驱动DOM视...
    99+
    2022-11-13
    Vue 双向数据绑定 Vue 响应式原理
  • Vue响应式流程及原理是什么
    本文小编为大家详细介绍“Vue响应式流程及原理是什么”,内容详细,步骤清晰,细节处理妥当,希望这篇“Vue响应式流程及原理是什么”文章能帮助大家解决疑惑,下面跟着小编的思路慢慢深入,一起来学习新知识吧。一、组件化流程1. 整个new Vue...
    99+
    2023-07-02
  • Vue中响应式系统实现原理图文讲解
    目录框架组件结构源码解析在哪里绑定vue实例与watcher实例vue实例中的data响应式初始化对Object的变化监测框架 每个组件实例都会对应一个watcher实例,在组件渲...
    99+
    2023-03-20
    Vue响应式系统 Vue响应式原理
  • vue中v-model和响应式的实现原理解析
    目录v-model响应式实现v-model 首先要了解v-model就是vue帮我们封装的语法糖,真正实现靠的还是:v-bind:绑定响应式数据 触发 input 事件 并传递数据 ...
    99+
    2022-11-13
  • 详解vue3 响应式的实现原理
    目录核心设计思想Vue.js 2.x 响应式Vue.js 3.x 响应式依赖收集:get 函数派发通知:set 函数总结源码参考核心设计思想 除了组件化,Vue.js 另一个核心设计...
    99+
    2022-11-13
  • Vue响应式系统的原理详解
    目录vue响应式系统的基本原理1.回顾一下Object.defineProperty的用法2.实战1:使用 Object.defineProperty 对 person的age属性 ...
    99+
    2022-11-12
  • Vue响应式原理的示例详解
    Vue 最独特的特性之一,是非侵入式的响应系统。数据模型仅仅是普通的 JavaScript 对象。而当你修改它们时,视图会进行更新。聊到 Vue 响应式实现原理,众多开发者都知道实现...
    99+
    2022-11-13
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作