什么是模块化架构?让我们通过一个例子来了解它不是什么,并且
我们将努力改变它。到最后你可能会相信它
优点或这是对时间的巨大浪费。
这是我在工作中经历的一个成长心态的真实场景。姓名和详细信息
虽然是匿名的,但一个常见概念的现实世界示例应该很有趣
如果没有别的的话,通过。
我们的网站有一个位于网站标题中的按钮。它显示有多少
用户已经离开了 V-Bucks,但也融入了一些业务逻辑:
等等。我们的产品经理和项目经理这样的例子还有很多
设计经理和 V-Bucks 集团董事梦想着我们需要
手柄。
实习生 Jimbo 的任务是实现这个,因为这只是一个
按钮!
他筛选了 Figma 设计的 15 种相互冲突的迭代。他发现
有多少个 PM,就在多少个单独的 Word 文档中包含这些要求。他组织并
与七个团队一起进行七次知识转移会议,以揭开
古老的专有知识知道哪些服务将提供他所需的数据
用于用户类型和 V-Bucks 数量。内容团队向他保证
所有字符串的最终版本将在年底获得法律和营销部门的批准
一周,这样,他就准备好构建这个按钮了。
这是他的 V-Bucks 按钮、弹出窗口和相关内容的第一次迭代
业务逻辑。
Jimbo 对他提出的简单目录结构感到满意:
/v-bucks-button ├── button.tsx ├── index.ts └── /v-bucks-popover │ ├── popover.tsx
所以他开始构建这个按钮,而且一开始很天真。
export const VBucksButton: React.FC<VBBProps> = ({ ... }) => { // Set up state const authConfig = { ... } const { user } = useAuth({ ...authConfig }) const { vBucks } = useGetVBucks({ user }) const { telemetry } = useTelemetry() const { t } = useTranslation('vBucksButton.content') const styles = useButtonStyles() // Derive some state via business logic const handleClick = () => { ... } const buttonText = vBucks === ERROR ? '--' : vBucks.toString(); // About 25 more lines of various button state, error handling, // conditional rendering, with comments throughout explaining // why we're showing or hiding something or other const popoverProps = { // About 200 lines of typical popover handling, // telemetry, business logic, content to display, etc } const tooltipProps = { // Another 100 lines of the same for tooltip } return ( <VBucksPopover {...popoverProps} trigger={ <Tooltip {...tooltipProps}> <button ariaLabel={t('ariaLabel')} className={` about seven-hundred classnames for responsive design, accessibility, conditional premium styles, et cetera`} onClick={handleClick}> {buttonText} </button> </Tooltip> } /> ) }
他已经实现了第一次尝试。 VBucksPopover 也有类似的复杂
业务逻辑、错误处理、状态管理、样式和注释
以运输名义的技术债务。
这个按钮只有不到 400 行,非常简单。即使弹出窗口是
另外500行意大利面条。真的是“清理”还是分裂
以任何方式使我们或我们的用户受益?这取决于。如果这就是我们所需要的
这个按钮,谁在乎呢。让我们继续前进吧!
但是两个月过去了,另一个产品团队的PM和设计师爱上了
您的按钮并希望将其放在应用程序的标题中。他们有一个简单列表,没有
来自他们的压力,他们希望你适应一些改变,如果
您可以在当天结束前给出 LT 的预计到达时间,那就太好了,谢谢:
Jimbo 能否将所有这些新功能塞进同一个组件中?
是的。拆分或重构会给用户带来好处还是给你的经理留下深刻印象?
不。但是在这种复杂程度上,重构有一些强有力的论据:
清洁守则倡导者和其他了解足够知识的肛门类型的道德
定期在 Stack Overflow 上回答,甚至你的祖父母也会看一些东西
像这样:
这些都很棒,有助于为 Jimbo 的下一次尝试提供信息。
之后他就没有被画中画了
所有,而且实际上还得到了提前交付和分享的促销
这么多的会议和文件。
但他现在更聪明了,学会了一种很酷的方法来实践这些格言。看起来
像这样的东西:
/v-bucks-button ├── button.tsx ├── index.ts └── /v-bucks-popover │ ├── popover.tsx
看起来像大量按钮和弹出框的样板。为什么会这样
更好吗?
这取决于。以下是 Jimbo 的简要概述及其基本原理:
它无限可扩展!这些构建块不会被
分解
任意规则,例如代码行或“复杂性”。它们被分解为
目的:每个概念边界都有一个目的。
PM 要你制作 10 个新的爆米花?没问题——Jimbo 的架构可以
处理一下。
领导层希望在某些应用程序中获得更好的销售指标,但其他团队则不然
有资金建立遥测技术来支持这一点。伟大的!我们有
我们可以水平扩展以满足各种变化的遥测实用程序
要求。
彻底的重新设计意味着每个弹出窗口都需要显示不同的内容,
根据不同的条件。现在,通常要简单得多,因为所有
我们渲染的东西,以及我们用来渲染它的所有逻辑,都存在于明确定义的
中
块。它们不再混杂在一大堆冲突和逻辑中
链长 20 行。
这是此容器/渲染器模式的示例:
/v-bucks-button ├── button.tsx ├── index.ts └── /v-bucks-popover │ ├── popover.tsx
export const VBucksButton: React.FC<VBBProps> = ({ ... }) => { // Set up state const authConfig = { ... } const { user } = useAuth({ ...authConfig }) const { vBucks } = useGetVBucks({ user }) const { telemetry } = useTelemetry() const { t } = useTranslation('vBucksButton.content') const styles = useButtonStyles() // Derive some state via business logic const handleClick = () => { ... } const buttonText = vBucks === ERROR ? '--' : vBucks.toString(); // About 25 more lines of various button state, error handling, // conditional rendering, with comments throughout explaining // why we're showing or hiding something or other const popoverProps = { // About 200 lines of typical popover handling, // telemetry, business logic, content to display, etc } const tooltipProps = { // Another 100 lines of the same for tooltip } return ( <VBucksPopover {...popoverProps} trigger={ <Tooltip {...tooltipProps}> <button ariaLabel={t('ariaLabel')} className={` about seven-hundred classnames for responsive design, accessibility, conditional premium styles, et cetera`} onClick={handleClick}> {buttonText} </button> </Tooltip> } /> ) }
/vBucksButton ├── /hooks │ ├── index.ts │ └── useButtonState.hook.ts ├── /vBucksPopover │ ├── /app1Popover │ │ ├── /hooks │ │ │ ├── index.ts │ │ │ └── usePopoverState.hook.ts │ │ ├── ... │ ├── /app2Popover │ ├── index.ts │ ├── popover.renderer.tsx │ ├── popover.styles.ts │ ├── popover.tsx │ └── popover.types.ts ├── /utils │ ├── experimentation.util.ts │ ├── store.util.ts │ ├── telemetry.util.ts │ └── vBucks.businessLogic.util.ts ├── button.renderer.tsx ├── button.styles.ts ├── button.tsx ├── button.types.ts └── index.ts
旁白:TailwindCSS 文档明确建议不要使用 @apply 来提取这样的公共类。这导致包大小几乎为零差异,除了“你必须想出类名”之外没有其他差异。生产级 CSS 几乎总是有几十行那么长,乘以给定组件中需要样式化的元素数量。这种权衡在 90% 的情况下似乎都是值得的。
其余现有的和新的业务逻辑位于钩子和实用程序中!
这种新架构满足了狂热者的需求,并使事情更容易扩展或
删除或移动。
编写单元测试变得不那么痛苦,因为你已经有了明确的定义
边界。您的渲染器不再需要模拟十种不同的服务来
验证它是否在给定一些输入的情况下显示了一组闪光。你的钩子可以
单独测试它们是否符合您预期的业务逻辑。
你的整个状态层刚刚改变了吗?如果您的
中的代码那就太可惜了
钩子与使用它的代码紧密耦合,但现在它更简单
更改,您的渲染器仍然只是等待一些输入。
这种模块化架构添加了大量样板,最终可以提供
零利益。
如果您正在从事一个充满激情的项目或
,我实际上无法推荐它
优先考虑运输和提供价值。如果你有
的东西
似乎它的范围可能会随着时间的推移而扩大,或者您可能想要
在 POC 后进行彻底检修,有时可以减少技术债务。
您可以使用 Plop 等工具来生成此样板。
那么我从 Jimbo 的工作和模块化架构中真正学到了什么?
我们在学校和世界各地的 Well Ackshuallys 中学习的干净代码和缩写词
是一系列的一端。将功能性意大利面条代码组合在一起是另一回事
结束,并且通常效果很好,因为最终所有代码都是技术债。
最佳解决方案存在于某种量子态或这些末端的组合中,并且
我们选择的路径可能取决于:
以上是模块化 React 架构的详细内容。更多信息请关注PHP中文网其他相关文章!