深入理解Vue的数据响应式


Posted in Vue.js onMay 15, 2021

1. ES语法的getter和setter

在开始了解 Vue 的数据响应式原理前应该先搞清楚 ES语法 中的 getter 和 setter 方法的具体用法。

getter和setter 方法是以 get 和 set 关键字来为对象添加虚拟属性的一种方式。这种属性其实并不真实存在,而是以取值函数 getter 和存值函数 setter 来模拟的一种属性。目的是对某个属性设置存值函数和取值函数,拦截该属性的存取行为,以便于对该属性的存取做一些限定处理。如下所示(以下代码来源于 mdn)

getter 方法

const obj = {
  log: ['a', 'b', 'c'],
  get latest() { //在正常方法前加 get 关键字
    if (this.log.length == 0) {
      return undefined;
    }
    return this.log[this.log.length - 1];
  }
}

console.log(obj.latest);// 输出 c,获取的是属性名不用带括号

setter方法

const language = {
  set current(name) {
    this.log.push(name);
  },
  log: []
}

language.current = 'EN';
language.current = 'FA';

console.log(language.log);//输出 Array ["EN", "FA"]

2. ES语法的 defineProperty

defineProperty 方法会直接在一个对象上定义一个新属性,或者修改一个对象的现有属性, 并返回这个对象,可用于在一个对象定义好后为其修改或添加属性。
语法为:

Object.defineProperty(obj, prop, descriptor)

添加常规属性:

let data = {
  m: 0
}

Object.defineProperty(data, 'n', {
  value: 1 //添加属性的 value 就是其值
})
console.log(`${data.n}`) //则会输出n值为1

也可用来添加 getter 和 setter 的虚拟属性

let data1 = {
    _n: 0
}

Object.defineProperty(data1, 'n', {
  get(){
    return this._n
  }, 
  set(value){
    if(value < 0) return
    this._n = value
  }//直接写明 get / set 即可
}) //由于指明了虚拟属性为 n,即 get n(){}、set n(value){},因此在函数定义时就不用再写n了

3. Vue对数据的代理和监听

代理,即 proxy,简单来说我自己的一些事情我自己不亲自处理,而是交给一个人让他去帮我做,那个做事的人就是代理。这个逻辑中有两个关键点需要搞清楚,代理是处理操作的人,而其处理操作的事情不属于他,而是属于委托其代理的人的。

因此类比到 Vue数据代理 ,委托代理的是 data{} 数据对象,其找到代理就是 Vue实例vm ,data{} 数据对象要代理 vm 做的事情是管理 data{} 数据对象里数据操作。因此 data{} 数据对象只负责内部数据的生产即可,对生产出来的数据的管理和操作全权交给 vm 处理。

那么 vm 如何对 data{} 数据对象里的数据进行控制和操作呢?换句话说,vm 如何在 data{} 数据对象里面的任意一个属性值变化时都及时知道呢?

于是便用到了 ES 语法中的 getter和setter 方法,通过 getter和setter 方法控制的属性的任何操作都会被这两个函数检测到,而 getter和setter 方法形成的属性是虚拟属性,真实并不存在,因此如果用户想私自不经过代理 vm 直接修改 data{} 数据对象的属性也获取不到对应的实体属性,只能通过 getter和setter 方法修改,那么其修改就必定被 vm 检测到。

因此 vm 为了实现对 data{} 数据对象里数据的全部控制,就必须在 Vue实例 创建的时候对传进来的 data{} 数据对象做一些处理,做的处理就是将 data{} 数据对象里的属性都变成了 getter和setter 方法控制的虚拟属性,并保存在代理数据对象 obj 并返回。

但为了不让用户直接修改原来的 data{} 属性,也将原来的 data{} 对象的实体属性全改变了,添加的虚拟属性名字和实体属性名一样,就会用虚拟属性覆盖原来的实际属性,用户在修改属性值是就是通过 getter和setter 方法修改的虚拟属性。这样一来 data{} 数据对象的全部属性的任何变化都会被 Vue实例vm 检测到。

let myData = {n:0}
let data = proxy({ data:myData }) // 类似于 let vm = new Vue({data: myData})

function proxy({data}/* 解构赋值*/){
  let _n = data.n
  Object.defineProperty(data, 'n', { //覆盖原来的data.n属性
    get(){
      return _n
    },
    set(newValue){
      if(newValue<0)return
      _n = newValue
    }
  })// 改变data{}数据对象本身属性,可通过闭包形成上下文,让原来的实际属性值存在闭包的上下文_n中
  
  const obj = {}
  Object.defineProperty(obj, 'n', {
    get(){
      return data.n
    },
    set(value){
      data.n = value
    }
  }) //添加data{}数据对象的代理,对data{}数据对象操作
  
  return obj // obj 就是data{}的代理
}

4. Vue的数据响应式

所谓响应式就是当事物发生变化时会根据变化做出相应的反映。

Vue 中的数据 data 是响应式的,由上述 Vue 通过 Object.defineProperty()函数 来用 getter和setter方法 对 data 数据做了代理和监听,一旦数据发生变化,Vue 就会改变数据对应的 UI 视图,这就是 Vue的数据响应式

但是 Vue 使用 Object.defineProperty 来设置监听,就只能对在 Vue实例化 时 data 对象里已经存在的属性设置监听,而对不存在的或者后来添加进去的属性没有进行监听。

为了解决这个问题,有两种方法:

1. 将所有属性都提前声明好

2. 使用 Vue.set 和 this.$set 添加属性

使用 Vue.set 和 this.$set 添加属性是会通知 Vue 对这后添加的属性也设置监听操作。

Vue.set('this.data','m','10')
this.$set('this.data','m','10')//为vm的data对象添加属性m值为10

3.数组变异

对于数组的数据增加,无法控制其新增个数因此不能提前声明所有数据值,而一个一个 set 又太麻烦,而且数组是常用的对象数据类型中的一种,因此 vue 的作者就对数组的增删函数如 push 和 pop 等进行了篡改,用户在使用 vue 中数组增删时仍是用 push 和 pop ,但是里面进行了额外的处理,这几个被篡改的 API 会对数组新增是数据代理监听并根据数据响应改变 UI 视图。

以上就是深入理解Vue的数据响应式的详细内容,更多关于Vue的数据响应式的资料请关注三水点靠木其它相关文章!

Vue.js 相关文章推荐
vue实现下载文件流完整前后端代码
Nov 17 Vue.js
vue 插槽简介及使用示例
Nov 19 Vue.js
在vue中动态修改css其中一个属性值操作
Dec 07 Vue.js
vue 通过base64实现图片下载功能
Dec 19 Vue.js
vue3.0中友好使用antdv示例详解
Jan 05 Vue.js
详解实现vue的数据响应式原理
Jan 20 Vue.js
Vue SPA 首屏优化方案
Feb 26 Vue.js
手写Vue2.0 数据劫持的示例
Mar 04 Vue.js
Vue通过懒加载提升页面响应速度
May 10 Vue.js
Vue的生命周期一起来看看
Feb 24 Vue.js
vue route新窗口跳转页面并且携带与接收参数
Apr 10 Vue.js
vue.js 使用原生js实现轮播图
Apr 26 Vue.js
详解Vue的options
May 15 #Vue.js
vue实现无缝轮播效果(跑马灯)
May 14 #Vue.js
Vue接口封装的完整步骤记录
Vue全家桶入门基础教程
vue实现可拖拽的dialog弹框
vue如何批量引入组件、注册和使用详解
vue组件的路由高亮问题解决方法
You might like
实用函数8
2007/11/08 PHP
Smarty Foreach 使用说明
2010/03/23 PHP
jQuery 源码分析笔记
2011/05/25 PHP
PHP用mysql_insert_id()函数获得刚插入数据或当前发布文章的ID
2016/11/25 PHP
iis 7下安装laravel 5.4环境的方法教程
2017/06/14 PHP
用window.location.href实现刷新另个框架页面
2007/03/07 Javascript
javascript实现修改微信分享的标题内容等
2014/12/11 Javascript
js实现DOM走马灯特效的方法
2015/01/21 Javascript
jquery实现表单验证并阻止非法提交
2015/07/09 Javascript
微信小程序中页面FOR循环和嵌套循环
2017/06/21 Javascript
详解ES6 Promise对象then方法链式调用
2018/10/20 Javascript
详解vue 自定义marquee无缝滚动组件
2019/04/09 Javascript
[01:24:51]2014 DOTA2华西杯精英邀请赛 5 25 LGD VS NewBee第二场
2014/05/26 DOTA
编写Python的web框架中的Model的教程
2015/04/29 Python
Python的几个高级语法概念浅析(lambda表达式闭包装饰器)
2016/05/28 Python
详解Python中最难理解的点-装饰器
2017/04/03 Python
用tensorflow实现弹性网络回归算法
2018/01/09 Python
python基础教程项目四之新闻聚合
2018/04/02 Python
Django objects的查询结果转化为json的三种方式的方法
2018/11/07 Python
Python基本数据结构与用法详解【列表、元组、集合、字典】
2019/03/23 Python
Python matplotlib 绘制双Y轴曲线图的示例代码
2020/06/12 Python
如何使用python-opencv批量生成带噪点噪线的数字验证码
2020/12/21 Python
pytorch 计算Parameter和FLOP的操作
2021/03/04 Python
使用 CSS3 中@media 实现网页自适应的示例代码
2020/03/24 HTML / CSS
详解HTML5 LocalStorage 本地存储
2016/12/23 HTML / CSS
英国旅游额外服务市场领导者:Holiday Extras(机场停车场、酒店、接送等)
2017/10/07 全球购物
美国餐厅用品和厨房设备批发网站:KaTom Restaurant Supply
2018/01/27 全球购物
MATCHESFASHION澳大利亚/亚太地区:英国时尚奢侈品电商
2020/01/14 全球购物
会计与审计专业大专生求职信
2013/10/03 职场文书
英语自荐信范文
2013/12/11 职场文书
房产销售经理职责
2013/12/20 职场文书
写字楼租赁意向书
2014/07/30 职场文书
班主任远程培训研修日志
2015/11/13 职场文书
如何用threejs实现实时多边形折射
2021/05/07 Javascript
Mysql 如何实现多张无关联表查询数据并分页
2021/06/05 MySQL
教你如何用cmd快速登录服务器
2022/06/10 Servers