首先我们得了解一些MVVM模型, Vue的作者在看到MVVM模型之后, 受到了启发, 开发出了Vue中所特有的模型.
M: 代表着模型层(Model) 就是data中的数据(一般js对象), V: 代表着视图层(View) 就是DOM元素, VM: 代表着模型层和视图层的链接桥梁 也就是Vue实例.
从这个图中间的两个箭头可以看出来, Vue总共做了两件事:
- 将data中的数据(一般js对象, 因为data中renturn出来了一个对象), 建设到了DOM元素上.
- 监听DOM的改变动态的修改data中的数据.
我们在data中定义的成员都会被挂载到Vue的实例Vm上面, 只要在模板(template)里面进行修改data中的数据, 都会触发视图的改变.
但是这是有一个前提的, 就是data中所有的成员都需要通过Object.defineProperty进行事件属性拦截, 简答的来说就是他通过Object.defineProperty这个属性将说有成员设置成响应式的.
现在就来介绍一下Object.definProperty:
- 第一个参数: 需要动态添加成员的对象(target)
- 第二个参数: 动态添加到目标对象(target)上的属性(property)
- 第三个参数: 配置项(description), 其中有enumerable、configurable、writable、value、get和set定义的对象.
不要着急哈, 现在来一点一点的了解:
- 先来对比一下动态添加的属性, 和直接将属性写入对象中有什么区别
很容易的可以看出两个"age"属性颜色是不同的, 使用Object.defineProperty方法添加的是不可以枚举的.
枚举的意思就是不能被遍历的, 举个栗子:
直接添加:
动态添加:
- 再来说一下enumerable、configurable、writable、value这几个成员是如何使用的:
- value是动态添加那个属性的属性值
- enumerable将它的属性这设置为true的话, 那么动态添加的属性就允许枚举, 默认为false
- configurable将它的属性这设置为true的话, 那么动态添加的属性就允许删除, 默认为fasle
- writable将它的属性这设置为true的话, 那么动态添加的属性就允许修改, 默认为false
上述来看, Object.defineProperty条条框框的很多, 很麻烦的样子; 其实不然, 它是一种高级的用法, 可以对属性添加控制.
不像手动添加属性那样随意, 我们可以对属性更好的控制.
- 现在就要开始着重的介绍set和get属性了, 数据代理和computed计算属性都是使用的这个底层原理来实现的, 上代码:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div>
<!-- 发布者 -->
<input type="text" class="Publisher">
<!-- 订阅者 -->
<div class="Subscriber">我是订阅者</div>
</div>
<script>
// 目标对象
let Target = {}
let ipt = document.querySelector('.Publisher')
let div = document.querySelector('.Subscriber')
Object.defineProperty(Target, 'msg', {
get() {
return console.log('有人访问了Target.msg的值')
},
set(value) {
console.log('有人修改了Target.msg的值', value)
div.innerHTML = value
}
})
// 观察者
ipt.addEventListener('input', (e) => {
Target.msg = e.target.value
})
</script>
</body>
</html>
原理:
Vue 数据双向绑定原理是通过 数据劫持
+ 发布者-订阅者模式
的方式来实现的,首先是通过 ES5
提供的 Object.defineProperty()
方法来劫持(监听)各属性的 getter、setter,并在当监听的属性发生变动时通知订阅者,是否需要更新,若更新就会执行对应的更新函数。
什么是数据劫持
数据劫持比较好理解,通常我们利用Object.defineProperty
劫持对象的访问器,在属性值发生变化时我们可以获取变化,从而进行进一步操作。
发布者模式 / 订阅者模式
在软件架构中,发布订阅是一种消息范式,消息的发送者(称为发布者)不会将消息直接发送给特定的接收者(称为订阅者)。而是将发布的消息分为不同的类别,无需了解哪些订阅者(如果有的话)可能存在。同样的,订阅者可以表达对一个或多个类别的兴趣,只接收感兴趣的消息,无需了解哪些发布者(如果有的话)存在。
这里很明显了,区别就在于,不同于观察者和被观察者,发布者和订阅者是互相不知道对方的存在的,发布者只需要把消息发送到订阅器里面,订阅者只管接受自己需要订阅的内容