我们通常可以对进行输入框进行数据的监听,只需要用到了input 事件或 change事件,就可以实时监听到数据的改变,但是如果只是一个单独的数据呢?怎么去做监听,watch吗??哈哈。
所以 vue响应式就用到了object.defineProperty 中的 get 和 set 方法 ,如果对方法不了解可访问蓝色文字。简单来说 只要一访问就会触发get,设置就会触发set。
基本语法:
Object.defineProperty(对象,键,{
get ( ) { },
set (val) { }
})
let data = {
name:'奥特曼'
}
Object.defineProperty(data,'name',{
get(){
console.log('访问了')
},
set(val){
console.log('设置了,值是:',val)
}
})
1. 单个数据实现响应
实现效果:
为了和vue保持格式一致 我们先定义初始变量data ,并且一开始把值赋给类名为name的内容数据
<div class="name"></div>
<script>
let data = {
name:'奥特曼'
}
function setName() {
document.querySelector('.name').innerHTML = username
}
setName()
</script>
接下来要对data.name进行实时的监听,就要用到了obj.defineProperty
let username = "奥特曼";
Object.defineProperty(data, "name", {
get() {
console.log("访问了");
return username;
},
set(val) {
console.log("设置了,值是:", val);
username = val;
setName();
},
});
这里注意 特意用了一个中间变量 username,如果不这么用你可能会想到,错误写法别抄哦
get(){
console.log('访问了');
return data.name
},
为什么不直接把data.username给返回出去,为什么要返回一个值一样的username?原因就是 我们返回着data.name 其实也是在访问他,然后呢每次访问每次进入get函数 就会进入一个死循环。
2.多个数据实现响应式
这里就不过多解释了 只需要把对象循环一下 利用Object.defineProperty监听每个属性,我相信你可以看懂的
<script>
let data = {
username:'奥特曼',
age:18,
}
function setData() {
username.innerHTML = data.username
age.innerHTML = data.age
}
function observe(data) {
for (const key in data) {
let value = data[key]
Object.defineProperty(data,key,{
get(){
return value
},
set(val){
value = val
setData()
}
})
}
}
observe(data)
setData()
</script>
3. 监听复杂数据类型响应式
我们只需要判断当前的参数是否是对象 如果是对象利用一下递归。
<script>
let data = {
username:'奥特曼',
age:18,
student:{
name :'怪兽'
}
}
function setData() {
username.innerHTML = data.username
age.innerHTML = data.age
monster.innerHTML = data.student.name
}
function observe(data) {
for (const key in data) {
if(typeof data[key] == 'object') {
observe(data[key])
}
let value = data[key]
Object.defineProperty(data,key,{
get(){
return value
},
set(val){
value = val
setData()
}
})
}
}
observe(data)
setData()
</script>
4.新增对象属性和数组下标修改
在vue中,我们是检测不到一个对象新增属性和直接修改数组某一项的变化,但是上面的代码中是能够对数组下标进行修改的,那为什么vue不可以呢?
尤大大也说考虑到了性能和用户体验的问题 如果一个数组10000条数据 监听这每一项的数据变化 确实也有很大的性能问题,但vue也提供了解决方案 $set
贴一下 修改数组下标 及 修改不了新增的对象属性代码
<script>
let data = {
username:'奥特曼',
age:18,
student:{
name :'怪兽'
},
arr:[0,1,2,3]
}
function setData() {
username.innerHTML = data.username
age.innerHTML = data.age
monster.innerHTML = data.student.name
// 无法对新增的属性进行监听 原因就是observe 一开始就对原数据进行数据的监听 这也是object.defineproperty的本身缺陷
monsterAge.innerHTML = data.student.age
number.innerHTML = data.arr[0]
}
function observe(data) {
for (const key in data) {
if(typeof data[key] == 'object') {
observe(data[key])
}
let value = data[key]
Object.defineProperty(data,key,{
get(){
return value
},
set(val){
value = val
setData()
}
})
}
}
observe(data)
setData()
</script>
补充:但是有一点哈,有时候呢 你是可以修改数组某一项实现响应式的,什么时候呢看下面的例子
<div>
<div> {{arr[0]}}</div>
{{ obj.a }}
<button @click="editNumber">要修改了</button>
</div>
<script>
export default {
data () {
return {
arr: [0, 1, 2, 3, 4, 5],
obj: { a: 1 }
}
},
methods: {
editNumber () {
this.arr[0] = 999
// 如果不加下面这一行代码,视图就不会更新
// 更新的原因:下面的数据是响应式的数据他会去更新视图,一旦更新视图 数据是对整个组件进行更新的,所以arr[0] 会更新
// 其实底层的object.defineProperty 可以对数组进行更新 但是尤大考虑到数组 的性能与用户体验不成正比 去掉了对数组的响应式 (获取涉及几万条数组非常的大)
this.obj.a = 2
}
}
}
</script>