Vue 双向绑定的原理

40 阅读4分钟

传统的 dom 操作

数据变化时更新视图:先获取到目标节点,然后将改变后的值放入节点中 视图变化时修改数据:需要绑定事件修改数据

简述MVVM

mvvm 是model viewmodel view 的缩写,model代表的是数据模型,也可以在model中定义数据的修改和操作的业务逻辑,view就是代表ui组件,将model转换成ui样式展现出来,viewmodel监听数据模型的改变、控制视图行为、处理用户交互,简单理解就是一个同步view 和 model 的对象,通过双向数据绑定连接model和view,

什么是MVVM

首先是 MVC ,MVC 是最常见的软件架构之一,在这个架构中,它将软件分为了3个部分:View、Controller、Model,各部分的通信方式如下: View 将用户指定发送到 Controller,Controller 完成业务逻辑后,让 Model 改变状态,Model 改变状态后将新数据传给 View,整个的过程都是单向通信。 随着业务量的扩大,Controller 需要处理各种逻辑,维护成本越来越高,也就衍生出了一个新的软件架构,即 MVVM。

MVVM 就是把 MVC 中的 Controller 的数据和逻辑处理部分从中抽离出来,用一个专门的对象去管理,即 ViewModel,是 Model 和 Controller 之间的一座桥梁。 MVVM 架构也是将软件分为了3个部分:View、ViewModel、Model,各部分通信方式如下: View 与 ViewModel 双向绑定,View 的变动自动绑定在 ViewModel 中,然后 ViewModel 与 Model 进行双向通信,Model 就不与 View 直接进行通信。

vue数据双向绑定是通过 数据劫持 结合 发布者-订阅者模式 的方式来实现的, 数据劫持是利用 ES5 的 Object.defineProperty(obj,key,val) 方法来劫持每个属性的 getter 和 setter,在数据变动时发布消息给订阅者,从而触发相应的监听回调来更新视图。

当把一个普通js对象传给vue示例来作为它的data选项时,vue将遍历它的属性用Object.defineProperty方法,property将它们转为getter或者setter,用户看不到这两个,但是在内部,它们让vue追踪依赖在属性被访问和修改时,通知变化;

vue的数据双向绑定将mvvm作为数据绑定的入口,整合observer、compile和watcher三者,

通过observer来监听自己的model的数据变化,

通过compile 来解析编译模版指令

最终利用watcher搭起observer 和compile之间的通信桥梁,

达到数据与视图双向绑定的效果

Vue 主要通过以下 4 个步骤来实现数据双向绑定的:

第一步:实现一个监听器 Observer :对数据对象进行遍历,包括子属性对象的属性,利用 Object.defineProperty() 对属性都加上 setter 和 getter。这样的话,给这个对象的某个值赋值,就会触发 setter,那么就能监听到了数据变化。

第二步:实现一个解析器 Compile :解析 Vue 模板指令,将模板中的变量都替换成数据,然后初始化渲染页面视图,并将每个指令对应的节点绑定更新函数,添加监听数据的订阅者,一旦数据有变动,收到通知,调用更新函数进行数据更新。

第三步:实现一个订阅者 Watcher :Watcher 订阅者是 Observer 和 Compile 之间通信的桥梁 ,主要的任务是订阅 Observer 中的属性值变化的消息,当收到属性值变化的消息时,触发解析器 Compile 中对应的更新函数。

第四步:实现一个订阅器 Dep :订阅器采用 发布-订阅 设计模式,用来收集订阅者 Watcher,对监听器 Observer 和 订阅者 Watcher 进行统一管理。

v-model 的实现以及它的实现原理

在表单输入元素或组件上创建双向绑定。

vue 中实现双向绑定是一个指令 v-model,可以绑定一个动态值到视图,同时视图中变化能改变该值,使用 v-model 可以减少大量繁琐的事件处理代码,提高开发效率,代码可读性也更好;

vue 项目中主要使用 v-model 指令在表单 input、textarea、select 等元素上创建双向数据绑定,

v-model 本质上是语法糖,v-model 在内部为不同的输入元素使用不同的属性并抛出不同的事件,以 input 表单元素为例:

<input v-model='something'>

相当于

<input v-bind:value="something" v-on:input="something = $event.target.value">