对vue中的数据劫持/数据代理/数据观察的理解

本文深入探讨了Vue数据劫持的概念,解释了如何通过Object.defineProperty()实现数据观察,指出其在数组和对象新增、删除属性时的局限性。接着介绍了Proxy在Vue3.0中替代Object.defineProperty()的优势,以及Vue无法检测到对象属性动态添加或删除的问题。此外,还提供了一道关于严格相等性的面试题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1、什么是数据劫持?

首先写一个简单vue 例子:

<!DOCTYPE html>
<html lang="en">
<head>
	<meta charset="UTF-8">
	<title>数据劫持</title>
</head>
<body>
	<div id="app">{{message}}</div>
	<script src="vue.js"></script>
	<script>
		var vm = new Vue({
			el:"#app",
			data(){
				return {
					message:"测试数据"
				}
			}
		})
	</script>
</body>
</html>

将数据挂载到视图上很简单,使用{{}}模板即可,那么如果要进行二次渲染、再次渲染,该怎么做呢?

我们可以使用延迟函数 ,若要在1s之后修改数据,添加如下函数:

setTimeout(function(){
	vm.message="123456789";
	// vm["message"]="123456789";  //两种方式都可以修改,效果一样
},1000)

运行发现页面显示内容发生改变,说明改变数据的时候触发视图的变化

再次渲染的时候怎么知道数据的变化呢 ?-----数据劫持/数据观察

在vue2.0中使用Object.defineProperty()来实现vue数据劫持这一行为。

数据劫持:指的是在访问或者修改对象的某个属性时,通过一段代码拦截这个行为,进行额外的操作或者修改返回结果。

2、如何劫持:Object.defineProperty()在数组 对象中的使用

首先创建一个对象,并将name属性数据劫持:

let vm = {
	name:"张三"
}
let vm = Object.defineProperty(vm,"name",{
	get(){
		console.log("get...")
		return "张三"
	},
	set(newValue){
		console.log("set...")
		console.log("新值",newValue)
	}
})

但是以上对象只有一个属性,若有多个属性或者嵌套多层时,就需要逐层遍历对象。

let obj = {
    name:"王五",
    age:22
}
Object.keys(vm).forEach(key=>{
	let value = vm[key]
	Object.defineProperty(vm,key,{
		get(){
			console.log("get....")
			return value
		},
		set(newV){
			console.log("set....")
			if(newV!==value){
				value= newV
			}
		}
	})
})

若执行 console.log(vm.name)输出name值,则控制台输出结果为:

get...
王五

若修改name值,vm.name=“李四”,则控制台输出结果为:

set...

但是,若给对象新增属性 vm.sex=“23432”,删除属性delete vm.name都监测不到变化。

Object.defineProperty() 可以监测到属性的获取、修改,但是新增、删除监测不到

那么数组的情况是什么样子的呢?

let array = [1,2,3]
Object.keys(array).forEach(key=>{
	let value = vm[key]
	Object.defineProperty(vm,key,{
		get(){
			console.log("get....")
			return value
		},
		set(newV){
			console.log("set....")
			if(newV!==value){
				value= newV
			}
		}
	})
})
array.push(4) // 监测不到
array[2] = 9 // 这个能正常输出 set...

修改其中某个索引的值,都会输出set…
但是若给数组加一个值,array.push(4) ,监测不到数组变化,还有pop等方法均监测不到数组变化。

若执行的方法修改了原数组, Object.defineProperty() 监测不到数组的变化,但是若该方法不修改原数组,返回一个新数组的时候, Object.defineProperty()就可以检测到数组的变化。

从上可以看出,使用Object.defineProperty() 对对象、数组的监测实现的不是很好,那么,就需要使用Proxy实现。

3、Proxy

在 Vue3.0 中将会通过 Proxy 来替换原本的 Object.defineProperty() 来实现数据响应式。Proxy 是 ES6 中新增的功能,它可以用来自定义对象中的操作。可以说Proxy 是defineProperty的升级版。

Proxy 的使用:
var newVm = new  Proxy(vm,{
	get(target,key){
		console.log("get.....");
		console.log(target);
		return target[key]
	},
	set(target,key,newV){
		console.log("set.....");
		if(target[key]!==newV){
			target[key]=newV
		}
	}
})

proxy不需要对数组、对象进行比遍历,性能上比较好,而且可以完美的监听到任何方式的数据改变,唯一的缺陷就是浏览器的兼容性不好。

一道面试题

什么样的 a 可以满足 (a === 1 && a === 2 && a === 3) === true 呢?(注意是 3 个 =,也就是严格相等)???

let a = {
	temp:1,
	valueOf:function(){
		return this.temp++
	}
}		
Object.defineProperty(window, 'temp', {
  get () {
    temp++
    return temp
  }
})
console.log(a === 1 && a === 2 && a === 3) // true
Vue 无法检测到对象属性的添加或删除

由于 Vue 会在初始化实例时对属性执行 getter/setter 转化,所以属性必须在 data 对象上存在才能让 Vue 将它转换为响应式的。例如:

var vm = new Vue({
  data:{
    a:1
  }
})
// `vm.a` 是响应式的
vm.b = 2
// `vm.b` 是非响应式的

对于已经创建的实例,Vue 不允许动态添加根级别的响应式属性。但是,可以使用 Vue.set(object, propertyName, value) 方法向嵌套对象添加响应式属性。例如,对于:

//第一种方式 Vue.set(object, propertyName, value)
Vue.set(vm.someObject, 'b', 2)

//第二种方式 vm.$set 实例方法
this.$set(this.someObject,'b',2)

有时你可能需要为已有对象赋值多个新属性,比如使用 Object.assign() 或 _.extend()。但是,这样添加到对象上的新属性不会触发更新。在这种情况下,你应该用原对象与要混合进去的对象的属性一起创建一个新的对象。

// 代替 `Object.assign(this.someObject, { a: 1, b: 2 })`
this.someObject = Object.assign({}, this.someObject, { a: 1, b: 2 })
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值