几种实现双向绑定的做法:
目前几种主流的mvc(vm)框架都实现了单项数据绑定,而我所理解的双向数据绑定,无非就是在单项数据绑定的基础上给输入元素input textare等添加了change(input)事件,来动态修改model和view,并没有多高深。所以无须介怀是实现的单项或者双向绑定,实现数据绑定 大致几种:
- 发布者-订阅者(backbone.js);
- 一般通过sub,pub的方式实现数据和视图的绑定监听,更新数据的方式通常是 vm.set('property',vlue);
- 脏值检查(angular.js);
- Angular.js是通过脏值检测的方式比对数据是否有变更。来决定是否更新视图,最简单方式是通过setinterval()定时轮询检测数据变动,当然angular只是在指定的事件触发时进入脏值检测:
- Ng-click
- $http
- $location
- $timeout $interval
- $digest()||$apply()
- 数据劫持(vue.sj);
Vue.js则采用数据劫持+发布者订阅者模式的方式,通过0bject.defineProperty()来劫持各个属性的setter getter,在数据发生改变时发布消息给订阅者,触发相应的监听回调。具体整理:
vue是通过数据劫持的方式来做数据绑定,其中核心方法是通过object,defineProperty()来实现对属性的劫持,达到监听数据变动的目的,无疑这个方法是本文中最重要,最基础的内容之一。
Object.defineProperty()直接在一个对象上定义一个新的属性,或者修改一个对象的现有属性,并返回这个对象。
实现mvvm双向数据绑定,步骤:
- 实现一个数据监听observer(观察者),能够对数对象的所有属性进行监听,如果有变动可拿到最新值并通知订阅者;
- 利用object.defineProperty()来监听属性变动;那么将需要observer的数据对象进行遍历,包括子属性对象的属性,都加上setter和getter这样的话给这个对象的某个值赋值,就会触发setter,从个人监听到数据变化。
- 监听到后,怎末通知订阅者呢?所以需要实现一个消息订阅器:维护一个数组,用来手机订阅者,数据变动会触发notify,再调用订阅者的update方法。watcher
- 实现一个指令解析器Compile,对每个元素的指令进行进行扫描和解析,根据指令模板替换数据,以及绑定相应数据;
- complile解析模板指令,将模板中的变量替换成数据,然后初始化渲染页面视图,并将每个指令对应的节点绑定跟新函数,方便随时更新视图。
- 因为遍历解析的过程有多次dom节点,为了提高性能和效率,会先将节点el转换为文档碎片进行解析编译(js对象虚拟DOM),解析完成后再添加回原来的真实dom节点中。
- 实现watcher,作为连接observer和compile的桥梁,能够订阅并收到每个属性变动的通知,执行指令绑定相应的会回调函数,从而跟新试图。
- 在自身实例化时往属性订阅器dep里面添加自己;
- 自身必须有一个update()方法;
- 代属性变动dep.notice()通知时,能调用自身的update()方法,并触发complie中绑定的回调.
- mvvm入口函数,整合以上三者;达到数据变化-》视图更新;视图交互变化(input)->数据model变更的双向数据绑定效果;
MVVM:model view view model
数据双向绑定,简化了页面和业务的依赖,解决了数据频繁更新。MvvM在使用当中,利用双向数据绑定计数,使得Model变化时,viewModel 会自动更新,而viewModel 变化时,view也会自动变化。
双向数据绑定原理:
采用数据劫持:发布者和订阅者模式的方式,通过object.defineProperty()来劫持各个属性的setter和getter,在数据变动时发布消息给订阅者,出发相应的监听回调;具体步骤:
- 当把一个普通的js对象传给VUE实例来作为他的data选项时,vue将遍历他的属性,用object.defineProperty都加上setter和getter方法,这样给这个对象的某个值赋值,就会出发setter,那么就能监听到数据变化;
- compile解析模板指令
- Watcher 订阅者是observer和compile之间的桥梁,主要做的事:
- 在自身实例化时往属性订阅器(dep)里添加自己;
- 子集必须有一个update()方法
- 待属性变动dep.notice通知时,调取update()方法,并触发compile绑定的回调,则功成身退。
- MvvM作为数据的绑定入口,整合observer compile watcher三者:
- 通过observer来监听自己的model数据变化;
- 通过compile来解析编译模板指令
- 最终利用wacher搭起observer和compile之间的通信桥梁,
- 达到数据变化=>视图跟新;视图交互变化(input)=>数据model变更的双向数据效果。
proxy相比defineProperty优势:
Object.defineProperty()三个主要问题:
- 不能监听数组变化;
- 必须遍历对象的每个属性;
- 必须生层次遍历嵌套的对象;
而 ES5的proxy新加入,特点:
- 针对对象:针对整个对象而不是对象的某个属性,所以也就不需要keys进行遍历
- 支持数组:proxy不需要对数组进行重载,省去了众多hack,减少代码等于减少维护成本。
vue中的虚拟DOM:
虚拟DOM本质就是一个和真实DOM结构类似的Js对象;
虚拟DOM可以提高浏览器的渲染速度。对比操作Js对象,比操作真实的DOM消耗的性能少的多,特别时 频繁的操作DOM时,优势彰显的更加明显。
虚拟DOM步骤:
- 在页面首次渲染时,将要渲染的数据全部加载到虚拟DOM中,而后在一次性渲染到真实的DOM上;
- 数据变动时,额外生成一个虚拟DOM树,
- 通过Diff算法对比修改的部分,而后将修改部分渲染到真实的DOM中;
- 释放内存。