【译】Flux入门
栏目: JavaScript · 发布时间: 5年前
内容简介:原文地址:TL;DR 当我在努力学习Flux时,我希望有人告诉我:它并不简单,没有好的详细文档,并且有许多变化的部分。如果你的应用程序需要处理动态数据(dynamic data)的话,那么答案就是yes,你可能需要使用Flux。
原文地址: blog.andrewray.me/flux-for-st… ,作者:Andrew Ray
TL;DR 当我在努力学习Flux时,我希望有人告诉我:它并不简单,没有好的详细文档,并且有许多变化的部分。
我需要使用Flux吗?
如果你的应用程序需要处理动态数据(dynamic data)的话,那么答案就是yes,你可能需要使用Flux。
如果你的应用程序仅仅是无需共享状态静态视图(static view),并且你从不保存也不更新数据,那么你不需要使用Flux,Flux不会给你带来任何好处。
为什么是Flux?
皮一下,因为Flux是个一个适度复杂的主意,为啥增加复杂度呢?
90%的iOS应用程序是表格视图中的数据。iOS工具包具有良好定义的视图和数据模型可以让应用开发变得简单。
但是在前端(Font End:HTML,JS,CSS),我们甚至都没有。相反,我们遇到一个很大的问题:没有人知道应该如何去构建一个前端应用。我从事这个行业多年,从来没人教给我“最佳实践”,相反,他们教了我好多“库(libraries)”,诸如jQuery,Angular,Backbone等等。但是真正的问题、数据流,仍然避开了我们。
什么是Flux?
Flux是一个用来描述具有非常特定事件和监听的“单向”数据流的词。没有官方的Flux库,但是你需要 Flux Dispatcher 和任何的JavaScriptevent library。
官方文档写的就像某人的意识流一样,从这里开始学习是不太好的。但是一旦你掌握了Flux,它可以帮助你填补空白。
不要试图把Flux同MVC架构进行比较,它们的相似之处只会令人困惑。
正式入坑!我将按顺序解释概念,并且一个一个地构建它们。
1.视图的“Dispatch”和“Actions”
Dispatcher(调度员)本质上是一个加入了额外规则的事件系统。它来广播事件并注册回调。全局的dispatcher只有唯一的一个,你应该使用Facebook Dispatcher Library。实例化非常容易:
var AppDispatcher = new Dispatcher(); 复制代码
假设你的应用程序有一个“新建”按钮来向列表添加项目。
<button onClick={ this.createNewItem }>New Item</button> 复制代码
点击会发生什么?你的视图会调度 一个非常具体的“操作” ,其中包含操作名称和新项目数据:
createNewItem: function( evt ) { AppDispatcher.dispatch({ actionName: 'new-item', newItem: { name: 'Marco' } // example data }); } 复制代码
“ action ”是Facebook创造的另一个词。它是一个JavaScript对象,用以描述我们 想要做什么事情 ,以及做这件事我们 需要的数据 。正如你所见到的,我们要做的事情就是添加一个 new-item
,我们需要的数据就是项目 name
。
2."Store"响应调度的操作
像Flux一样,“Store”这个词也是Facebook创造的.对于我们的应用程序,我们需要列表的特定逻辑和数据集合。这描述了我们的Store,我们称之为ListStore。
Store是一个单体对象,意味着你可能不能通过“new”关键字来声明它,应用程序中每个Store里只有一个实例。
// Single object representing list data and logic var ListStore = { // Actual collection of model data items: [] }; 复制代码
然后,Store会响应已分派的操作:
var ListStore = … // Tell the dispatcher we want to listen for *any* // dispatched events AppDispatcher.register( function( payload ) { switch( payload.actionName ) { // Do we know how to handle this action? case 'new-item': // We get to mutate data! ListStore.items.push( payload.newItem ); break; } }); 复制代码
这是Flux处理调度回调的传统方式。每个有效负载(payload)包含一个action的名称(actionName)和数据(newItem),switch语句确定Store是否应该响应action,并且知道根据action的类型处理数据变化。
:key:关键点:store 不是数据模型 , 一个Store包含模型 。
:key:关键点:store在你的应用程序中唯一知道如何更新数据的东西, 它是Flux中最重要的部分 。我们调度的action并不知道如何添加或者删除项目。
举个栗子,假如应用程序中不同的部分需要保持跟踪某些图片及其元数据,那么你就需要创建其他的store,并将其命名为ImageStore。一个 store相当于应用程序中一个单独的“域(domain)” ,如果应用程序非常庞大,这些域可能对你来说已经很明显了。如果应用程序很小,你可能只需要一个store。一般来说,一种模型类型只对应一个Store。
只有store允许注册Dispatcher的回调!view永远不应该调用 AppDispatcher.register
。Dispatcher应该只用于将消息从视图View发送到Store。视图(view)会响应不同类型的事件。
3. Store触发“Change”事件
即将完成!现在数据确实已经变化了,我们需要告诉全世界! Store触发一个事件(Event),但是不会使用dispatcher。这虽然令人困惑,但是这就是Flux的方式。让我们给我们的Store加入触发事件的能力。如果你正在使用MicroEvent.js,那么很简单:
MicroEvent.mixin( ListStore ); 复制代码
然后,触发changes事件
case 'new-item': ListStore.items.push( payload.newItem ); // Tell the world we changed! ListStore.trigger( 'change' ); break; 复制代码
:key:关键点:当我们触发事件的时候,我们不会传递最新的项目。视图View只关心有事情发生变化了。让我们继续关注数据以了解原因。
4. 视图(View)响应“Change”事件
现在我们需要展示列表。当列表发生变化时, 视图会完全地重新渲染(re-render)。
首先,当组件“安装(mount)”时,即组件首次被创建的时候,从ListStore中监听change事件:
componentDidMount: function() { ListStore.bind( 'change', this.listChanged ); }, 复制代码
为简单起见,我们只调用forceUpdate,它可以触发重新渲染(re-render)
listChanged: function() { // Since the list changed, trigger a new render. this.forceUpdate(); }, 复制代码
当组件“卸载(unmount)”的时候,不要忘记清除事件监听器
componentWillUnmount: function() { ListStore.unbind( 'change', this.listChanged ); }, 复制代码
现在怎么办?让我们来看看我的render函数,我故意将其保存到最后。
render: function() { // Remember, ListStore is global! // There's no need to pass it around var items = ListStore.getAll(); // Build list items markup by looping // over the entire list var itemHtml = items.map( function( listItem ) { // "key" is important, should be a unique // identifier for each list item return <li key={ listItem.id }> { listItem.name } </li>; }); return <div> <ul> { itemHtml } </ul> <button onClick={ this.createNewItem }>New Item</button> </div>; } 复制代码
现在已经完整了。当你添加新项目的时,View 发出用户的 Action,Dispatcher 收到 Action,要求 Store 进行相应的更新,store改变数据,然后store会触发change事件,最后视图通过重新渲染页面来响应change事件。
译者注:原文无此图
但这里有一个问题:每次列表更改时我们都会重新渲染整个视图!这不是非常低效吗?
不。
当然,我们将再次调用render函数,并确保渲染函数中的所有代码都将重新运行。但是,如果渲染输出已更改,React将仅更新真实DOM。您的render函数实际上是生成一个“虚拟DOM”,React与之前的输出进行比较render。如果两个虚拟DOM不同,React将仅使用差异更新真实DOM.
:key:关键点:当Store的数据改变时,视图不应该关心是否有东西被添加,删除,或是被改变了。视图应该只去做重新渲染。React 的“虚拟DOM”差异算法会去处理这些重大问题,找出那些真正发生变化的DOM节点。这会让您的生活更加简单,并降低您的血压。
还有一件事:“Action Creator”到底是个啥?
记住,当我们点击按钮的时候,会分配一个具体的动作(action):
AppDispatcher.dispatch({ eventName: 'new-item', newItem: { name: 'Samantha' } }); 复制代码
好吧,如果您的许多视图需要发送此操作,则可以重复输入。此外,您的所有视图都需要知道特定的对象格式。那太蹩脚了。Flux建议一种抽象,称为 Action Creator
,它只是将上述内容抽象为一个函数。
ListActions = { add: function( item ) { AppDispatcher.dispatch({ eventName: 'new-item', newItem: item }); } }; 复制代码
现在您的视图可以调用 ListActions.add({ name: '...' });
,而不必担心调度的对象语法。
PS:不要使用forceUpdate
我因为习惯了 forceUpdate
这个简单的缘故。组件读取store数据的正确方法,是将数据拷贝到 state
,并且在 render
函数中读取 this.state
。您可以在 TodoMVC example 中看到它的工作原理。
首次加载组件时, store的数据被拷贝到 state
中 ,当store更新时候,数据被完整地重新拷贝。这样做是更好的,因为在内部, forceUpdate
是同步的,同时 setState
的效率也是非常高的。
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:- TiDB入门(四):从入门到“跑路”
- MyBatis从入门到精通(一):MyBatis入门
- MyBatis从入门到精通(一):MyBatis入门
- Docker入门(一)用hello world入门docker
- 赵童鞋带你入门PHP(六) ThinkPHP框架入门
- 初学者入门 Golang 的学习型项目,go入门项目
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
The Creative Curve
Allen Gannett / Knopf Doubleday Publishing Group / 2018-6-12
Big data entrepreneur Allen Gannett overturns the mythology around creative genius, and reveals the science and secrets behind achieving breakout commercial success in any field. We have been s......一起来看看 《The Creative Curve》 这本书的介绍吧!