首先我们得了解一些MVVM模型, Vue的作者在看到MVVM模型之后, 受到了启发, 开发出了Vue中所特有的模型.
M: 代表着模型层(Model) 就是data中的数据(一般js对象), V: 代表着视图层(View) 就是DOM元素, VM: 代表着模型层和视图层的链接桥梁 也就是Vue实例.
从这个图中间的两个箭头可以看出来, Vue总共做了两件事:
我们在data中定义的成员都会被挂载到Vue的实例Vm上面, 只要在模板(template)里面进行修改data中的数据, 都会触发视图的改变.
但是这是有一个前提的, 就是data中所有的成员都需要通过Object.defineProperty进行事件属性拦截, 简答的来说就是他通过Object.defineProperty这个属性将说有成员设置成响应式的.
不要着急哈, 现在来一点一点的了解:
很容易的可以看出两个"age"属性颜色是不同的, 使用Object.defineProperty方法添加的是不可以枚举的.
枚举的意思就是不能被遍历的, 举个栗子:
直接添加:
动态添加:
上述来看, Object.defineProperty条条框框的很多, 很麻烦的样子; 其实不然, 它是一种高级的用法, 可以对属性添加控制.
不像手动添加属性那样随意, 我们可以对属性更好的控制.
- <!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
劫持对象的访问器,在属性值发生变化时我们可以获取变化,从而进行进一步操作。
发布者模式 / 订阅者模式
在软件架构中,发布订阅是一种消息范式,消息的发送者(称为发布者)不会将消息直接发送给特定的接收者(称为订阅者)。而是将发布的消息分为不同的类别,无需了解哪些订阅者(如果有的话)可能存在。同样的,订阅者可以表达对一个或多个类别的兴趣,只接收感兴趣的消息,无需了解哪些发布者(如果有的话)存在。
这里很明显了,区别就在于,不同于观察者和被观察者,发布者和订阅者是互相不知道对方的存在的,发布者只需要把消息发送到订阅器里面,订阅者只管接受自己需要订阅的内容