强劲的生产力,一文看懂 Redux

3,754 阅读13分钟
原文链接: mp.weixin.qq.com
edux是近期前端开发中最火的框架之一。然而,很多人不清楚它是什么,它有什么好处。

正如开发文档中描述的,Redux对于JavaScript应用而言是一个可预测状态的容器。换句话说,它是一个应用数据流框架,而不是传统的像underscore.js或者AngularJs那样的库或者框架。


Redux是由Dan Abramov在大概2015年的时候创建的。是受到Facebook的Flux框架和函数式编程语言Elm的启发。很快,Redux因其简单易学开始流行起来,体积小(只有2KB)还有丰富的开发资料。如果你想知道Redux内部原理,想深入挖掘这个框架库,建议查看Dan的免费课程。


Redux最主要是用作应用状态的管理。简言之,Redux用一个单独的常量状态树(对象)保存这一整个应用的状态,这个对象不能直接被改变。当一些数据变化了,一个新的对象就会被创建(使用actions和reducers)。我们将在下面详细介绍这些核心概念。


 1   它是如何不同于 MVC 和 Flux 的


了得到全面的认识,我们了解下传统的模型-视图-控制器(MVC)模式,大部分开发者都很熟悉。在MVC结构中,在数据(模型),表现层(视图)和逻辑(控制器)之间是清晰分离的。

这样有个问题,尤其是在大型应用中:数据流是双向的。就是说,在代码许多地方,一个数据的改变(用户的一个输入或者接口返回)就会影响这个应用的状态——比如,双向数据绑定。而这将难以维护和调试。

Flux和Redux很相似。主要的不同在于Flux有多个可以改变应用状态的store,它通过事件来广播这些变化。组件可以订阅这些事件来和当前的状态同步。Redux没有分发器dispatcher,但在flux中dispatcher被用来广播数据到注册的回调事件。另一个不同是Flux中有很多扩展是可用的,这也带来了一些混乱和矛盾。


使用Redux的好处

你可能会问,“为什么我需要使用Redux?” 问得好。这里列出了一些在你下个应用中使用Redux的好处:

  • 可预测的结果 总有一个准确的数据源,就是store, 对于如何将actions以及应用的其他部分和当前的状态同步可以做到绝不混乱。

  • 可维护性 具备可预测结果的性质和严格的组织结构让代码更容易维护。

  • 组织性 Redux对代码应该如何组织更严苛,这让代码更一致,对团队协作更容易。

  • 服务端渲染 这非常有用,尤其是对于初次渲染,有助于更好的用户体验和搜索引擎优化。仅仅是把服务端创建的store传递给客户端就行。

  • 开发者工具 开发者可以实时跟踪在应用中正在发生的一切,从actions到状态的改变。

  • 社区和生态 无论何时你学习使用任何库或者框架这都是一个巨大的加分项。有个支持Redux的社区使它更吸引人来使用。

  • 易测性 编写可测试代码的第一条准则是编写可以只做一件事并且独立的小函数。Redux的代码几乎全部都只是那样函数:小,纯粹,分离。


 2   函数式编程


如提到的,Redux是建立在函数式编程概念上的。理解这些概念对于理解Redux究竟是如何工作以及为什么那样工作是非常重要的。我们来回顾下函数式编程的基本概念:

  • 它能把函数视为第一类对象。

  • 它可以把函数当作参数。

  • 它可以使用函数、递归和数组来控制流程。

  • 它能使用纯粹的,递归,高阶,闭包以及匿名函数。

  • 它可以使用帮助函数,比如map、filter还有reduce。

  • 它可以将函数链式在一起。

  • 状态不会改变(也就是说它是永恒不变的)。

  • 代码的执行顺序不重要。


函数式编程让我们写出更干净更模块化的代码。通过编写更简洁的、作用域和逻辑分离的函数,可以使代码更容易测试,维护和调试。现在这些简短的函数成为可复用的代码,这样你可以少写代码,而更少的代码是好事。函数可以在不做修改的情况下复制粘贴在任何地方。函数在作用域上是分离的,那样就只执行一个任务,在一个APP中就可以少依赖其他模块,这样就减少了耦合性,这也是函数式编程的另一个好处。

查看图片
函数式编程例子比喻 

你将会看到除了其他东西之外,纯函数,匿名函数,闭包,高阶函数和链式调用经常应用于函数式JavaScript中。Redux最主要使用纯函数,所以理解它们是什么是非常重要的。

纯函数根据传参返回一个新的值。它们不修改存在的对象,相反,返回一个新值。这类函数不依赖称为state的东西,而且它们只返回一个而且是相同的结果,不管提供的参数是什么。正因如此,它们是可预测的。


因为纯函数不修改任何值,所以它们不会对作用域有任何影响,也不会有任何可察觉的副作用,这意味着开发者可以只关心纯函数返回的值。


 3   Redux 可用于何处


部分开发者会将Redux和React结合起来,但Redux可以和其他任何框架一起使用。例如,你可以将Redux和AngularJS, Vue.js, Polymer, Ember, Backbone.js 以及 Meteor搭配。

虽然,Redux加React仍然是最常用的组合。确保以一个正确的顺序来学习React:最好的入门资料是Pete Hunt的,这对于那些React才开始起步以及对前端生态不知所措的开发者来说是非常有用的。

JavaScript疲劳 是对前端开发者,无论是新人还是有经验的老人,一种合理的担忧,所以要以一个正确的顺序正确的方式来花时间学习React或者是Redux。

Redux很赞的原因之一是它的生态圈。可以找到很多文章,学习指南,中间件,工具还有示例代码。

对我而言,我使用 David Zukowski的 示例代码,因为它涵盖了一个需要用React,Redux和React-router建立javascript工程的所有东西。

劝告一句:当学习像React和Redux这样的新框架时尽量不要使用示例代码库和入门者锦囊。它会让你更困惑,因为你完全不懂它们是怎么一起工作的。首先了解它,其次是建一个非常简单的app,作为一个支持项目从理论上学习,然后再使用示例代码作为生产项目来节约时间。


 4   建立 Redux 部分


Redux概念听起来可能比较复杂或者说是神奇,但它是简单易懂的。记住这个库只有2KB大。Redux有三个基本部分:动作(actions),存储(store)和接头(reducers)。

查看图片
Redux数据流


我们讨论下每个部分做什么。


Actions

简单说来,动作就是事件。Actions传递来自这个应用(用户接口,内部事件比如API调用和表单提交)的数据给store。store只获取来自actions的信息。内部actions就是简单的具有一个type 属性(通常是常量)的JavaScript对象,这个对象描述了action的类型以及传递给store的负载信息。

{

  type: LOGIN_FORM_SUBMIT,

  payload: {username: ‘alex’, password: ‘123456’}

}

Actions通过action生成器创建。我知道这听起来显而易见。它们只是返回actions的函数。

function authUser(form) {

  return {

    type: LOGIN_FORM_SUBMIT,

    payload: form

  }

}

要在应用里任何地方调用actions,是很简单的。使用dispatch方法,像这样:

`dispatch(authUser(form));`

Reducers

我们已经讨论过在函数式JavaScript中reducer是什么。它基于数组reduce方法,接收一个回调(reducer)让你从多个值中获得单个值,整数和,或者一个一系列值的累积。在Redux中,reducer就是获得这个应用的当前状态和事件然后返回一个新的状态的函数(纯粹的)。理解接头是怎样工作的至关重要,因为它们完成大部分工作。这里是一个非常简单的reducer,通过获取当前状态和一个action作为参数,再返回下一个状态:

function handleAuth(state, action) {

  return _.assign({}, state, {

    auth: action.payload

  });

}

对于更多复杂的项目,使用Redux提供的combineReducers()实例是可行(必要、推荐)的。它把在这个应用中所有的reducer结合在一起成为单个索引reducer。每一个reducer负责它自己那部分应用的状态,这个状态参数和其他reducer的不一样。combineReducers()实例使文件结构更容易维护。


如果一个对象(状态)只改变一些值,Redux就创建一个新的对象,那些没有改变的值将会指向旧的对象而且新的值将会被创建。这对性能是极好的。为了让它更有效率你可以添加 Immutable.js。

const rootReducer = combineReducers({

  handleAuth: handleAuth,

  editProfile: editProfile,

  changePassword: changePassword

});

Store

Store对象保存应用的状态并提供一些帮助方法来存取状态,分发状态以及注册监听。全部状态由一个store来表示。任何action通过reducer返回一个新的状态对象。这就使得Redux非常简单以及可预测。

 import { createStore } from ‘redux’;

  let store = createStore(rootReducer);

  let authInfo = {username: ‘alex’, password: ‘123456’};

  store.dispatch(authUser(authInfo));

 5   开发者工具、代码时空旅行以及热重启

了使Redux更容易配合工作,尤其是和大型应用配合时,我推荐使用 Redux DevTools。它非常有用,随着时间展示状态的改变,实时的改变,动作,以及当前的状态。通过避免console.log当前的state和actions节约了你的时间精力。


查看图片
Redux DevTools


Redux较于Flux有一点轻微的不同就是时空旅行的实现。在Redux中,你可以回到上一个状态,甚至可以把你的状态从那一点放到不同的位置上。在Redux工作流中,Redux DevTools支持这些 “时空旅行”特征(把它们想成是针对状态state的git命令):

  • 重置: 重置store创建的状态

  • 恢复: 回退到上一次提交的状态

  • 清除: 清除所有你可能搞错来的已经改变了的有缺陷的actions

  • 提交: 使当前状态为初始状态


时空旅行的这个特征在生产上效率不高,而且仅仅是打算用在开发环境和调试中。DevTools也一样。


Redux让测试更容易,因为它使用函数式JavaScript作为基础,而小的独立函数容易测试。所以,如果你需要在你的状态树中改变一些东西,只导入一个负责那个状态的reducer接头就行了,然后再单独测它。


 6   建一个应用


我们来建一个非常简单的使用Redux和React的应用来结束这个入门指南。为了让每个人都容易跟上,我保留原生的JavaScript,尽量少用ECMAScript2015和2016。在这篇文章中我们以比较容易的登录逻辑开始。

这个例子不会使用任何真实的数据,因为这个应用的目的是来展示Redux是怎样管理一个非常简单的应用中的状态的。我们使用CodePen编辑。


1. React组件

我们需要一些React组件和数据。我们写一个简单的组件再渲染render到页面上。这个组件有个input字段和一个按钮(一个分成简单的登录表单)。接下来,我们添加表示状态的文本:

查看Redux介绍,由 Alex Bachuk 在 CodePen 上编写(http://codepen.io/abachuk/pen/Meaxgm/)。


2. 事件和动作

添加Redux模块到工程中,添加按钮的onClick 事件处理。一旦用户登录,我们就分发类型是LOGIN的action以及当前用户的值。在我们完成那个之前,我们必须创建一个store并传入一个reducer函数作为它的一个参数。到目前为止,这个reducer只是一个空的函数:

查看代码Redux介绍 – 步骤2. 事件和动作, 由 Alex Bachuk 在 CodePen 上编写的(http://codepen.io/abachuk/pen/yJYwYX/)。


3. Reducers

现在我们有正激活的action,reducer将获取这个action然后返回一个新的state。我们来处理LOGIN action返回一个已经登录的状态,并且添加一个LOGOUT 的action,便于我们后面使用它。auth reducer接收两个参数:

  1. 当前的state (有个默认值),

  2. action.

查看代码 Redux介绍 – 步骤3. Reducers ,由Alex Bachuk 在 CodePen 上编写(http://codepen.io/abachuk/pen/LZpaGX/)。

4. 显示当前状态

现在,我们已经有初始状态(在reducer中的默认值)以及React组件了,我们来看一下状态是什么样的。最好的实践就是将状态累加到子组件中。因为我们只有一个组件,所以我们将应用的状态作为一个属性传到auth组件。为了让所有的一起工作,我们必须用一个subscribe帮助方法注册store监听,通过将ReactDOM.render包装在一个函数中并传到 store.subscribe():

查看Pen Redux介绍 – 步骤4. 显示当前状态 由Alex Bachuk 在 CodePen上实现(http://codepen.io/abachuk/pen/NrGJRR/)。


5. 登录和退出

既然我们有了登录和退出action的处理方法,那我们来添加退出按钮以及分发LOGOUT的action。最后一步就是管理哪个按钮用来展示登录,哪个用来退出,我们通过将登录移到render方法的外面再渲染变量如下:


查看 Pen Redux介绍 – 步骤5. 登录/退出 由 Alex Bachuk 在 CodePen 上实现(http://codepen.io/abachuk/pen/dXYrpr/)。


 7   总结


Redux每天都在获得吸引。它被用在很多公司 (Uber, Khan Academy, Twitter)以及项目中(Apollo, WordPress’ Calypso),实际生产项目中很成功。

一些开发者可能会抱怨它有很多额外开销。在大部分情况下,要表现简单的像按钮点击的动作或者简单的UI变化需要更多的代码。或许简单的动作以及UI变化不必是Redux store的一部分也能在组件级别上得到维护。

对于你的应用或者框架来说,尽管Redux可能不是完美的解决方案,但我强烈建议检查一下,尤其是React应用。


✦ ✦ ✦ ✦ ✦ ✦ ✦ ✦


延伸阅读(点击标题)

基于React、Redux以及野狗的聊天室简单实现

文:Alex Bachuk 原文:http://www.zcfy.cc/article/an-introduction-to-redux-ndash-smashing-magazine-770.html

查看图片