一旦数据发生变化,我们可以立刻知道,并且做一些你想完成的事情,这些事情包括但不限于以下:
发送一个网络请求
打印一段文字
操作一个dom
…
提示:以下是本篇文章正文内容,下面案例可供参考
一、传统写法
用字面量定义对象设置值如下代码:
// 字面量定义对象
let obj = {
name: '晚星'
}
console.log(obj.name);
obj.name = '晚星good'
缺点:不管是访问还是修改 我们开发者无从得知什么时候进行的修改
// 没有插入我们自定义动作的机会
二、拦截式写法
如下代码:
let data = {
}
Object.defineProperty(data, 'name', {
// 再访问data中name的属性的时候会自动调用
get() {
console.log('data中的name属性被访问了');
},
// 在修改data中name属性的时候会自动调用
set() {
console.log('data中的name属性被设置了');
}
})
在拦截器中Object.defineProperty
get 方法主要监视该对象是是否又能调用,如果调用则执行函数体的代码
set方法 主要是对象里面的属性或者方法被调用后,再执行函数体里面的代码
三、拦截器中get与set
get方法如果没有return 返回值,则返回undefined
set方法中如果设置形参,再打印的时候会自动获取设置的形参打印
如下代码:
let data = {
}
Object.defineProperty(data, 'name', {
// 当我们访问data.name的时候 实际上拿到的就是get函数的返回值
get() {
console.log('data中的name属性被访问了');
return '晚星good'
},
// 当我们对data.name进行修改的时候 新值会被自动当成实参传入到newValue的位置
set(newValue) {
console.log('data中的name属性被设置了,新值为:' + newValue);
}
})
1.字面量对象
obj.name设置了一个初始值,当我调用它的时候,它显示它的初始值,当我设置了它的obj.name的值后,当我再次调用obj.name它显示的是我修改之后的值,这就是对象中联动。
代码如下:
let obj = {
name: '晚星'
}
console.log(obj.name);
obj.name = '晚星'
2.拦截器对象
obj.name而在拦截器对象中,当我设置完data.name的值后,再调用data.name的时候,它显示的还是初始值。细心的小伙伴可以先想想这是为什么?
let data = {
}
let _name = '晚星'
Object.defineProperty(data, 'name', {
get() {
console.log('data中的name属性被访问了');
return '晚星'
},
set(newValue) {
console.log('data中的name属性被设置了,新值为:' + newValue);
}
})
问题出现再了get方法中的return,因为它始终返回的都是return里面的值。
针对这一问题解决方案:
// 1.get函数返回的始终是一个固定值
// 2.set函数接收到的新值之后没有进行任何操作
// 解决方案:借助一个两个函数中都可以访问到的中间变量去做一个数据的中转
// 最终目的:get函数return出去的 其实已经变成set函数传下来的新值
代码如下:
let data = {
}
let _name = '晚星'
Object.defineProperty(data, 'name', {
get() {
console.log('data中的name属性被访问了');
return _name
},
set(newValue) {
console.log('data中的name属性被设置了,新值为:' + newValue);
_name = newValue
}
})
data.name首先给大家回忆下Object.keys(data)它是遍历对象的键,如果有一个已经很很多属性的对象,让你们把所有的键和值都打印出来你们会怎么做?
示例代码如下:
let data = {
name: "晚星",
age: 23,
height: 180
}
Object.keys(data).forEach(key => {
console.log(key + ' : ' + data[key]);//打印出所有的键和值
})
data.name但是如果只是获取到键和值的话,还是无法改变data对象中的属性,所以这时候就要结合属性拦截,来获取与修改对象data的值。
我们的需求是:
1.针对于已存在多个属性的对象进行get/set处理
2.每个属性的get和set都是可以进行联动的
代码如下:
Object.keys(data).forEach(key => {
// 针对于每一个属性都进行响应式转换
defineReactive(data, key, data[key])
})
// 专门用来进行响应式转换
function defineReactive(data, key, value) {
Object.defineProperty(data, key, {
get() {
return value
},
set(newValue) {
value = newValue
}
})
}
- 每一次遍历 都会执行defineReactive函数 每次执行都会形成一块独立的函数作用域
- get和set函数使我们defineReactive函数内部的函数 在内部函数中引用外部函数内的变量 可以形成闭包,由于value变量被内部函数引用,defineReactive执行完毕之后,value变量一致不被销毁
- 由于value一直不被销毁 且可以被get函数和set函数基于作用域访问到,所以value一直可以作为get和set联动中间变量
四、数据变化反应到视图
在上面我们讲了怎么改变已经定义的对象里面的属性,但是我们只是再控制台查看的效果,接下来,我们来说下,怎么把数据或者更改的数据显示在视图上,方便我们直观的查看数据的变化过程。
目标:
p元素的内部可以显示name属性的值 并且一旦name属性发生变化 p标签内的内容也会得到更新
如下图:
实现思路
1.要想控制元素内容的显示 离不开dom操作 dom.innerText
2.响应式已经实现好了 只需要在set函数中 重新使用最新的值 交给dom元素使用即可
实现代码如下:
let data = {
name: '晚星'
}
Object.keys(data).forEach(key => {
defineReactive(data, key, data[key])
})
function defineReactive(data, key, value) {
Object.defineProperty(data, key, {
get() {
return value
},
set(newValue) {
value = newValue
console.log('name属性发生变化了 最新的值为:', newValue);
// 使用data的name数据 再一次的渲染视图
const p = document.querySelector('#app p').innerText = data.name
}
})
}
//获取dom元素
const p = document.querySelector('#app p').innerText = data.name
1. 不管是指令也好,插值表达式也好,这些都是将数据反应到视图的标记而已,通过标记我们可以把数据的变化响应式的反应到对应的dom位置上去
2. 找标记,把数据绑定到dom的过程,我们称之为bindin