React+Redux在模块/域之间共享操作



想象一下,您有一个包含2个模块的应用程序(以鸭子的方式拆分)
一个是急切加载的Notification模块,用于在某个事件成功或失败时显示通知。另一个是Calculation,它进行一些计算

- Notification
- components
- actions
- ...
- index.js
- Calculation
- components
- actions
- ...
- index.js

在许多体系结构文章中,他们建议您应该通过index.js文件导出每个模块的动作创建者,该文件作为模块的公共API。例如,如果我想公开我的Notification模块的success操作创建者,那么我会从该模块中的index.js文件导出它。现在,我的其他模块可以导入这些动作创建者。

我喜欢在您的模块中使用面向公众的API。我发现这种工作方式很麻烦的是,然后将模块紧密地耦合到redux库。因为如果我切换到一个新的Notification模块,那么这个模块也必须公开动作创建者,这与redux有关。

我的担忧有效吗?如果是,你能提出一个更好的(但仍然惯用的)解决方案吗?

Angular中,我将执行以下操作:我将从Notification模块中公开一个单例服务,该服务充当该模块的面向公众的API。如果任何其他模块(例如Calculation)需要使用Notification模块中的功能,则可以使用依赖注入来注入服务并调用notificationService.addNotification('message')。在那个单例服务中,我将在store上调用dispatch方法。

CCD_ 14模块不需要知道CCD_。我可以很容易地切换Notification模块,只要面向公共的单例服务仍然公开addNotification方法。通过反转依赖关系,我不需要更改使用Notification模块的每个模块。

谢谢你的建议!

使用connect函数怎么样?这样你的组件

  1. 可以是完全没有Redux的用户
  2. dispatch和其他类似的冗余人员将隐藏在connect后面

以下是示例

export const MyComponent = ({ alertState, notificationsArray, SetAlert, AddNotification }) => {
return <div>
Alert state: {alertState.toString()}
<button onClick={() => SetAlert(!alertState)}>Toggle alert</button>
<div>
Notifications: {notificationsArray.map(n => `${n}, `)}
<button onClick={() => AddNotification()}>Add notification</button>
</div>
</div>
}
export default connect(state => ({ alertState: state.alert.alertState, notificationsArray: state.notifications.notificationsArray }), {...Alerts.actionCreators, ...notification.actionsCreators})(MyComponent)

注意,在MyComponent内部没有dispatch。所以你可以通过在没有Redux的情况下使用MyComponent

// Another file
import { MyComponent } from './MyComponent.js'
export const App = () => {
return <MyComponent alertState={true} SetAlert={(alert) => console.log(alert)} notificationsArray={[ 'notification1', 'notification2' ]} AddNotification={() => {}} />
}

或者,如果你想使用它作为连接,做

// Some third file
import MyComponent from './MyComponent.js'  // Note, that now it is default import
export const AnotherComponent = () => {
return <MyComponent />

现在请注意,我不向MyComponent提供任何道具,它们将由connect提供。

您也可以将对connect的调用移到其他文件中。因此MyComponent将完全独立于Redux。

您也没有义务将MyCompoent完全连接到Redux。你可以部分连接

export default connect (state => ({ alertState: state.alert.alertState }), Alerts.actionCreators)(MyComponent)

现在您应该在调用MyComponent时提供notificationsAddNotification,因为它们不是从Redux获取的。

我认为惯用的Redux方式是让Calculation模块调度一个动作,让对该动作感兴趣的模块在其减速器中处理该动作。由于所有操作都传递给所有减速器,这使得操作分配器和操作使用者之间的耦合不那么紧密。在这种情况下,Calculation模块不需要关心哪些组件、有多少组件,或者是否有组件在监视该操作。(尽管在大多数情况下,我发现我确实创建了一个动作生产者和该动作的一个消费者——在大多数情况中只有一个消费者,即使它们松散耦合,我也会同时处理这两者。)

我认为在理论上可以创建一个Notification单例,您可以从Calculation模块调用它,这反过来会调度一个仅由Notification模块本身处理的操作。我不太熟悉Angular是如何工作的,但似乎如果您调用Notification公开的函数,就会在组件之间产生紧密耦合。如果您以后想将Notification组件换成另一个组件,您是否必须再次查看所有绑定?如果其他组件对Calculation的成功感兴趣呢?Calculation模块是否也必须调用从这些模块中的singleton公开的函数,从而引入更紧密的耦合?

与大多数事情一样,这两种方法似乎都有利弊。如果你接受Redux的做事方式,那么组件之间不那么紧密的耦合是"优点"之一,如果你决定放弃Redux,转而采用不同的方法,则会以不那么灵活为代价。

您可能会错误地思考/假设某些事情。

如果您从某种意义上思考,操作/减少程序等在这里的组织和编写方式使这些特定模块独立。这里的Notification是独立的,Calculation也是独立的。两者的代码都在各自的文件夹中。Calculation模块无需担心世界各地正在发生的事情。Calculations相关工作已完成,相关行动已调度或减速器已更新。现在,如果某个模块(例如Notification)想在Calculation成功时做一些事情,它可以监听自己区域内的成功调度
(请注意,我们不需要对"计算"模块进行任何更改,通知模块即可工作)。因此,两者都是解耦的。

What I find troublesome with that way of working is that you then very tightly couple the module to the redux library是的,这是绝对正确的,但当您使用某个特定框架创建项目时,情况并非如此您使用这些库提供的语法和功能,但这会使整个项目与该库紧密绑定,如果您更改库,则必须根据新库或指南重新编写大量代码(除非有一些智能编译器)。但是,这并不能使模块耦合(至少在redux中)

This means that when I want to replace my Notification module with another Notification module that doesn't use redux, I'll have to refactor my whole app to not use the dispatch function anymore to create a success是的,您必须这样做,因为底层库已经更改我不是angular方面的专家,但即使在你的angular项目中,如果你决定在Notification模块中使用其他东西,我相信你必须重写Calculation模块中或周围的很多东西才能解决问题。

我认为你所说的通常发生在if there are very big projects written badly which led to origin of micro-services like architecture。以电子商务网站为例。最初,身份验证、搜索、结账、支付(基本上是后端服务)等都是一起编写的,因此它们是紧密耦合的。后来,人们用它们创建了微服务,每个微服务都可以使用API进行通信。现在,每个服务和底层框架都可以在不影响其他API的情况下进行更改,但有标准API同样,在前端,你也可以有这样的东西,但这本质上意味着你有单独的项目,需要通信,而不是同一项目中的模块。但在Redux或Angular中也会出现同样的问题

编辑:在评论中讨论后更新了几点:

你能有微型前端吗

Yes, you can have micro-frontends,如Notifications inReactJs AngularJsand use some public methods such as [窗口中的and计算。postMessage `](https://developer.mozilla.org/en-US/docs/Web/API/Window/postMessage)或eventListeners,但会有它的优点和缺点

我发现的几篇文章:

  • 微服务体系结构的前端
  • 微前端——一种用于前端web开发的微服务方法
  • https://micro-frontends.org/

现在流行吗


我认为其中一些原因是:

  • 与后端相比,frontend is more of UI/UX and has to look consistent in look and feel wise. So, achieving that might be a bit of issue
  • 网络延迟是一个大问题,需要下载大量内容。使用2或3个框架意味着您必须下载额外的数据才能使该框架工作。例如React和Angular库等。如果你看到了,在减少下载量方面做了很多工作,下载量会随着框架数量的增加而增加
  • 大多数网站的页面不多。假设最多10-12个不同的页面,那么在一个框架中创建所有页面既简单又便宜。然而,如果这个项目变得很大,那么大公司就会分化。在一些非常大的项目中,a.domain.com处于reactJs,b.domain.com处于angular。但这种情况通常发生在项目规模大且彼此完全分离的情况下

所以,是的,你可以拥有它,但这在很大程度上取决于各种因素,包括但不限于资源、价格、可用性等

如果你想构建微型前端,你可以使用

  • window.postMessage
  • 事件监听器
  • 使用库和window.postMessage API将微应用程序隔离到IFrame中进行协调。IFrame共享其父窗口公开的API
  • 事件发射器(一个非常好的相同库是-https://github.com/chrisdavies/eev)
  • 使用html5存储并听取它们的更改(通常是在任何允许我们在dom/window中或周围播放的内容上,因为这将是API层,帮助我们在不同模块之间进行通信)

希望,我能为你澄清一下。回复任何疑问/困惑。

要将状态模块与Redux调度/操作范式解耦,您可以通过钩子公开"公共API":

例如,假设您的模块有一个操作:

// notifications/actions.js
const createNotification = (data) => ({
type: 'CREATE_NOTIFICATION',
data
});
// ...

在您的模块中,定义一个钩子,该钩子返回一个分派操作的函数:

// notifications/hooks.js
import { useDispatch } from 'react-redux';
import { createNotification } from './actions';
function useCreateNotification() {
const dispatch = useDispatch();
return (data) => dispatch(createNotification(data))
}
// ...

现在,您的组件不必知道分派/操作。只需导入并使用挂钩。

// components/MyComponent.js
import React from 'react';
import { useCreateNotification } from './notifications/hooks'
function MyComponent() {
const createNotification = useCreateNotification();
const handleClick = () => createNotification('foo');
return (
<button onClick={handleClick}>Create Notification</button>
);
}

如果您需要公共API来公开普通(非好用)函数,您可以通过一个高级函数来实现这一点取CCD_ 58并返回一组函数。为了这个例子,这些函数将被称为"端点"。

// endpoints.js
import * as actions from './actions';
const createEndpoints = (dispatch) => {
const createNotification = (data) => {
dispatch(actions.createNotification(data))
}
// ...
return {
createNotification,
// ...
}
}

通过分派调用高阶函数:

// store.js
import { createStore } from 'redux';
import rootReducer from './reducer';
import { createEndpoints } from './notifications/endpoints';
export const store = createStore(rootReducer, {});
export const { 
createNotification,
// ...
} = createEndpoints(store.dispatch);

现在,您的UI不必知道分派、操作或挂钩;只需这样调用普通函数:

// MyComponent.js
import { createNotification } from './store'
function MyComponent() {
const handleClick = () => createNotification('foo');
return (
<button onClick={handleClick}>Create Notification</button>
);
}

使用这种方法,您可以在很大程度上与redux实现解耦。您仍然需要使用redux"dispatch"来使用模块,但现在您在一个点(createEndpoints)上耦合,而不是在整个组件中耦合多个点。

最新更新