1. 首页

Rematch: Redux 的重新设计

难道现在状态管理不是一个可以解决的问题吗?直观地说,开发人员似乎知道一个隐藏的事实:状态管理的使用似乎比需要的更困难。在本文中,我们将探讨一些你可能一直在问自己的问题:

  • 你是否需要一个用于状态管理的库?
  • Redux 的受欢迎程度是否值得我们去使用? 为什么或者为什么不值得?
  • 我们能否制定更好状态管理解决方案吗?如果能,要怎么做?

状态管理需要一个库吗

作为前端开发人员,不仅仅是布局,开发的真正艺术之一是知道如何管理存储状态。简而言之:状态管理是复杂的,但又并非那么复杂。

让我们看看使用React等基于组件的视图框架/库时的选项:

码农进阶题库,每天一道面试题 or Js小知识

1. Component State (组件状态)

存在于单个组件内部的状态。在React中,通过setState方法更新state

2. Relative State (关联状态)

从父级传递给子级的状态。在React中,将 props 作为属性传递给子组件。

3. Provided State (供给状态)

状态保存在根 provider (提供者) 组件中,并由 consumer (消费者) 在组件树的某个地方访问,而不考虑组件之间的层级关系。在 React 中,通过 context API 可以实现。

大多数的状态都是存在于视图中的,因为它是用来反映用户界面的。那么,对于反映底层数据和逻辑的其它状态,又属于谁呢?

将所有内容都放在视图中可能会导致关注点的分离:它将与javascript视图库联系在一起,使代码更难测试,而且可能最大的麻烦是:必须不断地思考和调整存储状态的位置。

状态管理由于设计变更而变得复杂,而且通常很难判断哪些组件需要哪些状态。最直接的选择是从根组件提供所有状态,如果真要这么做的话,那么选用下一种方式会更好。

4. External State (外部状态)

状态可以移出视图库。然后,库可以使用提供者/消费者模式连接以保持同步。

也许最流行的状态管理库是Redux。在过去的两年里,它变得越来越受欢迎。+ 每天一道面试题 or Js小知识 https://www.javascriptc.com/

那么为什么这么喜欢一个简单的库呢?

Redux 更具性能?答案是否定的。事实上,为了每一个必须处理的新动作(action),都会稍微慢一些。

Redux是否更简单?当然不是。

简单应当是纯javascript:比如 TJ Holowaychuk 在twitter上说

码农进阶题库,每天一道面试题 or Js小知识

那么为什么不是每个人都使用 global.state={}?

为什么使用 Redux

在表层之下,Redux 与 TJ 的根对象{}完全相同——只是包装在了一系列实用工具的管道(pipeline)中。

码农进阶题库,每天一道面试题 or Js小知识

在 Redux 中,不能直接修改状态。只有一种方法:派发(Dispatch)一个动作(Action)到管道中,管道会自动根据动作去更新状态。

沿着管道有两组侦听器:中间件(middleware)订阅(subscriptions)。 中间件是可以侦听传入的动作的函数,支持诸如“logger”,“devtools”或“syncWithServer”侦听器之类的工具。 订阅是用于广播这些状态更改的函数。

最后,合成器(Reducer)函数负责把状态变更拆分成更小、更模块化、更容易管理的代码块。

和使用一个全局对象相比,Redux 确实简化了开发过程。

将 Redux 视为一个带有更新前/更新后钩子的全局对象,以及能够以简单的方式合成新状态。

Redux 是不是太复杂了?

是的。有几个不可否认的迹象表明 API 需要改进,这些可以用下面的方程来总结

码农进阶题库,每天一道面试题 or Js小知识

time_saved来表示你开发自己的解决方案所花费的时间,time_invested相当于阅读文档,学习教程和研究不熟悉的概念所花费的时间。

Redux 是一个拥有陡峭学习曲线的小型库。虽然有不少开发者能够克服深入学习函数式编程的困难并从 Redux 获益良多,但是也有很多开发者望而却步,宁愿重新使用 jQuery。

使用jQuery你不需要理解“monad”是什么,你也不需要为了使用Redux去理解函数组合。

使用 jQuery 你不需要理解“comonad”是什么,你也不需要为了使用 Redux 去理解函数组合。

任何框架或者库的目的都应该是把复杂的事物抽象得更加简单。

重新设计Redux

我认为Redux值得重写,至少有以下 6 个方面可以改进得更友好。

1.初始化

让我们来看看一个基本的 Redux 初始化过程,如下图左边所示:

码农进阶题库,每天一道面试题 or Js小知识

许多开发人员在第一步后就在这里暂停,茫然地盯着深渊。 什么是 thunkcompose?一个函数能做到这些吗?

如果 Redux 是基于配置而不是函数组合的话,那么像右边那样的初始化过程明显看起来更加合理。

2. 简化 reducers

Redux 中的 reducers 可以通过一个转换,让我们远离已经习惯但不必要且冗长的 switch 语句。

码农进阶题库,每天一道面试题 or Js小知识

假设reduceraction类型匹配,那么我们可以对参数进行反转,这样每个reducer都是一个接受stateaction的纯函数。 也许更简单,我们可以标准化action并仅传入state和有效负载(payload)。

3.使用 Async/Await 代替 Thunks

thunk 通常用于在 Redux 中创建异步 action。 在许多方面,thunk 的工作方式看起来更像是一个聪明的黑客,而不是官方推荐的解决方案。 我们一步一步来看:

  1. 你派发一个action(dispatch an action),它实际上是一个函数而不是预期的对象。
  2. thunk 中间件检查每个动作,看看它是否是一个函数。
  3. 如果是,中间件调用该函数,并传入一些 store 的方法:dispatchgetState

怎么会这样?一个简单的 action 到底是作为一个动态类型的对象、一个函数,还是一个 Promise?这难道不是一种拙劣的实践吗?

码农进阶题库,每天一道面试题 or Js小知识

如上图右边所示,难道我们就不能只使用 async/await ?

4. 两种 action

仔细想想,其实有两种 action

1.reducer action: 触发 reducer 并改变状态。

2.effect action:触发异步 action,这可能会调用reducer操作,但异步函数不会直接更改任何状态。

将这两种类型的 action 区分开来,将比上面的thunk用法更有帮助,也更容易理解。

5. 不再有 action 类型(action.type)变量

为什么我们的标准实践要把 action creator 和 reducer 区分开来呢?能否只用其中一个呢?改变其中一个又是否会影响到另一个?

action creator 和 reducer 是同一枚硬币的两面。

const ACTION_ONE = ‘ACTION_ONE’是分离 action creators 和 reducers 的一个冗余产物。应将两者视为一体,并且不再需要文件导出类型的字符串。

6.reducers 即 action creators

按照使用方式,把 Redux 中所涉及的概念进行合并分组,那么我们可以得出下面这个更简单的模式。

码农进阶题库,每天一道面试题 or Js小知识

可以从 reducer 中自动确定 action creator。 毕竟,在这种情况下,reducer 可以成为action creator

使用一个基本的命名约定,下面是可预测的:

  1. 如果 reducer 命名为 increment,那么 type 就是 increment。更好的做法是加上命名空间 “count/increment”
  2. 每个 action 都通过 payload 键来传递数据。

码农进阶题库,每天一道面试题 or Js小知识

现在,从 count.increment 中,我们可以以一个 reducer 生成 action creator。

好消息:我们可以有一个更好的 Redux

以上这些痛点就是我们创建 Rematch 的原因。

码农进阶题库,每天一道面试题 or Js小知识

Rematch 对 Redux 进行了封装,提供更简单的 API,但又不失任何可配置性的特点

码农进阶题库,每天一道面试题 or Js小知识

请参见下面的一个完整的 Rematch 示例:

码农进阶题库,每天一道面试题 or Js小知识

在过去的几个月里,我一直在实际业务中使用 Rematch。作为证明,我会说:状态管理从未变得如此简单、高效。

Redux 与 Rematch 的对比

Redux 是一个出色的状态管理工具,有键全的中间件生态与出色的开发工具。

Rematch 在 Redux 的基础上构建并减少了样板代码和执行了一些最佳实践。

说得清楚点,Rematch 移除了 Redux 所需要的这些东西:

  • 声明 action 类型
  • action 创建函数
  • thunks
  • store 配置
  • mapDispatchToProps
  • sagas

让 Redux 与Rematch 作对比有助于让理解更加清晰。

Rematch

1.model


import { init } from '@rematch/core' const count = { state: 0, reducers: { upBy: (state, payload) => state + payload } } init({ model: { count } })

2.View


import { connect } from 'react-redux' // Component const mapStateToProps = (state) => ({ count: state.count }) const mapDispatchToProps = (dispatch) => ({ countUpBy: dispatch.count.upBy }) connect(mapStateToProps, mapDispatchToProps)(Component)

Redux (最佳实践)

1.store


import { createStore, combineReducers } from 'redux' // devtools, reducers, middleware, etc. export default createStore(reducers, initialState, enhancers)

2.Action Type


export const COUNT_UP_BY = 'COUNT_UP_BY'

3.Action Creator


import { COUNT_UP_BY } from '../types/counter' export const countUpBy = (value) => ({ type: COUNT_UP_BY, payload: value, })

4.Reducer


import { COUNT_UP_BY } from '../types/counter' const initialState = 0 export default (state = initialState, action) => { switch (action.type) { case COUNT_UP_BY: return state + action.payload default: return state } }

5.view


import { countUpBy } from '../actions/count' import { connect } from 'react-redux' // Component const mapStateToProps = (state) => ({ count: state.count, }) connect(mapStateToProps, { countUpBy })(Component)

Rudex 与 Rematch 的分数板

码农进阶题库,每天一道面试题 or Js小知识

Redux 并没有被抛弃,而且也不应该被抛弃。

只是,我们应该以更低的学习成本,更少的样板代码和更少的认知成本,来拥抱 Redux 背后的简单哲学。

你的点赞是我持续分享好东西的动力,欢迎点赞!

欢迎加入前端大家庭,里面会经常分享一些技术资源。

码农进阶题库,每天一道面试题 or Js小知识

作者:前端小智
链接:https://segmentfault.com/a/1190000019056045

看完两件小事

如果你觉得这篇文章对你挺有启发,我想请你帮我两个小忙:

  1. 关注我们的 GitHub 博客,让我们成为长期关系
  2. 把这篇文章分享给你的朋友 / 交流群,让更多的人看到,一起进步,一起成长!
  3. 关注公众号 「画漫画的程序员」,公众号后台回复「资源」 免费领取我精心整理的前端进阶资源教程

JS中文网是中国领先的新一代开发者社区和专业的技术媒体,一个帮助开发者成长的社区,目前已经覆盖和服务了超过 300 万开发者,你每天都可以在这里找到技术世界的头条内容。欢迎热爱技术的你一起加入交流与学习,JS中文网的使命是帮助开发者用代码改变世界

本文著作权归作者所有,如若转载,请注明出处

转载请注明:文章转载自「 Js中文网 · 前端进阶资源教程 」https://www.javascriptc.com

标题:Rematch: Redux 的重新设计

链接:https://www.javascriptc.com/3375.html

« 面试回答问题的技巧
面试官到底想看什么样的简历?»
Flutter 中文教程资源

相关推荐

QR code