一、前言
在Vue.js的使用中,不多不少会触及到数据驱动视图的功能,也就是我们常说的“数据双向绑定”,并且在面试中也经常会被问及它的实现原理,今天打算做个由浅入深的总结,回顾知识。
二、MVVM的概念
首先需要理解MVVM到底是什么。要说起这个,还得从最早的MVC开始说起,直接上图两者的区别:
- 首先我们看MVC:
- 然后是MVVM:
可以发现,实际上MVVM是有MVC演变而来,由原来的 Controller(控制处理模块) 变成了ViewModel(第三方观察者),由此我们就知道,所有的页面数据实际上最终都是View和Model之间的变化而来,其中的Model其实通俗来讲就是我们逻辑代码中的data数据。
三、双向绑定的原理
基于以上的说明,我们知道双向绑定的双向,分别指的就是View和data了,如图:
至于是如何实现的?如上图可知,由Data驱动View时,调用的是ES5中的Object.defineProperty
接口,而View驱动Data则是通过绑定input事件
实现,下面我们会谈一谈Object.defineProperty
的用法。
四、双向绑定的实现方法
首先说明,Object.defineProperty
是ES5的用法,在ES6中已经出现了reflect.defineProperty
,两者之间是存在一些区别的,前者直接返回的是新对象,后者返回的是布尔判断,想查看更多的资料请看这里:MDN相关介绍
那么下面我们只会对Object.defineProperty做介绍,因为两者的运作原理差别不大。先上代码:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<style>
</style>
</head>
<body>
<div class="partone"><input type="text" name="" id="input"></div>
<div class="parttwo"><input type="text" id="inputtwo"></div>
<div>
<P id="output"></P>
</div>
<script>
let obj = {} //创建一个对象容器用于将多方数据绑定
Object.defineProperty(obj, 'input', {
get() {
console.log('get it')
return obj
},
set(word) {
console.log('set it') //如果该对象属性触发了输入,则会打印set it
document.getElementById('input').value = word;
document.getElementById('inputtwo').value = word;
document.getElementById('output').innerHTML = word;
}
})
Object.defineProperty(obj,'inputtwo',{
get(){return obj},
set(newWord){
document.getElementById('inputtwo').value = newWord;
document.getElementById('output').innerHTML = newWord;
}
})
//以上是data向View的数据绑定过程,
//下面是View对data的数据绑定过程
document.addEventListener('keyup', e => {
obj.input = e.target.value
obj.imputtwo = e.target.value
})
</script>
</body>
</html>
Object.defineProperty()
方法主要用于定义新属性或修改原有的属性,这个函数接受三个参数,一个参数是obj,表示要定义属性的对象,一个参数是prop,是要定义或者更改的属性名字,另外是descriptor
,描述符,来定义属性的具体描述。如:
Object.defineProperty(obj, prop, descriptor)
其中,descriptor
既可以直接定义属性的value及其特性如:writable
(是否可写)、enumerable
(是否可枚举)等,也可以直接通过get
、set
方法去操作对象的属性。其中,Vue就是用了后者来进行数据双向绑定(data=>view)。
通过将dom的值通过属性方式写入一个对象obj的input属性中,然后input属性
又定义了get
和set
方法,一旦该属性被修改,则触发set方法,那么我们就可以在set方法中修改另一个dom的值,这样的相当于借助obj对象进行了数据的监听。
而反向绑定就简单多了,在input框上绑定keyup或其他事件,将值付给相应的data或关联的dom即可。
五、后记
以上说的只是简单的DOM双向绑定,当然Vue是没有这么简单的,Vue本身的采取了发布-订阅的设计模式,因为全局Dom的遍历非常损耗性能,因此在Vue的数据绑定流程中,增加了Watcher、Observer模块来进行监听和分发Dom变动,如官方文档的图示:
关于这个如何实现,日后再探讨。