响应式原理步骤:
1、封装对象响应式defineReactive函数(步骤:① 递归 ② Object.defineProperty())
2、数组的响应式(数组方法重写)
① 将数组原型保存出来Array.prototype
② 复制一份数组的原型,用来进行数组方法的重写,防止污染原有的数组原型Object.create()
③ 枚举所有数组方法,将数组方法进行重写
3、封装监听observer函数,监听某些数据的变化,判断监听的是数组还是对象,步骤:
对象:
①枚举对象,调用defineReactive
数组:
①将数组的__proto__属性指向重写后的原型
4、渲染函数到页面render()
```javascript
const data = {
name: "wuying",
age: 18,
looks: {
look: "beauty"
},
arr: [1, 2, 3]
}
/*
1、
data:监听的对象
key:监听的某个属性名
value:该属性名对应的属性值
*/
function defineReactive(data, key, value) {
observer(value)//递归从而可以监听子对象子子对象等
Object.defineProperty(data, key, {
get() {
return value
},
set(newValue) {
value = newValue
render()
}
})
}
/*
2、
① 将数组原型保存出来
② 复制一份数组的原型,用来进行数组方法的重写,防止污染原有的数组原型
③ 枚举所有数组方法,将数组方法进行重写
*/
const saveObjectPro = Array.prototype
const copyPrototype = Object.create(saveObjectPro)
['pop','push','shift','unshift','sort','splice','resverse'].forEach(method => {
copyPrototype[method] = function () {
saveObjectPro[method].call(this,...arguments)
render()
}
});
/*
3、
data:监听的对象
*/
function observer(data) {
/*
1、判断data是对象或者数组
对象:①枚举对象,调用defineReactive
数组:将数组的__proto__属性指向重写后的原型
*/
if (Array.isArray(data)) {
data.__proto__ = copyPrototype
return;
}
if (typeof data === 'object') {
for (const key in data) {
defineReactive(data, key, data[key])
}
}
}
/*
4、渲染函数
*/
function render() {
console.log("这里会经过渲染操作,即完成页面的渲染")
}
observer(data)
## 扩展:`$set`和`$delete`如何实现
```javascript
function _$set(data,key,value) {
if (Array.isArray(data)) {
data.splice(key,1,value)
return value
}
defineReactive(data,key,value)
render()
return value
}
function _$delete(data,key) {
if (Array.isArray(data)) {
data.splice(key,1)
return ;
}
delete data[key]
render()
}
以上是vue2.0响应数据原理,vue3.0使用proxy实现,后序会继续更新…
总结:使用Object.defineProperty实现响应式的劣势
1、无法监听数组不存在的索引的变化
2、无法监听数组长度的变化
3、无法监听对象的增删
4、天生就需要进行递归