【译】Flux入门
栏目: JavaScript · 发布时间: 6年前
内容简介:原文地址: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入门项目
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Head First PHP & MySQL(中文版)
Lynn Beighley、Michael Morrison / 苏金国、徐阳 / 中国电力 / 2010-6 / 98.00元
通过《深入浅出PHP&MySQL(影印版)》,你将学习:准备好把你的静态HTML网页提升到下一个层次并使用PHP和MySQL建立数据库驱动的网站了吗?《深入浅出PHP& MysQL》是一本快捷实用的指南,让你的动态网站快速运行。自己动手建立实际应用程序,从视频游戏高分留言板到在线交友网站。当你完成后,你将可以进行验证表单、使用会话ID和cookies工作、执行数据库查询和联接、处理文件I/0操作等......一起来看看 《Head First PHP & MySQL(中文版)》 这本书的介绍吧!