何为数据代理?
通过一个对象代理对另一个对象中的属性的操作(读/写),就是数据代理。
要搞懂Vue数据代理这个概念,那我们就要从Object.defineProperty()入手
Object.defineProperty()是Vue中比较底层的一个方法,在数据劫持,数据代理以及计算属性等地方都或多或少的用到了本函数
一、认识 Object.defineProperty ()
在前端开发领域,Vue.js 以其简洁易用和高效的响应式数据绑定而备受青睐。其中,数据代理机制是 Vue 实现响应式的核心原理之一。要深入理解 Vue 的数据代理,我们首先得了解 JavaScript 中的一个重要方法 ——Object.defineProperty()。
Object.defineProperty()是 JavaScript 中用于定义或修改对象属性的方法。它允许我们精确地控制对象属性的行为,比如属性的可写性、可枚举性以及是否可配置等。
该方法接收三个参数:
obj:要定义属性的对象。
prop:要定义或修改的属性的名称。
descriptor:一个对象,包含了要定义或修改的属性的描述符。
描述符对象可以包含以下属性:
- value:属性的值,默认为undefined。
- writable:布尔值,表示属性是否可写,默认为false。
- enumerable:布尔值,表示属性是否可枚举(即是否能通过for...in循环或Object.keys()等方法被枚举出来),默认为false。
- configurable:布尔值,表示属性是否可配置(即是否能被删除或再次修改其描述符),默认为false。
- get:一个函数,当访问该属性时会被调用,默认为undefined。
- set:一个函数,当设置该属性的值时会被调用,默认为undefined。
1.示例代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Object.defineProperty()</title>
</head>
<body>
<!--
Object.defineProperty()
1. 这个方法是ES5新增的。
2. 这个方法的作用是:给对象新增属性,或者设置对象原有的属性。
3. 怎么用?
Object.defineProperty(给哪个对象新增属性, '新增的这个属性名叫啥', {给新增的属性设置相关的配置项key:value对})
4. 第三个参数是属性相关的配置项,配置项都有哪些?每个配置项的作用是啥?
value 配置项:给属性指定值
writable 配置项:设置该属性的值是否可以被修改。true表示可以修改。false表示不能修改。
getter方法 配置项:不需要我们手动调用的。当读取属性值的时候,getter方法被自动调用。
* getter方法的返回值非常重要,这个返回值就代表这个属性它的值。
setter方法 配置项:不需要我们手动调用的。当修改属性值的时候,setter方法被自动调用。
* setter方法上是有一个参数的,这个参数可以接收传过来的值。
注意:当配置项当中有setter和getter的时候,value和writable配置项都不能存在。
-->
<script>
// 这是一个普通的对象
let phone = {}
// 给上面的phone对象新增一个color属性
Object.defineProperty(phone, 'color', {
value: '太空灰',
})
</script>
</body>
</html>
2.writable
:布尔值,若为 true
,则该属性的值可以被修改,默认是 false
。
3.get、set
get
:一个函数,作为该属性的 getter 函数。当访问该属性时,会调用此函数,默认是undefined
。set
:一个函数,作为该属性的 setter 函数。当为该属性赋值时,会调用此函数,默认是undefined
。
注意:当配置项当中有setter和getter的时候,value和writable配置项都不能存在。
否则报错
在 Vue 的数据代理机制中,getter
和 setter
是自动调用的
在get函数多加return '你好世界'
测试set函数,对set的参数做出修改
可以看出setter方法上是有一个参数的,这个参数可以接收传过来的值。
让这两个函数串起来,让其通过set方法获取值,get方法输出值
下面是一个会导致递归的做法
set方法也一样
解决方法:增加临时变量temp
4.ES6新特性:在对象中的函数/方法 :function 是可以省略的。
二、数据代理机制原理
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>数据代理机制</title>
<script src="../js/vue.js"></script>
</head>
<body>
<!--
1. 什么是数据代理机制?
通过访问 代理对象的属性 来间接访问 目标对象的属性。
数据代理机制的实现需要依靠:Object.defineProperty()方法。
2. ES6新特性:
在对象中的函数/方法 :function 是可以省略的。
-->
<script>
// 目标对象
let target = {
name: 'zhangsan'
}
// 代理对象
let proxy = {}
// 如果要实现数据代理机制的话,就需要给proxy新增一个name属性。
// 注意:代理对象新增的这个属性的名字 和 目标对象的属性名要一致。
Object.defineProperty(proxy, 'name', {
// get : function(){
// // 间接访问目标对象的属性
// return target.name
// },
// set : function(val){
// target.name = val
// }
get() {
console.log('getter方法执行了@@@@');
return target.name
},
set(val) {
target.name = val
}
})
</script>
</body>
</html>
1.给proxy新增一个name属性。代理对象新增的这个属性的名字 和 目标对象的属性名要一致。
原因:希望访问代理对象的属性的时候,就像访问目标对象的属性一样
2.访问代理对象的属性的时候,返回目标对象的属性(即间接访问目标对象的属性)的代码实现
3.如果给代理对象的属性赋值,希望把目标对象的属性改了的代码实现
4.类比Vue
三、MVVM分层思想
1. MVVM是什么?
M:Model(模型/数据)
V:View(视图)
VM:ViewModel(视图模型):VM是MVVM中的核心部分。(它起到一个核心的非常重要的作用。)
MVVM是目前前端开发领域当中非常流行的开发思想。(一种架构模式。)
目前前端的大部分主流框架都实现了这个MVVM思想,例如Vue,React等。
2. Vue框架遵循MVVM吗?
虽然没有完全遵循 MVVM 模型,但是 Vue 的设计也受到了它的启发。
Vue框架基本上也是符合MVVM思想的。
3. MVVM模型当中倡导了Model和View进行了分离,为什么要分离?
假如Model和View不分离,使用最原始的原生的javascript代码写项目:如果数据发生任意的改动,接下来我们需要编写大篇幅的操作DOM元素的JS代码。
将Model和View分离之后,出现了一个VM核心,这个VM把所有的脏活累活给做了,也就是说,当Model发生改变之后,VM自动去更新View。当View发生改动之后,VM自动去更新Model。我们再也不需要编写操作DOM的JS代码了。开发效率提高了很多。
vue框架就起到了一个VM的作用,监听页面视图的变化,将数据反馈到页面视图上,视图和数据双向绑定。
四、Vue 的数据代理机制
Vue 的数据代理机制基于Object.defineProperty()实现了数据的响应式。当我们创建一个 Vue 实例时,会传入一个data对象,Vue 会遍历这个data对象的所有属性,并使用Object.defineProperty()将它们转换为响应式数据。
1.Vue 实例与 data 对象的关系
在 Vue 中,我们通过new Vue()创建一个 Vue 实例,并传入一个data选项:
let vm = new Vue({
data: {
message: 'Hello, Vue!'
}
});
Vue 会将data对象中的属性代理到 Vue 实例上,使得我们可以通过vm.message来访问和修改data.message的值。这里的代理过程其实就是利用Object.defineProperty()在 Vue 实例上定义了与data对象属性同名的访问器属性。
2.数据代理的具体实现
假设我们有如下简单的 Vue 实例:
<div id="app">
<p>{{ message }}</p>
</div>
<script>
let app = new Vue({
el: '#app',
data: {
message: '初始消息'
}
});
</script>
当 Vue 初始化时,它会遍历data对象的属性,对于message属性,其内部大致实现如下(简化示意,实际 Vue 源码更复杂):
let data = {
message: '初始消息'
};
let vm = {};
Object.defineProperty(vm,'message', {
get: function() {
return data.message;
},
set: function(newValue) {
data.message = newValue;
// 这里触发视图更新相关操作,Vue会通知依赖该数据的视图进行更新
console.log('数据已更新,通知视图更新');
}
});
这样,当我们通过vm.message读取数据时,实际上是读取了data.message的值;当我们通过vm.message修改数据时,不仅data.message的值会改变,Vue 还会触发视图更新操作,从而实现数据与视图的自动同步。
3.数据代理的优势
简化数据访问:开发者可以直接通过 Vue 实例访问和修改数据,无需像传统 JavaScript 那样通过复杂的对象层级去访问。例如vm.message比this.$data.message(在 Vue 中也可访问,但相对繁琐)更简洁直观。
实现响应式更新:通过Object.defineProperty()的getter和setter,Vue 能够监听数据的变化,一旦数据发生改变,立即自动更新与之关联的视图,大大提高了开发效率,减少了手动操作 DOM 更新视图的代码量。
五、总结
Vue 的数据代理机制巧妙地利用了 JavaScript 的Object.defineProperty()方法,将data对象的属性代理到 Vue 实例上,实现了简洁高效的数据访问和强大的响应式更新功能。深入理解这一机制,有助于我们更好地编写 Vue 应用,优化代码结构,提升开发体验。无论是构建小型的交互组件,还是大型的单页应用,Vue 的数据代理机制都为我们提供了坚实的基础,让我们能够专注于业务逻辑的实现,而无需过多关注数据与视图同步的底层细节。
希望通过本文的介绍,你对 Vue 的数据代理机制有了更清晰的认识,在今后的 Vue 开发中能够更加得心应手地运用这一特性。