Vue数据响应Object.defineProperty
Posted 空城机
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Vue数据响应Object.defineProperty相关的知识,希望对你有一定的参考价值。
Vue数据响应
在网页开发中,一般数据想要渲染到页面中,需要依赖于操作DOM更新。 Vue
和React
则是使用数据驱动视图,也就是数据改变,DOM也相应完成变化。
而数据变化更新DOM也分为侵入式和非侵入式
侵入式和非侵入式
Vue
属于非侵入式,React
和小程序
数据变化输入侵入式。
侵入式设计,就是设计者将框架功能“推”给客户端;
而非侵入式设计,则是设计者将客户端的功能“拿”到框架中用
侵入式设计带来的最大缺陷是,代码需要依赖框架的代码,如果把框架拿掉或者换一个框架,就需要重新修改代码
下面的例子中,vue
改变a的值没有调用其他的API,而react
和小程序
则调用了setState
和setData
的API
Vue:
this.a++;
React:
this.setState(
a: this.state.a + 1
);
小程序:
this.setData(
a: this.state.a + 1
);
Object.defineProperty() 数据代理
当然,本文的重点在于Object.defineProperty。
在上面说到,在React
和小程序当中,因为调用API方法改变数值,所以界面改变也很好理解,API方法对应会改变DOM。
那么Vue
改变数值时是非侵入式,那么界面应该响应数值改变而改变?
在这里就需要提到Object.defineProperty
数据代理了,MDN地址:https://developer.mozilla.org/zh-CN/docs/Web/javascript/Reference/Global_Objects/Object/defineProperty
这是一个JavaScript引擎的API,可以来检测对象属性的变化
在vue3中使用proxy代替
defineProperty
,但是proxy具有兼容性问题,且无法polyfill (基本上IE浏览器可以放弃)参考: https://caniuse.com/?search=proxy
之前在《前端面试中有趣的题目》 中也有用到过该方法去定义一个const
Object.defineProperty
中具有get和set方法,当Vue去改变a值时,a属性已经被set给劫持了。(get和set都需要变量进行周转)
例子: 在下面会看到例子当中obj对象存在一个_a
属性,这是为了对应之后设置a
的get
和set
,现在修改obj.a
的数值,也能够打印console
中的语句。如果在set
方法中添加updateView
更新视图方法,那么就能够简单的达到了数据同步的效果
let obj =
_a: 0
Object.defineProperty(obj, 'a',
get()
return this._a;
,
set(n)
console.log('设置a属性的值' + n);
this._a = n;
// 触发更新视图
//updateView();
)
obj.a = 0; // 设置a属性的值0
obj.a++; // 设置a属性的值1
obj.a++; // 设置a属性的值2
obj.a += 10; // 设置a属性的值12
console.log(obj.a); // 12
封装一个defineProperty方法
先定义一个等一下要测试的数据
let personInfo =
name: 'zhangsan',
age: 20,
info:
address: '江苏'
在上面的基础上,可以对其进行封装
function defineProperty(object, key, data)
if (!data)
data = object[key]
Object.defineProperty(object, key,
get()
return data;
,
set(val)
if (data != val)
console.log(`$key的原值$data被更新为:$val`)
data = val;
)
对其中的name
属性调用方法进行代理,能够在控制台看到name的原值zhangsan被更新为:lisi
被打印
defineProperty(personInfo, 'name');
personInfo.name = 'lisi' // name的原值zhangsan被更新为:lisi
并且在此封装好的基础上,可以再写一个方法,对对象进行分析,遍历对象各个属性放入方法代理
function observe(obj)
if (typeof obj != 'object')
return ;
for(let key in obj)
observeReactive(obj, key);
这样一来层次不深的属性
基本已经被defineProperty代理了
observe(personInfo);
personInfo.age = 30; // age的原值20被更新为:30
不过如果是内部还有层次的就不行了,比如下面的例子,就没有打印出来
personInfo.info.address = '浙江';
那么就对属性再做一次判断,如果还是对象,继续遍历
function defineProperty(object, key, data)
if (!data)
data = object[key]
// 递归
if (typeof data == 'object')
observe(data);
Object.defineProperty(object, key,
get()
return data;
,
set(val)
if (data != val)
console.log(`$key的原值$data被更新为:$val`)
data = val;
// 新增加对象也要observe
if (typeof val == 'object')
observe(data);
)
这样一来address的原值江苏被更新为:浙江
就能够被打印出来了
Object.defineProperty缺点
Object.defineProperty
还是有挺多不足的
- 比如上面说到的深度监听,需要递归到底,一次性的计算量很大。
- 无法监听新增属性/删除属性 (所以在vue中会有
Vue.set
和Vue.delete
) - 数据劫持并不能对数组的
push
、shift
等方法生效生效,无法原生监听数组,需要特殊处理
以上是关于Vue数据响应Object.defineProperty的主要内容,如果未能解决你的问题,请参考以下文章