返回介绍

使用 Flux 所面临的问题和解决方案

发布于 2025-04-26 18:09:27 字数 3285 浏览 0 评论 0 收藏

如果你现在已经明白了什么是 Flux,并且想继续使用它处理复杂的应用程序状态,你将会面临下面一些问题。

- 冗长。JavaScript 开发者已经习惯于语义明确且易于使用的 API。但是,Facebook 的 dispatcher 和 store 并没有真正提供如上的 API,这导致即使做一个很简单的东西也要有很多重复而烦琐的人工检查和定义。

- 异步操作。在测试你的应用程序时,如果能同步改变存储状态(store state),最好同步改变。原因是同步应用程序的测试用例比较容易写,并且对 store 有更少的依赖。你想测试的是在不同存储状态下组件该如何渲染,如果 API 是同步改变存储状态的,在进行测试之前就比较容易准备存储状态的测试用例。

- 在多个 store 中处理同一个 action。Facebook 引入了一个在应用程序中可以引用其他 store 的 waitFor 方法。如果一个 action 被下发,并且许多 store 都对这个 action 有响应,那么 waitFor 方法允许你选择哪个 store 先处理该 action。

- 在 store 间共享 state。你可能预先不能精确地知道 state 在应用程序的什么地方,更不用说 state 如何在应用程序中流动的了。state 和 flow 会改变,即使你在初始规划方面是个行家,你仍然看不透未来的变化。使用官方 Flux 的一个痛点是 state 被分离到许多 store 中,但是其他 store 也需要这个 state。这最初是没有问题的,但是如果两个 store 相互依赖时就出现了问题,最终产生了依赖循环。

- 不变性。虽然不变性 [1] 是一个很难理解的概念,但是有一个更重要的问题需要回答:“你为什么要使用它?”。这个问题可以从两个方面来考虑。不变性是一个基本概念,任何时候一个对象或数组发生改变,对象或数组自身也要改变它的引用。这种机制保证了你代码中某一部分状态的更新不会影响到其他部分的 state。我个人的理解是,就像类型检查,这在大型项目中才会发挥更大的价值。另一方面是关于 ReactJS 和它的渲染的。当验证渲染是否应当在一个组件中发生时,你可以使用不变性做一个浅比较。React.addons.update 方法或者 immutability-js(https://github.com/facebook/immutable-js )类库会帮助你做这些。问题是它难用且难理解,为什么你还需要使用它?

要解决上述问题有许多选择,看看 React JS wiki(https://github.com/facebook/react/wiki/Comple-mentary-Tools )就了解了。它们的共同点是在语法上更富有表现力,并且与 Facebook 的 dispatcher 和 store 相比较,没有那么冗长。它们中的一些库在 dispatcher 外面进行了包装,叫作 action creators,专门用于处理异步操作。为了在多个 store 中处理相同的 action,一些库对 waitFor 方法有不同的实现。一些库利用混合器把多个 store 合并成一个。目前解决循环依赖问题的库不是很多,混合器是其中一种解决方案。一些库利用不可变性,通过更新方法或者只是纯粹克隆来改变 state。

现在,我们知道了 Flux 面临许多挑战,仍然需要持续发展,以达到一个被广泛接受的实现。现在,我们看一个我认为优雅地解决了以上所有问题的具体实现——Baobab。

初识 Baobab

虽然我好不容易才记住了这个库的名字,但是我必须承认它对于处理状态有一个非常有趣的解决方案。我们必须暂时忘记 dispatchers、actions 和 stores。一个 Baobab tree 是严格的应用程序状态,所有的一切均存在于一棵树(tree)中。让这棵树如此特别的是,任何的改变都会触发一个事件。让我们通过一些代码来探索这棵树是如何工作的,然后学习如何通过使用 ReactJS 和这个库来实现一个 Flux 架构。

首先来创建树:

var Baobab = require('baobab');
var stateTree = new Baobab({
  admin: {
    users: []
  },
  home: {
    news: []
  }
});

正如你所看到的,这棵树只是一个由嵌套的对象和数组组成的基本对象。它像一个有分支的树。我们可以使用 select 方法来获取相应节点。

var Baobab = require('baobab');
var stateTree = new Baobab({
  admin: {
    users: []
  },
  home: {
    news: []
  }
});
var adminCursor = stateTree.select('admin');
var admin = adminCursor.get(); // { users: [] }

adminCursor 是什么?一个游标(cursor)就是一个指向树中特定数据的指针,在这里是 admin 对象。在 cursor 上调用 get() 方法可以获取真实数据。游标除了可以改变指定的值外,最重要的是提供了监听事件的能力。举个例子:

var Baobab = require('baobab');
var stateTree = new Baobab({
  admin: {
    notifications: {
      list: []
    }
  },
  home: {
    feeds: []
  }
});

var adminCursor = stateTree.select('admin');
var notificaitonsCursor = adminCursor.select('notifications');
var feedsCursor = stateTree.select('home', 'feeds');

adminCursor.on('update', function(){
  console.log('I saw a change'); // 将会被触发


});

notificationsCursor.on('update', function(){
  console.log('I saw a change'); // 将会被触发


});

feedsCursor.on('update', function(){
  console.log('I saw a change'); // 不会被触发,因为这是另一个不同的分支


});

notificationsCursor.push('foo');

如你所见,update 事件在树中冒泡。这真的很强大。也许应用程序中的一部分只对 admin 对象的事件感兴趣,另一部分只对 notifications 数组的事件感兴趣。你可以缩小不同部分的事件响应范围来达到上面的目的。

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。