博客地址:https://ainyi.com/93

vue3 响应式数据放弃了 Object.defineProperty,而使用Proxy来代替它

我们知道,在 vue2 中,实现数据监听是使用Object.defineProperty --> 实现方法可看:vue 数据双向绑定原理

而这个方法有缺点,并且不能实现数组和对象的部分监听情况;具体也可以看我之前写的一篇博客:关于 Vue 不能 watch 数组 和 对象变化的解决方案

最新的 Proxy,相比 vue2 的 Object.defineProperty,能达到速度加倍、内存减半的成效。

具体是怎么实现、以及对比旧的实现方法为啥能有速度加倍、内存减半的特性

下面来聊聊

Vue 初始化过程

Vue 的初始化过程,分别有Observer、Compiler和Watcher

当我们 new Vue 的时候,会调用Observer,通过 Object.defineProperty 遍历 vue 对象的 data、computed 或者 props(如果是组件的话)的所有属性进行监听。同时通过Compiler解析模板指令,解析到属性后就 new 一个Watcher并绑定更新函数到 watcher 当中,Observer 和 Compiler 就通过属性来进行关联

如上,当 Observer 中的 setter 检测到属性值改变的时候,就调用属性对应的所有 watcher 调用更新函数,从而更新到属性对应的 dom

Object.defineProperty

来个简单的 Object.defineProperty 例子

  1. class Observer {
  2. constructor(data) {
  3. // 遍历参数 data 的属性,给添加到 this 上
  4. for(let key of Object.keys(data)) {
  5. if(typeof data[key] === 'object') {
  6. data[key] = new Observer(data[key]);
  7. }
  8. Object.defineProperty(this, key, {
  9. enumerable: true,
  10. configurable: true,
  11. get() {
  12. console.log('你访问了' + key)
  13. return data[key]
  14. },
  15. set(newVal) {
  16. console.log('你设置了' + key)
  17. console.log('新的' + key + '=' + newVal)
  18. if(newVal === data[key]) {
  19. return
  20. }
  21. data[key] = newVal
  22. }
  23. })
  24. }
  25. }
  26. }
  27. const obj = {
  28. name: 'app',
  29. age: '18',
  30. a: {
  31. b: 1,
  32. c: 2,
  33. },
  34. }
  35. const app = new Observer(obj);
  36. app.age = 20;
  37. console.log(app.age);
  38. app.newPropKey = '新属性';
  39. console.log(app.newPropKey);

输出如下:

  1. 你设置了age
  2. 新的age=20
  3. 你访问了age
  4. 20
  5. 新属性

从上面可以知道:

  1. Object.defineProperty 需要遍历所有的属性,这就造成了如果 vue 对象的 data/computed/props 中的数据规模庞大,那么遍历起来就会慢很多
  2. 同理,如果 vue 对象的 data/computed/props 中的数据规模庞大,那么 Object.defineProperty 需要监听所有的属性的变化,那么占用内存就会很大

Proxy

再来看看 Proxy

Proxy 对象用于定义基本操作的自定义行为(如属性查找,赋值,枚举,函数调用等)

可以理解为在对象之前设置一个”拦截“,当监听的对象被访问的时候,都必须经过这层拦截。可以在这拦截中对原对象处理,返回需要的数据格式

也就是无论访问对象的什么属性,之前定义的或是新增的属性,都会走到拦截中进行处理。这就解决了之前所无法监听的问题

官方例子:

  1. const p = new Proxy(target, handler)

参数

  • target: 要使用 Proxy 包装的目标对象(可以是任何类型的对象,包括原生数组,函数,甚至另一个代理)
  • handler:一个通常以函数作为属性的对象,各属性中的函数分别定义了在执行各种操作时代理 p 的行为

来个实际 Proxy 例子

  1. const obj = {
  2. name: 'krry',
  3. age: 24,
  4. others: {
  5. mobile: 'mi10',
  6. watch: 'mi4'
  7. }
  8. }
  9. const p = new Proxy(obj, {
  10. get(target, key, receiver) {
  11. console.log('查看的属性为:' + key);
  12. return Reflect.get(target, key, receiver);
  13. },
  14. set(target, key, value, receiver) {
  15. console.log('设置的属性为:' + key);
  16. console.log('新的属性:' + key, '值为:' + value);
  17. Reflect.set(target, key, value, receiver);
  18. }
  19. })
  20. p.age = 22
  21. console.log(p.age)
  22. console.log('--------')
  23. p.single = 'NO'
  24. console.log(p.single)
  25. console.log('--------')
  26. p.others.shoe = 'boost'
  27. console.log(p.others.shoe)

输出如下:

  1. 设置的属性为:age
  2. 新的属性:age 值为:22
  3. 查看的属性为:age
  4. 22
  5. --------
  6. 设置的属性为:single
  7. 新的属性:single 值为:NO
  8. 查看的属性为:single
  9. NO
  10. --------
  11. 查看的属性为:others
  12. 查看的属性为:others
  13. boost

由上可知,新增或编辑属性,并不需要重新添加响应式处理,都能监听的到

因为 Proxy 是对对象的操作,只要你访问对象,就会走到 Proxy 的逻辑中

Reflect 是一个内置的对象,它提供拦截 JavaScript 操作的方法。这些方法与proxy handlers的方法相同。Reflect不是一个函数对象,因此它是不可构造的

区别

Proxy 和 Object.defineProperty 的使用方法看似很相似,其实 Proxy 是在更高维度上去拦截属性的

Object.defineProperty

Vue2 中,对于给定的 data:如 { count: 1 },是需要根据具体的 key 也就是 count,去对 get 和 set 进行拦截,也就是:

  1. Object.defineProperty(data, 'count', {
  2. get() {},
  3. set() {},
  4. })

必须预先知道要拦截的 key 是什么,这也就是为什么 Vue2 里对于对象上的新增属性无能为力,所以 Vue 初始化的过程中需要遍历 data 来挟持数据变化,造成速度变慢,内存变大的原因

Proxy

而 Vue3 所使用的 Proxy,则是这样拦截的:

  1. new Proxy(data, {
  2. get(key) { },
  3. set(key, value) { },
  4. })

可以看到,proxy 不需要关心具体的 key,它去拦截的是 修改 data 上的任意 key 和 读取 data 上的任意 key

所以,不管是已有的 key 还是新增的 key,都会监听到

但是 Proxy 更加强大的地方还在于 Proxy 除了 get 和 set,还可以拦截更多的操作符,具体可看 MDN

兼容性

Proxy 对 IE 不友好,vue3 在检测到使用 IE 的情况下(包括 IE11),会自动降级为 Object.defineProperty 的数据监听系统

博客地址:https://ainyi.com/93

Vue3 为何使用 Proxy 实现数据监听的更多相关文章

  1. 谈谈数据监听observable的实现

    一.概述 数据监听实现上就是当数据变化时会通知我们的监听器去更新所有的订阅处理,如: var vm = new Observer({a:{b:{x:1,y:2}}}); vm.watch('a.b.x ...

  2. Blazor和Vue对比学习(基础1.8):Blazor中实现计算属性和数据监听

    1.7章<传递UI片断>,需要做几个案例,这部分暂停消化几天.我们先把基础部分相对简单的最后两章学习了. 计算属性和数据监听是Vue当中的概念,本质上都是监听数据的变化,然后做出响应.两者 ...

  3. Python黑客编程基础3网络数据监听和过滤

    网络数据监听和过滤 课程的实验环境如下: •      操作系统:kali Linux 2.0 •      编程工具:Wing IDE •      Python版本:2.7.9 •      涉及 ...

  4. vue中的数据监听以及数据交互

    现在我们来看一下vue中的数据监听事件$watch, js代码: new Vue({ el:"#div", data:{ arr:[,,] } }).$watch("ar ...

  5. Vue获取dom和数据监听

    Vue获取dom对象 在js和jq中我们都能获取dom对象例如 // 获取id=1的div标签 <div id=d1>dom对象</div> // js语法 let ele = ...

  6. vue2.0 之计算属性和数据监听

    计算属性computed <template> <div> <input type="text" name="" v-model= ...

  7. Vue之数据监听存在的问题

    Vue之数据监听 当数据监听的是列表时,数据发生改变,不会被监听到. // 用$set修改数组中的数组能够被监听 // app.$set(this.hobby, 0, "爱你哦") ...

  8. 设置USB数据监听

    设置USB数据监听   在Kali Linux中,USB也是作为一个通信端口进行存在.常见的鼠标.键盘.U盘都是通过USB接口传输数据.所以,对于USB接口也可以实施监听,类似网络接口一样.在进行US ...

  9. 1-STM32物联网开发WIFI(ESP8266)+GPRS(Air202)系统方案安全篇(来看一下怎么样监听网络数据,监听电脑上位机软件的数据)

    首先安装网络监听软件 运行这个软件 这个软件安装到电脑上,默认是监听咱电脑上的网络通信 咱们先监听电脑的软件的网络通信数据,然后再说怎么监听Wi-Fi和APP的软件的网络通信数据 咱就监听咱基础篇的 ...

随机推荐

  1. SQLAlchemy(二):SQLAlchemy对数据的增删改查操作、属性常用数据类型详解

    SQLAlchemy02 /SQLAlchemy对数据的增删改查操作.属性常用数据类型详解 目录 SQLAlchemy02 /SQLAlchemy对数据的增删改查操作.属性常用数据类型详解 1.用se ...

  2. Python之爬虫从入门到放弃(十三) Scrapy框架整体的了解

    这里是通过爬取伯乐在线的全部文章为例子,让自己先对scrapy进行一个整理的理解 该例子中的详细代码会放到我的github地址:https://github.com/pythonsite/spider ...

  3. 什么是控制反转(IoC)?什么是依赖注入(DI)?以及实现原理

    ​ IoC不是一种技术,只是一种思想,一个重要的面向对象编程的法则,它能指导我们如何设计出松耦合.更优良的程序.传统应用程序都是由我们在类内部主动创建依赖对象,从而导致类与类之间高耦合,难于测试:有了 ...

  4. ant design pro : 依赖项 webpack-theme-color-replacer 最新版导致项目无法启动?

    重新装了一个项目的依赖,结果发现打不开了? 报错如下: This dependency was not found: * webpack-theme-color-replacer/client in ...

  5. 利用tox打造自动自动化测试框架

    什么是tox tox官方文档的第一句话 standardize testing in Python,意思就是说标准化python中的测试,那是不是很适合测试人员来使用呢,我们来看看他究竟是什么? 根据 ...

  6. Vuex里的module选项和移动端布局

    Vuex里的modules 在store文件夹里创建一个modules的文件夹,里面随意创建一个.js文件,然后export输出

  7. Ubuntu Server 19.04配置静态IP

    这个/etc/netplan下默认有个文件50-cloud-init.yaml,直接修改它就行了 sudo vim /etc/netplan/50-cloud-init.yaml 网口名字enp0s3 ...

  8. LaTeX公式学习

    简介 本文公式较多可能有加载较慢. 使用 LaTeX 的主要原因之一是它可以方便地排版公式.我们使用数学模式来排版公式. 公式 插入公式 可以用一对$来启用数学模式. 行中公式可以用如下方法: $数学 ...

  9. zabbix修改默认密码

    1.mysql -u root -p 2.desc users; 3.select userid,alias,passwd from users; 4.update users set passwd= ...

  10. 想学Python不知道从哪里开始学?|百度网盘免费下载| 这本入门书了解下

    百度网盘免费下载:编程小白的第一本 Python 入门书 提取码:s0pc Python是什么 Python是一种计算机程序设计语言,由吉多·范罗苏姆创造,第一版发布于1991年,可以视之为一种改良的 ...