React中的“双向绑定”
概述
React并不是一个MVVM框架,其实它连一个框架都算不上,它只是一个库,但是react生态系统中的flux却是一个MVVM框架,所以我研究了一下flux官方实现中的“双向绑定”,并记录下来供以后开发时参考,相信对其他人也有用。
参考资料:
如何监听 js 中变量的变化?
Flux For Beginners
数据双向绑定的分析和简单实现
The ReactJS Controller View Pattern
Controller View
React的核心思想是Controller View设计模式:顶层组件具有所有的state,并把它们作为props向下传递给子组件。
这样的好处有:
- 如果要增加一个相同的子组件,直接增加即可。
- 如果有url参数解析,直接从父组件得到,不需要单独重复处理。
- 与可变的state相比,静态的props更加容易理解和预测。
- 方便测试。(只需传入props即可)
这种设计模式实现的数据流动其实是一种单向流动,即从父组件流向子组件。如果子组件要向父组件传递数据,那么只能通过父组件把回调函数作为props传递给子组件,然后子组件通过调用这个回调函数来传递数据给父组件,这样就实现了父组件和子组件之间数据的双向流动。
然而,这个设计模式有一个缺点,就是需要一层一层地向下传递数据,如果层级很多的话,就特别麻烦,每个子组件接收的props不全是它需要的数据,还有很多它并不需要但是它的子组件需要的数据。在这种情况下就需要用到flux。
注意:如果层级很少的话,就不建议使用flux或redux。
flux的双向绑定
我们知道数据的双向绑定是指:
- view层的用户修改界面数据,model层的数据也会被修改。这个可以通过浏览器自带的事件响应来解决。
- model层的数据修改会同步到view层画面的变化。这个时候就涉及到2个方面,一个是model层的数据会渲染到view层,通过react的数据流动即可实现;另一个是model层的数据变化会引起注意,在这个方面,angular是通过脏检测实现的,vue是通过es5的getter和setter以及Object.defineProperty方法(数据劫持)实现的,那么flux是怎么实现的呢?
flux是通过和浏览器类似的事件响应实现,通过事件监听数据的变化,如果有变化,就引发一个change事件,从而实现同步数据到view层。
事件监听其实是一种观察者模式,下面我们来具体讨论一下事件监听的实现。这个实现需要解决下列问题(以change事件为比方):
- 数据具有绑定change事件的方法。
- 数据在change事件发生的时候能够调用绑定的回调函数。
- 数据在改变的时候能够触发change事件。
首先我们要引入MicroEvent.js,这个库只有一页代码,我选取其中重要的部分来讲解:
var MicroEvent = function(){};
MicroEvent.prototype = {
bind : function(event, fct){
this._events = this._events || {};
this._events[event] = this._events[event] || [];
this._events[event].push(fct);
},
unbind : function(event, fct){
this._events = this._events || {};
if( event in this._events === false ) return;
this._events[event].splice(this._events[event].indexOf(fct), 1);
},
trigger : function(event /* , args... */){
this._events = this._events || {};
if( event in this._events === false ) return;
for(var i = 0; i < this._events[event].length; i++){
this._events[event][i].apply(this, Array.prototype.slice.call(arguments, 1));
}
}
};
MicroEvent对象的实例有bind,unbind和trigger方法,分别对应绑定自定义事件,解绑,触发事件。这样通过object.asign方法就能把这些方法“给”数据。这就解决了第一个问题。
然后数据在通过bind绑定自定义事件及回调函数之后,可以通过trigger触发自定义事件并且依次执行绑定在事件上的回调函数。这就解决了第二个问题。
接下来是第三个问题,我们怎么在数据改变的时候触发事件???
在这一点上,vue是通过es5的getter和setter以及Object.defineProperty方法,通过重写setter函数,并在里面写上trigger事件的代码,实现数据在改变的时候能自动调用trigger方法,从而实现了触发事件。
但是flux用的并不是这种方法!!!我们先来看一下vue里面实现事件响应的过程,数据的任何改动都会触发Object.defineProperty绑定的setter方法,从而实现调用trigger方法。所以如果我们只定义具体的改动呢?这样是不是可以不用Object.defineProperty方法?
这就是flux的实现,我们并不是直接修改数据,而是通过定义具体的动作,通过这个动作修改数据。
AppDispatcher.dispatch({
actionName: 'new-item',
newItem: { name: 'Marco' } // example data
});
而这个动作在被调用的时候会自动触发trigger函数,从而实现事件响应!!!
这就是为什么flux里面要分为Actions,Dispatcher,Store的原因。我们并不是直接修改数据,而是通过一个中间层Actions修改数据,这样这个中间层在被调用的时候会触发trigger函数,实现事件响应!
其它
值得一提的是,node自带Events库,通过这个库也能够实现与上面的事件响应。但是MicroEvent.js适用性更广,它还能够适用于客户端。
另外,es6定义了新的双向绑定机制——Proxy。
Proxy就是对象代理,类似上面的中间层actions,它可以给一个对象绑定一个代理对象,通过这个代理对象来代理原对象的各种行为。
var p = new Proxy(target, handler);
let a = new Proxy({}, {
set: function(obj, prop, value) {
obj[prop] = value;
if (prop === 'zhihu') {
console.log("set " + prop + ": " + obj[prop]);
}
return true;
}
});
a.zhihu = 100;
当然,Proxy的能力远不止此,还可以实现代理转发等等。
至于兼容性,Proxy的大部分方法都被各大浏览器实现了,只有少数几个方法没有被实现。具体可以看这里:MDN Proxy。
React中的“双向绑定”的更多相关文章
- AngularJS中数据双向绑定(two-way data-binding)
1.切换工作目录 git checkout step-4 #切换分支,切换到第4步 npm start #启动项目 2.代码 app/index.html Search: <input ng-m ...
- vue中的双向绑定
概述 今天对双向绑定感兴趣了,于是去查了下相关文章,发现有用脏检查的(angular.js),有用发布者-订阅者模式的(JQuery),也有用Object.defineProperty的(vue),其 ...
- react 实现数据双向绑定
好久没有更新了 只是都写在有道笔记中 今天整理下 一些基础的 大神勿喷 一个基础的不能再基础的数据双向绑定 因为react不同于vue 没有v-model指令 所以怎么实现呢? import Reac ...
- vue中数据双向绑定注意点
最近一个vue和element的项目中遇到了一个问题: 动态生成的对象进行双向绑定是失败 直接贴代码: <el-form :model="addClass" :rules=& ...
- vue中数据双向绑定的实现原理
vue中最常见的属v-model这个数据双向绑定了,很好奇它是如何实现的呢?尝试着用原生的JS去实现一下. 首先大致学习了解下Object.defineProperty()这个东东吧! * Objec ...
- javascript中的双向绑定
阅读目录 一:发布订阅模式实现数据双向绑定 二:使用Object.defineProperty 来实现简单的双向绑定. 前言: 双向数据绑定的含义:可以将对象的属性绑定到UI,具体的说,我们有一个对象 ...
- wp中的双向绑定
using System; using System.Collections.Generic; using System.ComponentModel; using System.Linq; usin ...
- 利用JS实现vue中的双向绑定
Vue 已经是主流框架了 它的好处也不用多说,都已经是大家公认的了 那我们就来理解一下Vue的单向数据绑定和双向数据绑定 然后再使用JS来实现Vue的双向数据绑定 单向数据绑定 指的是我们先把模板写好 ...
- AngularJS学习--- AngularJS中数据双向绑定(two-way data-binding) orderBy step4
1.切换工作目录 git checkout step- #切换分支,切换到第4步 npm start #启动项目 2.代码 app/index.html Search: <input ng-mo ...
随机推荐
- java学习--equals
Object类是所有类的基类. Object类有equals方法.而继承Object中的equals方法判断的是两个对象的引用是否相等,相当于"==",也就是说只有比较的两个对象为 ...
- spring @transactional 注解事务
1.在spring配置文件中引入<tx:>命名空间 <beans xmlns="http://www.springframework.org/schema/beans&qu ...
- 微信开发 invalid openid
微信开发时候测试号运行正常,换到正式号就会报invalid openid的错误. 看了微信问答系统里的答案,说是json格式的问题,但是我这边不是这个原因. 后来突然想到了,应该是AppId和AppS ...
- oracle --hint总结
得到一条sql语句执行计划的常用方法:1.explain plan 命令 2.DBMS_XPLAN包3.sqlplus中的AUTOTRACE开关4.10046事件5.10053事件6.AWR报告或者 ...
- @font-face使用在线字体
@font-face规则在CSS3规范中属于字体模块,该规则的推出对于网页设计来说是一个革命性的进步.在传统设计中,设计师不敢使用各种艺术字体类型,甚至是常规字体也需要慎重使用.因为设计师必须考虑每位 ...
- tomcat/Java指定加载jar包的路径
背景:部署的web站点,应用默认加载工程的/webapps/工程名/WEB-INF/lib下的jar包 但是我需要提供一个和web工程没关系的的jar包管理目录 解决方法: 执行java方法时 ...
- jQueryEasyUI学习笔记
data-options 是jQuery Easyui的一个特殊属性.通过这个属性,我们可以对easyui组件的实例化可以完全写入到html中 data-options="region:'w ...
- 关于@autoreleasepool
苹果推荐使用场景: 如果你编写的程序不是基于 UI 框架的,比如说命令行工具: 如果你编写的循环中创建了大量的临时对象:(常用) 如果你创建了一个辅助线程. @interface ViewContro ...
- pythone函数基础(8)内置函数学习
内置函数学习# sorted# map# filter# max# sum# round# chr# ord# dir# bool# eval# exec# zipimport mathres = m ...
- ArrayList 初探
1.ArrayList继承AbstractList,实现List.RandomAccess.Cloneable.Serializable接口 public class ArrayList<E&g ...