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 ...
随机推荐
- ps命令详解
1.简介: ps 命令有两种不同的语法风格 —— BSD 与 UNIX 两种风格.新手常常对这两种形式产生误解,因此我们有必要在这里作一个简单的说明: ps aux 与 ps -aux 是不同的,例如 ...
- df命令详解
1.简介: df命令作用是列出文件系统的整体磁盘空间使用情况.可以用来查看磁盘已被使用多少空间和还剩余多少空间. df命令显示系统中包含每个文件名参数的磁盘使用情况,如果没有文件名参数,则显示所有当前 ...
- ceph常用运维技巧总结1
格式 json 数据增强可读性 --format json-pretty -f json-pretty ceph quorum_status -f json-pretty ceph mon_statu ...
- R语言-简单模型画图
1.回归拟合 > plot(mtcars$mpg~mtcars$disp) > lmfit<-lm(mtcars$mpg~mtcars$disp) #线性回归模型 > abli ...
- Unity中的点击,长按,划动
public void GetClickType() { if(Input.GetMouseButtonDown(0)) { if(isGetBeginPos) { beginPosition = I ...
- 网络文件系统NFS
NFS介绍 什么是NFS? NFS是Network File System的缩写,即网络文件系统.它的主要功能是通过网络(一般是局域网)让不同的主机系统之间可以共享文件或目录.NFS客户端(一般为应用 ...
- js监控鼠标滚动事件
//滚动动画 windowAddMouseWheel(); function windowAddMouseWheel() { var scrollFunc = function (e) { e = e ...
- 使用setx 命令添加环境变量(Windows)
背景 用GUI的方法可能添加环境变量可能会比较麻烦,为此可采用命令行操作的方式. 步骤 以管理员身份运行 cmd 输入 setx /M "%path%" "%path%[ ...
- 如何访问dashed filename
第一关:boJ9jbbUNNfktd78OOpsqOltutMc3MY1 第二关:CV1DtqXWVFXTvM2F0k09SHz0YwRINYA9 cat <- 第三关:如何访问spaces i ...
- centos7切换图像界面和dos界面
在图形界面使用 ctrl+alt+F2切换到dos界面 dos界面 ctrl+alt+F2切换回图形界面 在命令上 输入 init 3 命令 切换到dos界面 输入 init 5命令 切换到图形界面 ...