vue2.0数据响应式原理
概念
vue中,当data中的数据一旦发生变化,会自动同步试图进行更新
前置
想实现数据响应式,那么我们需要先了解关于对象的属性的特性
getOwnPropertyDescriptor
该方法返回一个对象,该对象描述给定对象上特定属性的配置(即直接存在于对象上而不在对象的原型链中的属性)。返回的对象是可变的
const data = {
name: '张三',
age: 14
}
console.log(Object.getOwnPropertyDescriptor(data, 'name'))
- configurable: 是否能够通过delete删除属性
- enumerable: 是否可枚举,也就是能否通过for-in循环得到属性
- value: 属性值
- writable: 是否可修改
- getter: 在读取属性时,调用的函数
- setter: 在写入属性时调用的函数
defineProperty
Object.defineProperty() 方法会直接在一个对象上定义一个新属性,或者修改一个对象的现有属性,并返回此对象。
属性配置
在未针对对象特性设置之前,属性是可删除、枚举、修改
const data = {
name: '张三',
age: 14
}
console.log(Object.getOwnPropertyDescriptor(data, 'name'))
data.name = '赵四'
for(key in data) {
console.log(key)
}
delete data.name
通过Object.defineProperty配置之后
const data = {
name: '张三',
age: 14
}
// 配置对象
Object.defineProperty(data, 'name', {
configurable: false,
enumerable: false,
writable: false
})
console.log(Object.getOwnPropertyDescriptor(data, 'name'))
data.name = '赵四'
for(key in data) {
console.log(key)
}
delete data.name
数据读写
读数据,会触发get,并且get内部需要返回数据,才能读取到数据
但是由于Object.defineProperty的缺陷,直接在return 后面 返回data.name,会进入死循环,所以需要通过一个全局变量进行管理
const data = {
name: '张三',
age: 14
}
let value = data.name
Object.defineProperty(data, 'name', {
get () {
console.log('读数据了')
return value
},
set (val) {
console.log('写数据了')
}
})
console.log(data.name)
写数据
const data = {
name: '张三',
age: 14
}
let value = data.name
Object.defineProperty(data, 'name', {
get () {
console.log('读数据了')
return value
},
set (val) {
console.log('写数据了' + val)
value = val
}
})
console.log(data.name)
data.name = '赵四'
console.log(data.name)
读数据了
张三
写数据了赵四
读数据了
赵四
实现简易的数据响应式
Object.defineProperty不是为了实现数据响应式出现的,而是vue利用了Object.defineProperty的特性从而实现了数据响应式
单层数据响应
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div>
<p id="name"></p>
<p id="age"></p>
</div>
<script>
// 定义数据
const data = {
name: '张三',
age: 19
}
// 同步视图的方法
function setView() {
document.getElementById('name').innerHTML = data.name
document.getElementById('age').innerHTML = data.age
}
setView()
// 利用循环对数据进行监听
Object.keys(data).forEach(key => {
// 利用外部数据进行数据的读写控制
let getValue = data[key]
Object.defineProperty(data, key, {
// 读数据
get () {
return getValue
},
// 写数据
set (val) {
getValue = val
setView()
}
})
})
</script>
</body>
</html>
多层数据响应
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div>
<p id="name"></p>
<p id="age"></p>
<p id="idCard"></p>
<p id="address"></p>
</div>
<script>
// 数据
const data = {
name: '张三',
age: 19,
info: {
idCard: 233455667889001223,
address: '下沙'
}
}
// 同步视图的方法
function setView() {
document.getElementById('name').innerHTML = data.name
document.getElementById('age').innerHTML = data.age
document.getElementById('idCard').innerHTML = data.info.idCard
document.getElementById('address').innerHTML = data.info.address
}
setView()
observer(data)
// 递归的方法
function observer(data) {
Object.keys(data).forEach(key => {
if (typeof data[key] === 'object') {
return observer(data[key])
}
let getValue = data[key]
Object.defineProperty(data, key, {
get () {
return getValue
},
set (val) {
getValue = val
setView()
}
})
})
}
</script>
</body>
</html>
数据无法响应的情况
对象
对象新增的数据无法响应
数组
通过索引修改数组
\