我最近刚刚发现了Redux。它看起来很不错。使用Redux相比Flux有什么缺点、需要注意的地方或妥协吗?谢谢。
我是Redux的作者!
我要说的是,你在使用Redux时需要做出以下妥协:
你需要学会避免更改数据。 Flux对更改数据没有意见,但Redux不喜欢更改数据,许多与Redux配套的包也假定您从不更改状态。您可以使用仅限于开发环境的包(例如redux-immutable-state-invariant),使用Immutable.js,或信任自己和团队编写非变异代码,但这是您需要了解的事情,并且这需要成为您团队接受的有意识的决策。
你将不得不仔细选择你的包。尽管Flux明确不会试图解决“附近”的问题,例如撤消/重做,持久性或表格,但Redux具有中间件和存储增强器等扩展点,并且它已经产生了一个年轻但丰富的生态系统。这意味着大多数软件包都是新想法,尚未获得足够使用量的关键性。您可能会依赖某些东西,几个月后明显是个坏主意,但现在很难判断。
你暂时还没有完美的Flow集成。 Flux目前允许您进行非常印象深刻的静态类型检查,Redux目前还不支持。我们会到那里的,但需要一些时间。
我认为对于初学者来说,第一个是最大的障碍,对于过于热衷于早期采用的人来说,第二个可能会成为问题,第三个则是我个人的小怨恨。除此之外,我认为使用Redux没有任何特定的缺点,Flux也避免不了它,有些人甚至认为它相比Flux还有一些优势。
另请参阅我在使用Redux的优势上的回答。
无论是Redux还是Flux,在涵盖许多常见模式(特别是涉及异步数据获取的模式)时,都需要大量的样板代码。Redux文档已经有了几个示例来减少样板代码:http://redux.js.org/docs/recipes/ReducingBoilerplate.html。你可以从像Alt或Fluxxor这样的Flux库中获得你可能需要的一切,但Redux更喜欢自由而不是功能。这可能对某些开发人员来说是一个缺点,因为Redux对你的状态做出了某些假设,可能会被无意忽略。
你真正回答你的问题的唯一方法是尝试Redux,如果可能的话,可以在个人项目中尝试。Redux的出现是因为需要更好的开发者体验,并且它偏向于函数式编程。如果你不熟悉诸如reducer和函数组合之类的功能概念,那么你可能会变慢,但只会稍微慢一些。在数据流中采用这些思想的优点是更容易进行测试和可预测性。
免责声明:我从Flummox(一种流行的Flux实现)迁移到了Redux,好处远远超过了任何缺点。我更喜欢我的代码中有更少的魔法。较少的魔法需要付出一点更多的样板代码的代价,但这是一个非常小的代价。
Redux 不是一个纯粹的 Flux 实现,但绝对受到了 Flux 的启发。最大的区别在于它使用一个单一的存储器,该存储器包装一个状态对象,其中包含您应用程序的所有状态。与您在 Flux 中创建存储器不同,在 Redux 中,您将编写减速器函数,这些函数将更改单个对象状态。此对象表示您应用程序中的所有状态。在 Redux 中,您将获得当前操作和状态,并返回新状态。这意味着操作是顺序的,状态是不可变的。这使我认为 Redux 最明显的缺点。
这里有几个原因:
1. 一致性 - 存储的状态总是被reducer更改,因此很容易跟踪谁更改了什么。
2. 性能 - 因为它是不可变的,Redux只需要检查前一个状态!==当前状态,如果是,则进行渲染。无需每次循环状态来确定渲染。
3. 调试 - 新的惊人概念,如时间旅行调试和热重载。
更新:如果这还不够说服你,请观看Lee Byron关于不可变用户界面的精彩演讲。
Redux需要开发者在代码库/库中保持这个理念的纪律性。您需要确保选择库并以不可变的方式编写代码。 如果您想了解Flux概念的不同实现(以及哪种最适合您的需求),请查看this 有用的比较。 话虽如此,我必须承认,就JS未来的发展而言,Redux是一个方向(当我写下这些话时)。我更喜欢使用Redux,因为它仅使用一个存储,相比于Flux,使状态管理更加容易。此外,Redux DevTools是一个非常有用的工具,可以让您查看一些有用的数据以及与React开发工具一起使用。
另外,Redux在与其他流行框架(如Angular)一起使用时更加灵活。无论如何,让我们看看Redux如何介绍自己。
三个原则
你整个应用程序的状态都存储在单个存储中的对象树中。
这使得创建通用应用程序变得容易,因为您的服务器的状态可以被序列化并转换为客户端,而不需要额外的编码工作。单个状态树还使得调试或检查应用程序变得更加容易;它也使您能够在开发中持久化应用程序状态,以获得更快的开发周期。如果所有状态都存储在单个树中,则某些功能传统上很难实现,例如撤销/重做,可能会突然变得容易。
console.log(store.getState())
/* Prints
{
visibilityFilter: 'SHOW_ALL',
todos: [
{
text: 'Consider using Redux',
completed: true,
},
{
text: 'Keep all state in a single tree',
completed: false
}
]
}
*/
状态是只读的
改变状态的唯一方法是发出一个操作,即描述发生了什么的对象。
这确保视图和网络回调都不会直接写入状态。相反,它们表达了转换状态的意图。由于所有更改都是集中的,并且按照严格的顺序逐个发生,因此没有微妙的竞争条件需要注意。由于操作只是普通对象,因此可以记录、序列化、存储并在以后用于调试或测试目的重播。
store.dispatch({
type: 'COMPLETE_TODO',
index: 1
})
store.dispatch({
type: 'SET_VISIBILITY_FILTER',
filter: 'SHOW_COMPLETED'
})
使用纯函数进行更改
为了指定如何通过操作转换状态树,您需要编写纯函数。
Reducer仅仅是纯函数,它接受先前的状态和一个动作并返回下一个状态。请记住始终返回新的状态对象,而不是改变先前的状态。您可以从一个单一的Reducer开始,随着应用程序的增长,将其拆分成管理状态树特定部分的较小的Reducer。因为Reducer只是函数,所以您可以控制它们被调用的顺序、传递额外的数据,甚至创建可重复使用的Reducer来处理诸如分页等常见任务。
function visibilityFilter(state = 'SHOW_ALL', action) {
switch (action.type) {
case 'SET_VISIBILITY_FILTER':
return action.filter
default:
return state
}
}
function todos(state = [], action) {
switch (action.type) {
case 'ADD_TODO':
return [
...state,
{
text: action.text,
completed: false
}
]
case 'COMPLETE_TODO':
return state.map((todo, index) => {
if (index === action.index) {
return Object.assign({}, todo, {
completed: true
})
}
return todo
})
default:
return state
}
}
import { combineReducers, createStore } from 'redux'
let reducer = combineReducers({ visibilityFilter, todos })
let store = createStore(reducer)
欲了解更多信息,请点击这里
Redux对于不可变性需要有严格的纪律。我可以建议使用ng-freeze来让你发现任何意外的状态突变。
shallowEqual
检查来确定状态是否更改。但它可以被替换为deepEqual或JSON.stringify和比较。 最终它有点低性能 - 但它是纯计算而不涉及DOM - 所以足够快。 无论如何,渲染本身都是相同的。 - amakhrov