这是我参与8月更文挑战的第26天,活动详情查看: 8月更文挑战
前言
MVVM模式最核心的特性就是数据双向绑定,Vue构建了一套响应式系统,可以实现用声明的方式绑定数据,从而在数据发生变化时自动渲染视图
Vue 2.x 响应式系统的实现原理
Vue 2.x是利用 Object.defineProperty() 方法为对象添加get() 和 set() 方法来侦测对象的变化,当获取对象属性值时会调用get() 方法,当修改对象属性值时会调用set()方法,于是可以在get()和set()方法中添加代码,实现数据与视图的双向绑定。代码示例如下:
// 对Object.defineProperty() 方法进行封装
function defineReactive(obj, key, value){
Object.defineProperty(obj, key, {
get() {
return value
},
set(newValue) {
if(newValue !== value) {
updateView(); // 在set()方法中触发更新
value = newValue
}
}
}
// 对一个对象中所有属性的变化进行侦测
function observer(target){
// 如果不是对象数据类型,则直接返回
if(typeof target !== 'object' ) {
return target
}
// 循环遍历对象的所有属性,并将他们转化为getter和setter形式
for(let key in target){
difineReactive(target, key, target[key])
}
}
// 模拟更新视图的方法
function updateView() {
console.log("视图更新")
}
let user = {name: 'jack'}
// 对user对象的所有属性变化进行侦测
observer(user)
user.name = 'tom'
运行上述代码,输出结果:
视图更新
以上代码只是简单的侦测了一个对象的属性变化,并没有考虑到对象属性本身又是一个对象的情形(即对象的嵌套)。假设user对象中有一个属性address,该属性本身也是一个对象。代码如下:
let user = {name: 'jack', address: {city: '北京'}
// 对user对象的所有属性变化进行侦测
observer(user)
user.address.city = '河北'
运行上述代码,将看不到任何输出,说明对address对象的city属性的修改并没有被侦测到。因此,需要修改上述的代码,当对象的属性也是对象类型时,继续为该属性对象的所有属性添加get() 和 sett() 方法。实现起来也很简单,就是在defineReactive() 函数中添加一个observer() 函数的递归调用。代码示例如下:
// 对Object.defineProperty() 方法进行封装
function defineReactive(obj, key, value){
// 通过递归调用解决多层对象嵌套的属性侦测问题
observer(value)
Object.defineProperty(obj, key, {
get() {
return value
},
set(newValue){
if(newValue !== value) {
//observer(newValue)
updateView(); // 在set()方法中触发更新
value = newValue
}
}
})
}
再次运行代码,可以看到 更新视图 的输出