过去一年中,前端技术大发展,最耀眼的明星就是React。
React 本身只涉及UI层,如果搭建大型应用,必须搭配一个前端框架。也就是说,你至少要学两样东西,才能基本满足需要:React + 前端框架。
Facebook官方使用的是 Flux 框架。本文就介绍如何在 React 的基础上,使用 Flux 组织代码和安排内部逻辑,使得你的应用更易于开发和维护。
阅读本文之前,我假设你已经掌握了 React 。如果还没有,可以先看我写的《React入门教程》。与以前一样,本文的目标是使用最简单的语言、最好懂的例子,让你一看就会。
一、Flux 是什么?
简单说,Flux 是一种架构思想,专门解决软件的结构问题。它跟MVC 架构是同一类东西,但是更加简单和清晰。
Flux存在多种实现(至少15种),本文采用的是Facebook官方实现。
二、安装 Demo
为了便于讲解,我写了一个Demo。
请先安装一下。
$ git clone https://github.com/ruanyf/extremely-simple-flux-demo.git $ cd extremely-simple-flux-demo && npm install $ npm start
然后,访问 http://127.0.0.1:8080 。
你会看到一个按钮。这就是我们的Demo。
三、基本概念
讲解代码之前,你需要知道一些 Flux 的基本概念。
首先,Flux将一个应用分成四个部分。
- View: 视图层
- Action(动作):视图层发出的消息(比如mouseClick)
- Dispatcher(派发器):用来接收Actions、执行回调函数
- Store(数据层):用来存放应用的状态,一旦发生变动,就提醒Views要更新页面
Flux 的最大特点,就是数据的"单向流动"。
- 用户访问 View
- View 发出用户的 Action
- Dispatcher 收到 Action,要求 Store 进行相应的更新
- Store 更新后,发出一个"change"事件
- View 收到"change"事件后,更新页面
上面过程中,数据总是"单向流动",任何相邻的部分都不会发生数据的"双向流动"。这保证了流程的清晰。
读到这里,你可能感到一头雾水,OK,这是正常的。接下来,我会详细讲解每一步。
四、View(第一部分)
请打开 Demo 的首页index.jsx
,你会看到只加载了一个组件。
// index.jsx var React = require('react'); var ReactDOM = require('react-dom'); var MyButtonController = require('./components/MyButtonController'); ReactDOM.render( <MyButtonController/>, document.querySelector('#example') );
上面代码中,你可能注意到了,组件的名字不是 MyButton
,而是 MyButtonController
。这是为什么?
这里,我采用的是 React 的 controller view 模式。"controller view"组件只用来保存状态,然后将其转发给子组件。MyButtonController
的源码很简单。
// components/MyButtonController.jsx var React = require('react'); var ButtonActions = require('../actions/ButtonActions'); var MyButton = require('./MyButton'); var MyButtonController = React.createClass({ createNewItem: function (event) { ButtonActions.addNewItem('new item'); }, render: function() { return <MyButton onClick={this.createNewItem} />; } }); module.exports = MyButtonController;
上面代码中,MyButtonController
将参数传给子组件MyButton
。后者的源码甚至更简单。
// components/MyButton.jsx var React = require('react'); var MyButton = function(props) { return <div> <button onClick={props.onClick}>New Item</button> </div>; }; module.exports = MyButton;
上面代码中,你可以看到MyButton
是一个纯组件(即不含有任何状态),从而方便了测试和复用。这就是"controll view"模式的最大优点。
MyButton
只有一个逻辑,就是一旦用户点击,就调用this.createNewItem
方法,向Dispatcher发出一个Action。
// components/MyButtonController.jsx // ... createNewItem: function (event) { ButtonActions.addNewItem('new item'); }
上面代码中,调用createNewItem
方法,会触发名为addNewItem
的Action。
五、Action
每个Action都是一个对象,包含一个actionType
属性(说明动作的类型)和一些其他属性(用来传递数据)。
在这个Demo里面,ButtonActions
对象用于存放所有的Action。
// actions/ButtonActions.js var AppDispatcher = require('../dispatcher/AppDispatcher'); var ButtonActions = { addNewItem: function (text) { AppDispatcher.dispatch({ actionType: 'ADD_NEW_ITEM', text: text }); }, };
上面代码中,ButtonActions.addNewItem
方法使用AppDispatcher
,把动作ADD_NEW_ITEM
派发到Store。
六、Dispatcher
Dispatcher 的作用是将 Action 派发到 Store、。你可以把它看作一个路由器,负责在 View 和 Store 之间,建立 Action 的正确传递路线。注意,Dispatcher 只能有一个,而且是全局的。
Facebook官方的 Dispatcher 实现输出一个类,你要写一个AppDispatcher.js
,生成 Dispatcher 实例。
// dispatcher/AppDispatcher.js var Dispatcher = require('flux').Dispatcher; module.exports = new Dispatcher();
AppDispatcher.register()
方法用来登记各种Action的回调函数。
// dispatcher/AppDispatcher.js var ListStore = require('../stores/ListStore'); AppDispatcher.register(function (action) { switch(action.actionType) { case 'ADD_NEW_ITEM': ListStore.addNewItemHandler(action.text); ListStore.emitChange(); break; default: // no op } })
上面代码中,Dispatcher收到ADD_NEW_ITEM
动作,就会执行回调函数,对ListStore
进行操作。
记住,Dispatcher 只用来派发 Action,不应该有其他逻辑。
七、Store
Store 保存整个应用的状态。它的角色有点像 MVC 架构之中的Model 。
在我们的 Demo 中,有一个ListStore
,所有数据都存放在那里。
// stores/ListStore.js var ListStore = { items: [], getAll: function() { return this.items; }, addNewItemHandler: function (text) { this.items.push(text); }, emitChange: function () { this.emit('change'); } }; module.exports = ListStore;
上面代码中,ListStore.items
用来保存条目,ListStore.getAll()
用来读取所有条目,ListStore.emitChange()
用来发出一个"change"事件。
由于 Store 需要在变动后向 View 发送"change"事件,因此它必须实现事件接口。
// stores/ListStore.js var EventEmitter = require('events').EventEmitter; var assign = require('object-assign'); var ListStore = assign({}, EventEmitter.prototype, { items: [], getAll: function () { return this.items; }, addNewItemHandler: function (text) { this.items.push(text); }, emitChange: function () { this.emit('change'); }, addChangeListener: function(callback) { this.on('change', callback); }, removeChangeListener: function(callback) { this.removeListener('change', callback); } });
上面代码中,ListStore
继承了EventEmitter.prototype
,因此就能使用ListStore.on()
和ListStore.emit()
,来监听和触发事件了。
Store 更新后(this.addNewItemHandler()
)发出事件(this.emitChange()
),表明状态已经改变。 View 监听到这个事件,就可以查询新的状态,更新页面了。
八、View (第二部分)
现在,我们再回过头来修改 View ,让它监听 Store 的 change
事件。
// components/MyButtonController.jsx var React = require('react'); var ListStore = require('../stores/ListStore'); var ButtonActions = require('../actions/ButtonActions'); var MyButton = require('./MyButton'); var MyButtonController = React.createClass({ getInitialState: function () { return { items: ListStore.getAll() }; }, componentDidMount: function() { ListStore.addChangeListener(this._onChange); }, componentWillUnmount: function() { ListStore.removeChangeListener(this._onChange); }, _onChange: function () { this.setState({ items: ListStore.getAll() }); }, createNewItem: function (event) { ButtonActions.addNewItem('new item'); }, render: function() { return <MyButton items={this.state.items} onClick={this.createNewItem} />; } });
上面代码中,你可以看到当MyButtonController
发现 Store 发出 change
事件,就会调用 this._onChange
更新组件状态,从而触发重新渲染。
// components/MyButton.jsx var React = require('react'); var MyButton = function(props) { var items = props.items; var itemHtml = items.map(function (listItem, i) { return <li key={i}>{listItem}</li>; }); return <div> <ul>{itemHtml}</ul> <button onClick={props.onClick}>New Item</button> </div>; }; module.exports = MyButton;
九、致谢
本文受到了Andrew Ray 的文章《Flux For Stupid People》的启发。
(完)
mrdream 说:
好复杂,看一遍根本不懂
2016年1月15日 10:46 | # | 引用
workdog 说:
action不一定要放在controller view里,要是组件层次很深,传props会很累的,可以直接放在需要和action打交道的view里,fb的官方示例(todomcv和chat)就是这么做的
2016年1月15日 11:52 | # | 引用
abc 说:
文章中 MyButtonController.jxs 的示例中的类名误写成 MyButton 了.
2016年1月15日 12:25 | # | 引用
夜幕下的猫 说:
第一部分:
var MyButton = React.createClass({
......
});
这里的 MyButton 应该是 MyButtonController 吧?
2016年1月15日 13:28 | # | 引用
xenojoshua 说:
Flux 的设计理念确实很棒,但是一个简单的列表就可以搞这么复杂,实在是不太利于做项目
2016年1月15日 13:42 | # | 引用
阮一峰 说:
@abc,@夜幕下的猫:
谢谢指出,已经改过来了。
2016年1月15日 13:56 | # | 引用
ArchiTech 说:
学海无涯
2016年1月15日 15:38 | # | 引用
钊王 说:
刚看了一遍没有看懂
2016年1月15日 19:22 | # | 引用
Xixilive 说:
我表示非常不认同把Flux与MVC放在一起说,Flux完全是另外一种Pattern。
2016年1月15日 23:00 | # | 引用
必填 说:
刚好前两天看到有人在抱怨前端技术…… https://medium.com/@wob/the-sad-state-of-web-development-1603a861d29f#.iuy0tz462
你们说的我都不懂,我就看看热闹。
2016年1月16日 05:17 | # | 引用
阮一峰 说:
前端现在就是黎明前的黑暗,大家各自寻找解决方案,一两年后就会清晰。
2016年1月16日 10:14 | # | 引用
cshenger 说:
看了一遍大概明白Flux的设计思路了,谢谢阮老师能把教程写的如此简单易懂,理解起来倒不困难就是感觉实现一个简单的功能都这么复杂,实在有点累觉不爱啊。
2016年1月16日 11:34 | # | 引用
yaoelvon 说:
使用Angular1.x版本中,未来会接触React,加油!
2016年1月16日 17:13 | # | 引用
kingguy 说:
楼主为什么不用ES6写呢?
2016年1月17日 15:39 | # | 引用
Loogeek 说:
很简单,作为入门教程来讲,降低学习门槛以达到分享知识见解的目的是很重要的,很多初学者不一定接触过ES6,只是想了解下Flux,React官网教程也没有在主篇幅用Es6,只是在一些变化处写了说明,对于想用Es6写的这也不是什么问题
2016年1月18日 09:43 | # | 引用
qzttt 说:
买一本书,支持一下,阮老师的书还是很好的
2016年1月19日 11:15 | # | 引用
Min 说:
对于入门的demo来说简单才是王道。看了你的demo,再看看官方的“Flux TodoMVC Example,思路渐渐清晰,非常感谢楼主分享,当初react就是看楼主的文章入门。
2016年1月19日 16:08 | # | 引用
迹忆 说:
感谢分享,思路很清晰
2016年1月22日 16:31 | # | 引用
edokeh 说:
监听 Action 的回调那段写的不对吧?应该写在 Store 里,而不是 AppDispatcher;
比如官方 example 里的这一段https://github.com/facebook/flux/blob/master/examples%2Fflux-todomvc%2Fjs%2Fstores%2FTodoStore.js
而且例子里如果能举一下 waitFor 的用法,更能体现 Flux 的精髓
2016年1月24日 20:59 | # | 引用
呃。。。 说:
贴个一年前翻译的启发文章链接。http://caichao.me/2015-01-14-FluxForStupidPeople.html
2016年1月25日 11:11 | # | 引用
咸鱼老弟 说:
多谢ruan大神的分享,之前看flux一直没看懂,现在清晰多了
2016年1月25日 15:29 | # | 引用
bear 说:
阮老师的博客,思路清晰,赞赞赞....
2016年1月25日 15:54 | # | 引用
tata 说:
2016年1月26日 17:46 | # | 引用
阮一峰 说:
@edokeh,@tata:
我觉得,与派发器有关的逻辑,都放在AppDispatch比较好。这样,Store就不用加载AppDispatch了。
2016年1月26日 19:54 | # | 引用
但丁 说:
一直没能下定决心去学react,就是因为代码看起来好复杂啊,看一遍根本没看懂,代码可读性不高,看起来乱七八糟的的感觉
2016年1月30日 19:38 | # | 引用
黄哲霆 说:
阮大大,其实我觉得flux是mvc的更好的实践吧,action类似于controller的角色。
第一:改了名字之后,感觉更加向ui靠拢,意为ui的动作,更加直接的指向了ui输入。另外,这样做的好处也是为每一个ui输入留下一笔记录。
第二:也更加明确了action的范围,只是向model发送事件,不对model做任何直接的改变,model内部通过action发来的事件自己处理数据。这也使得model的改变有了稳定的位置,有利于控制和追踪代码。
不知道我这样的理解是否正确?
2016年2月 3日 15:00 | # | 引用
james 说:
阮老师啥时候讲下Redux,很是期待哈。
2016年2月 4日 20:04 | # | 引用
大名 说:
Flux For Stupid People
2016年2月21日 17:49 | # | 引用
sz 说:
这到底是在客户端运行的,还是在node.js中运行的。
现在出的东西,对于我这种菜鸟来说真的搞不懂
2016年2月29日 19:36 | # | 引用
Ken Zhang 说:
Facebook画的那个MVC的图,View和Model之间是直接互相调用的,而且这么多个View和Model才对应一个Controller,似乎跟我对MVC的理解有很大出入。
我分别用Flux和MVC各实现了一次Todo。老实说,没看出Flux的优点,反倒觉得有以下缺点:
1. 规定Dispatcher为全局的单例。个人认为任何全局对象或者单例都会降低对它有依赖的类的内聚性,也会造成对View和Store单元测试上的障碍。比方说,定义一个class A,它内部调用了单例class B。首先A的接口没有体现出这种依赖,单元测试也不方便mock。其次对A的所有测试用例,都要把B的行为考虑进去。
2. View同时依赖于Store和Dispatcher。在MVC中,Model和View的所有事件都通过Controller来中转,保证了View和Model的独立。而Flux中,View触发事件(Action)时是通过Dispatcher间接传到Store上,但取数据时又是直接访问Store。导致View需要同时依赖于Store和Dispatcher,这更像是一种倒退。
就说那么多吧。限于个人水平,认识可能有许多误解,请大神帮忙指正,感激不尽~~
2016年3月 1日 06:52 | # | 引用
bai 说:
_onChange: function () {
this.setState({
items: ListStore.getAll()
});
},
这个触发后, this 对象变为了 store 对象。没有setState 方法,老师遇到这个问题没
2016年3月 7日 17:57 | # | 引用
可乐加糖 说:
额,一遍看下来表示完全不懂,还是多看几遍把。
2016年3月 8日 23:11 | # | 引用
lazyou 说:
对flux的action dispatch表示费解, 和 PubSub一样吗?
我直接放弃flux,用pubsub来处理。
最上层的组件(Controller)subscribe事件, 子组件(View)publish触发事件去该表store(Model)。
store变化由最顶层setState, 子组件通过 shouldComponentUpdate(nextProps, nextState) 来决定是否更新。
感觉本质上应该是一样的。
2016年3月22日 20:35 | # | 引用
咸鱼老弟 说:
之前看了一遍没看懂,看了几遍之后,再自己写写,终于搞懂了flux。不过在实际项目中,如果项目不是很大,发觉这个东西没什么用处。而且如果一个项目好几个人一起做,有些人会flux,有些不会,沟通成本其实还蛮大的
2016年4月 6日 10:29 | # | 引用
front-thinking 说:
阮老师的这篇讲解Flux技术的文章比之于其它草草翻译官方文档的文章来说的确非常容易理解,深入浅出。仔细看了两三遍后,的确明白了这种架构数据流的过程。但是,其实还有一点不甚清楚的就是为什么要如此来处理数据流,这样有什么好处等等,关于这几点感觉文章并没有提到。因为,单纯从例子来看,反而觉得不用Flux会更简洁、方便一些。
2016年4月12日 15:49 | # | 引用
二点零 说:
请问阮老师,npm start启动的时候,控制台报了以上错误,应该怎么解决呢?没有接触过webpack,问题应该很低级,实在抱歉。
2016年4月15日 10:58 | # | 引用
阮一峰 说:
@二点零:
我这里是正常的,没法复现这个错误,猜不出原因啊。要不你删掉 node_modules 目录,重装一次。
2016年4月16日 11:27 | # | 引用
二点零 说:
问了你之后不久就解决了,因为之前没有了解和学习过webpack,后面专门去入门了一下。问题原因是webpack没有全局安装。感谢阮老师,学习了很多!!!
2016年4月21日 11:52 | # | 引用
diper 说:
> webpack-dev-server --progress --port 8888
0% compile http://localhost:8888/webpack-dev-server/
webpack result is served from /
content is served from D:\git\extr 95% emit
npm ERR! Windows_NT 6.1.7600
npm ERR! argv "C:\\Program Files\\nodejs\\node.exe" "C:\\Program Files\\nodejs\\ node_modules\\npm\\bin\\npm-cli.js" "start"
npm ERR! node v4.4.4
npm ERR! npm v2.15.1
npm ERR! code ELIFECYCLE
npm ERR! [email protected] start: `webpack-dev-server --progress --port 8888`
npm ERR! Exit status 3221225501
npm ERR!
npm ERR! npm bugs flux-for-stupid-people-demo
npm ERR! D:\git\extremely-simple-flux-demo\npm-debug.log
windows上报这个错误有没有遇到过
2016年5月11日 18:22 | # | 引用
189 说:
请收下我的膝盖,顺便啥时候出个 redux 的教程呢?
2016年5月14日 19:48 | # | 引用
ruby 说:
阮老师好,问个题外话,为什么执行demo的时候不能直接用webpack-dev-server来执行?而一定要用package.json中的npm start? npm start不也是执行webpack-dev-server --progress么?
2016年5月20日 15:24 | # | 引用
菜鸟 说:
老师讲讲webpack哈
2016年5月23日 15:06 | # | 引用
北去 说:
刚开始学flux,有点看不懂,看来得多看几遍啊。哈哈
2016年5月27日 13:31 | # | 引用
Adam Lu 说:
确实是一开始有点儿费解,头一遍完全晕掉
2016年5月30日 10:54 | # | 引用
普罗 说:
其实我的理解 就是广播机制更细化了而已……
先说说对广播机制的理解----有一个radio center,任何对象都可以订阅它
如radio.on(xxx,function(){ do....})
然后也可以通过它来发消息
Radio.push(xxx)
订阅了该消息的对象,响应的做些行为
而flux就是把on和push的写法抽离到了action和dispatch里统一管理,但这种抽离不见的一定好。
不知道我的理解对不对……
2016年6月 1日 08:06 | # | 引用
沐浴晴朗的心 说:
之前一直接触的是js和页面,对于安装的指令看不明白,现在都无法启动例子。有没有简单的教程呢?
2016年6月 2日 14:53 | # | 引用
jahon 说:
@diper:
没安装全局webpack. 执行npm install webpack -g
2016年6月24日 19:17 | # | 引用
asdf 说:
git clone之后是不是还要npm install啊
2016年8月22日 12:36 | # | 引用
jdy 说:
阮老师什么时候讲讲redux和webpack啊?
2016年9月 7日 15:41 | # | 引用
哈哈 说:
组件化开发之后就明白好处了啊。细流合成江湖,公用状态方便获取等。
二是数据都汇起来了方便做实时监测。
2016年9月10日 10:49 | # | 引用
Seven 说:
阮老师博客最棒的地方就是能把复杂的东西变成简单的。让大家都能读懂。感谢。
2016年9月10日 16:17 | # | 引用
幽灵 说:
感觉确实不如redux思路清晰,只是作为了解皆可以了。
2016年9月26日 21:59 | # | 引用
kyle 说:
讲的很棒,一遍就懂了。
2016年10月11日 14:00 | # | 引用
辛未六月羊 说:
之前用过vuex,但概念上一直有点模糊,看了阮大神的文章,感觉清晰多了。
2016年10月17日 17:28 | # | 引用
码农的男人 说:
在store的数据层为什么addChangeListener里面的this后面跟on而在removeChangeListener里面的this后面不是更on的?
有点不理解求解中
2016年10月20日 18:02 | # | 引用
wikiJane 说:
请问阮老师MyButton那个组件是怎么回事?为什么是function的?import了React为什么没有用到的?
2016年10月25日 16:37 | # | 引用
tylerdong 说:
大神我下载了代码,在自己的机器上看,为啥没有这两个js文件(init.js,bundle.js)呢?访问报错了
2016年11月 7日 17:50 | # | 引用
nortonlee 说:
webpack构建
2016年11月12日 22:26 | # | 引用
LiShuxue 说:
学React没多久,看您这篇文章遇到一个困惑。在您的MyButtonController.jsx中,return了标签,按理说这个MyButton应该是一个组件,但是为什么在Mybutton.jsx中却没有用React.createClass去创建这个组件,而Mybutton却是一个function呢?求解惑...
2016年12月 2日 10:10 | # | 引用
LiShuxue 说:
同问,在MyButtonController.jsx引用了这个《MyButton /》这个标签,这个标签按说应该是一个组件,可是在MyButton.jsx却是用function去定义的这个?为什么这样,如果这样定义,他为什么可以通过标签《MyButton /》去引用?
2016年12月 2日 10:17 | # | 引用
liumingyuan 说:
为什么在启动的时候,会报错:
C:\Users\myliu\WebstormProjects\lmy_developer\node_modules\open-browser-webpack-plugin\index.js:50
throw err+url;
^
Error: Command failed: C:\Windows\system32\cmd.exe /s /c "start "" "http://localhost:8080""
ϵͳ��ִ��ָ���ij�����
然后在webpack.config.js中删除插件openBrowser在启动就可以了呢?
2016年12月19日 16:20 | # | 引用
尊上 说:
和MVC有什么不同,感觉是一样的
2016年12月20日 09:36 | # | 引用
tester 说:
_onChange那里确实会报this.setState is undefined
2016年12月22日 21:23 | # | 引用
caicai 说:
看下创建React组件的方式,有三种方式,大神用的这种叫无状态组件,
createCLass方式是es5的方式,还有一种es6的方式,就是class Mybutton extends React.component{ //xxxx }
2017年1月 5日 12:12 | # | 引用
小虾米 说:
这个组件 如果多次使用的话 就是一个页面出现多个 不能独立开来 数据都是共享的
2017年2月16日 15:41 | # | 引用
蔓蔓 说:
终于把整个流程搞明白了
2017年3月17日 20:47 | # | 引用
BD7IWD 说:
请问“Flux 的最大特点,就是数据的"单向流动"。”上面那个流程图是什么工具画的?
2017年4月22日 20:40 | # | 引用
paysage 说:
还是更喜欢redux的思路多一些 不过阮老师的文章确实帮助入门不少 浅显易懂 感谢
2017年5月 2日 00:45 | # | 引用
carl 说:
要做一个阮老师那样的男人
2017年6月27日 17:54 | # | 引用
疯的旅人 说:
请问,MyButtonController.jsx中的ListStore.addChangeListener(this._onChange),是如何保证参数_onChange函数中出现的this指向的是MyButtonController组件对象而不是window对象?
2017年7月11日 11:31 | # | 引用
sunxy1990 说:
写的很好,我看了两遍才看明白,我的目的不是学flux,而是redux,但学redux之前要学flux,我感到难过的是,demo我运行不了,报错,而且可能阮老师的文章写的早,写法跟现在写法不一样,不过还是理解了flux的流程,比书上讲的明白,thanks
2017年8月 1日 10:56 | # | 引用
ff 说:
原来我们都是stupid poeple
2017年8月 8日 16:53 | # | 引用
Mailomail 说:
怎么看Web上的这些工具框架都想是WPF和QML的复杂抄袭版本。
2017年8月12日 17:13 | # | 引用
helloyxw 说:
我把Demo下载安装后能运行,但不理解setState函数在哪里定义的?
2017年8月29日 09:12 | # | 引用
一网友 说:
看了四五遍,终于有点眉目了!更新个数据,facebook搞的这么麻烦!不过厉害了我的峰哥,你是我心中的支柱!
2017年9月18日 14:43 | # | 引用
初学react的学渣 说:
讲的不错,不过例子感觉不够好,感觉例子可以用更简单的办法写出来,感觉这样用让flux大材小用了
2017年10月11日 07:55 | # | 引用
mo 说:
第一次看的时候直接看代码硬啃,今天来复习一下的,看了一下,文章写的真心不错,这就是设计思路。
2017年10月11日 11:47 | # | 引用
pageliu16 说:
看了两三遍,基本上有点思路了,估计还要跟着Demo写上一遍才算入门。
2017年10月22日 19:56 | # | 引用
loycoder 说:
嗯。代码有些绕,我也是边看,边写注释的。
2017年11月10日 10:55 | # | 引用
二元 说:
看来未来世界的幸存者,很有感悟,感谢阮老师的分享
2017年12月25日 14:19 | # | 引用
崔庆华 说:
看到Dispatcher部分的时候,有一个非常大的疑问,直接将对应的store引入进来,如果store变多,Dispatcher会不会越变越大呢?
2018年3月14日 10:15 | # | 引用
Kevin 说:
这样的设计只适合做大型的,业务逻辑比较复杂的项目。不过这种设计理念还是值得学习的。谢谢阮老师分享。
2018年5月12日 10:32 | # | 引用
小米虾 说:
dispatch 和 register 这种绑定关系,阮老师能不解读一下? 最好贴上源码! 工作2年多了,一直做node微服务开发,还没怎么学习react,写的很好,基本看懂了!
2018年5月25日 23:28 | # | 引用
Link_x 说:
三年后再看,前端现在关于框架之争确实少了很多,阮老师诚不欺我也
2019年12月 7日 11:20 | # | 引用
无 说:
AppDispatcher.js中的“ListStore.emitChange();”放到ListStore.js的addNewItemHandler中更合适
2019年12月25日 14:40 | # | 引用