我如何才能让我的故事书项目达到最高效率
结论
我们正处于“组件化”时代,任何用户界面都被拆分成可重用的小模块,然后组装成页面。无论是使用 React、Angular、Vue.js、Svelte 还是其他任何框架,我们每天都在编写组件,因此我们需要一个工具来以尽可能原子化的方式隔离和构建它们。这就是Storybook。
注意:本文并非 Storybook 的教程。如果您之前从未使用过 Storybook,项目核心团队制作了一个相当不错的教程,可以帮助您了解 Storybook 是什么以及如何使用它。
Storybook 是一个很棒的工作空间:它简单易用,插件丰富,而且你可以自由地构建 Storybook 项目。但你的故事很容易变得杂乱无章,最终效果也不如预期。
我使用 Storybook 已经快三年了,从那时起我就很喜欢这个强大的工具,但它有时也会让我的项目变得混乱。这并非 Storybook 的问题,而是我自己的问题!我发现,如果没有结构和组织,Storybook 的使用体验可能不如预期。
多年来,我制定了一些规则,让我的 Storybook 项目运行得更清晰,从而能够更高效地维护和调试项目。因此,在本文中,我将与大家分享我总结出的关于 Storybook 项目的一些最佳实践。
本文中包含一些 React 组件的代码片段,但我的观点并非仅限于 React,也适用于 Storybook 以及任何模板库。
谁会用你的故事书?
根据 Storybook 项目的使用者不同,构建方式也会有所不同。当然,你的开发团队会参与其中,他们需要 Storybook 作为工作区。
但如果您编写一些开源项目或构建一个供第三方团队使用的设计系统,外部开发人员可能需要访问您的故事。在这种情况下,您的 Storybook 应该成为一份详尽的文档,解释组件的 API。
一些设计师可能也会感兴趣!对他们来说,Storybook 就像一个展示平台。Stories 就像是他们设计系统的清单,列出了哪些部分已经可以实现。
有些插件允许您将故事下载为 Sketch 文件,这使得故事成为前端开发人员和 UI 设计师共享的公共资源。
产品负责人也可以参与 Storybook 的开发。对他们来说,Storybook 是一个展示技术栈的工具。你的老板可以从沟通的角度,通过展示你的 Storybook 来了解你的工作成果,从而受益匪浅。
所以,根据故事的读者群体不同,他们期望你的 Storybook 项目提供特定类型的信息,你在维护代码时必须牢记这一点。这将迫使你专注于某些内容,避免在与项目无关的事情上浪费时间。
你即将读到的一些要点对你的故事读者来说可能并非必要。但别忘了,首先需要这些故事的人是未来的自己,所以尽量把事情讲清楚,对“未来的自己”好一点。
让你的故事更加清晰明了。
首先,尽量用最简单的方式实现。随着项目规模的扩大,组件越来越多,你经常需要回顾一下某个组件的使用方法。Storybook 非常适合这种情况,但你需要注意一些事项:
用最简单的方式写出你的故事片段
您可以stories.js在 Storybook 中编写代码来显示组件,但它本身也可以成为有关如何实现该组件的文档。
代码必须清晰易懂。你的目标是让用户能够毫不费力地理解如何使用你的组件。
为此,尽量保持代码静态:避免抽象,必要时进行重复,避免使用任何算法。代码越简洁,就越能直击要点。
// This story source is written with the new "Component Story Format" from Storybook 5.2
import React from 'react'
import ActorList from './index';
import ActorListItem from './item';
export default {
'title': 'ActorList'
}
// ❌ Bad
export const badStory = () => {
const actors = [{
name: 'Jonathan Groff',
role: 'Holden Ford',
isDetective: true,
}, {
name: 'Holt McCallany',
role: 'Bill Tench',
isDetective: true,
}, {
name: 'Cameron Britton',
role: 'Edmund Kemper',
isDetective: false,
}]
return (
<ActorList length={actors.length}>
{actors.map(actor => (
<ActorListItem key={actor.name} {...actor}/>
))}
</ActorList>
)
}
// ✅ Good
export const goodStory = () => (
<ActorList length={3}>
<ActorListItem name="Jonathan Groff" role="Holden Ford" isDetective />
<ActorListItem name="Holt McCallany" role="Bill Tench" isDetective />
<ActorListItem name="Cameron Britton" role="Edmund Kemper" />
</ActorList>
)
在上面的例子中,它badStory有自己的逻辑,但这与我们想要展示的内容无关。当然,创建一个循环会感觉更自然,这也是我们<ActorListItem>在实际应用中实现的方式。但这会对我们想要展示的内容——“如何使用此组件列出参与者”——进行不必要的抽象。而这个示例goodStory清晰明了、简单易读,是完美的文档。
新版5.2将Component Story FormatStorybook API 接口精简到了极致!编写故事就像编写普通组件一样简单,而且源代码比以前更容易阅读!
不要用很多参数来制作一个故事,而是用很少的参数来制作很多故事。
Storybook-knobs是个很实用的插件!它“允许你使用 Storybook UI 动态编辑属性”。这很棒,但当你想要展示一个有很多属性的组件时,某些渲染需要用户以特定组合设置属性。一些特殊情况下的渲染会在你的故事中“隐藏”起来,因为它们的存在并不明显。
为了避免这种情况,请将组件创建为故事,每个故事代表组件 API 的一个切片。故事的数量应与组件的功能数量相同。由于所有故事都会列在左侧,因此可以清晰地展示所有已涵盖的内容。
例如,对于一个<Button>包含一个元素theme和一个size道具的场景,你可以在Button故事套件中编写两个不同的故事。这样,任何阅读故事套件的人都能很快明白元素theme和size道具是如何影响渲染的,因为所有元素都被混杂在一起了。
当然,你通常会使用带有多个 props 值的组件。因此,你需要展示各种 props 组合的效果。为此,你可以"playground"为每个 props 组合创建一个示例。这样,开发人员和设计师就可以尝试组件结合任何 props 值所能实现的各种可能性。
import { storiesOf } from '@storybook/react';
import { withKnobs, text, boolean, number } from '@storybook/addon-knobs';
import Button from './index';
const stories = storiesOf('Button', module);
// ❌ Bad
stories.add('default', () => {
const themes = ['default', 'primary', 'success', 'danger', 'warning'];
const sizes = ['sm', 'md', 'lg'];
return (
<Button
theme={select('theme', themes)}
size={select('size', sizes)}
>
Button
</Button>
);
});
// ✅ Good
const themes = ['default', 'primary', 'success', 'danger', 'warning'];
const sizes = ['sm', 'md', 'lg'];
stories.add('default', () => {
return (
<Button>default button</Button>
);
});
stories.add('theme', () => {
const theme = select('theme', themes);
return (
<Button theme={theme}>{theme} button</Button>
);
});
stories.add('size', () => {
const size = select('size', sizes);
return (
<Button size={size}>{size} button</Button>
);
});
stories.add('playground', () => {
const theme = select('theme', themes);
const size = select('size', sizes);
const children = text('children', 'hello world !')
return (
<Button theme={theme} size={size}>{children}</Button>
);
});
上面的例子中这看起来可能有点傻,但随着组件的扩展,它的意义就显现出来了。正如你所看到的,这并不意味着你必须放弃旋钮插件,只是不要过度依赖它。通过添加多个故事,你可以突出组件的每个部分,并通过让所有信息都易于获取和组件行为更可预测来提升用户体验。
你可能会在片段中看到一个“默认”故事。我们稍后会在这篇文章中讨论它 ;)
像设计代码库一样设计你的故事
前端开发人员最常写的词之一就是import……使用基于组件的库时,我们尝试创建小型组件,然后将它们导入到更大的组件中,这些更大的组件又会被导入到更大的组件中,如此循环往复……你懂的。
因此,如果 Storybook 的左侧栏可以帮助您找到组件展示的位置,那将是一个不错的额外功能。
假设你这样设计组件架构:
/src
| /components
| <Button>
| /form
| <Input>
| <Checkbox>
| /container
| <SignUpForm>
| /view
| <SignUpPage>
你的故事标题应该写成:
Components|ButtonComponents|Form/InputComponents|Form/CheckboxContainer|SignUpFormView|SignUpPage
这样,Storybook 页面上的导航栏就会指示任何组件的位置,从而节省宝贵的时间。
使用 DocsPage 增强您的文档功能
DocsPage 是 Storybook 最新更新中的一项全新功能。它可以帮助您根据故事和组件定义创建美观的文档。
例如,您可以显示一个表格,列出组件的所有属性,并提供诸如预期类型或默认值等实用信息。您还可以轻松添加其他信息、显示代码片段,未来甚至可能添加更多功能。
| import { Meta, Story, Props } from '@storybook/addon-docs/blocks'; | |
| import { Badge } from './Badge'; | |
| <Meta title="Demo/Badge" component={Badge} /> | |
| # Badge | |
| With `MDX` we write longform markdown documentation for our `Badge` component and embed Doc Blocks inline. | |
| <Props of={Badge} /> | |
| <Story name="positive"> | |
| <Badge status="positive">Positive</Badge> | |
| </Story> |
以上内容摘自 Storybook 发布关于 DocsPage 功能的帖子。
如果你的组件是公开的,这是分享使用方法的绝佳方式。或者,如果组件比较特殊/复杂,你可以重点介绍一些特定的 API。
但对于私有项目中的简单组件来说,这可能有点过于复杂。所以,请知悉 DocsPage 可用,并根据需要使用它。
把你的故事书设计成你的“组件库”。
现在你的 Storybook 项目文档更完善了,但请记住,Storybook 也必须是一个实用的工具,用于创建、增强和修复你的组件。为此,以下是一些建议,可以帮助你更好地使用组件系统:
"default"为每个组件编写一个故事
在所有面向组件的库中,都有 props 的概念,它们是影响组件渲染和行为的选项。有些 props 是必需的,有些则是可选的。
因此,为了了解组件的“纯净版”是什么样子,最好为每个组件编写一个"default"故事,展示它只包含必需属性时的样子。
至于所需的道具,应该尽可能选择最简单的,以保持这种淡淡的香草味。
这将使组件的实现更具可预测性,因为您知道它最简单的实现方式应该是什么样子。此外,这个"default"故事可以作为与其他所有故事的对比点,从而突出每个属性的作用(正如我们之前讨论过的)。
最后,"default"当您调试或增强组件时,这个故事将非常有用,因为您可以检查组件的默认用法是否发生变化,从而控制潜在的回归问题。
使用操作插件
大多数 props 会影响组件的渲染,但有些是“事件处理程序”。这类 prop 需要一个函数作为值,当组件中发生特定事件时,组件会执行该函数。
在你的故事中填充这些属性不会在视觉上改变任何事情,但它们仍然至关重要,因为它们是用户界面和业务逻辑之间的联系。
因此,每个事件处理程序属性都应该填充action来自 `.` 的函数actions addons。这样,你的组件触发的每个事件都会被记录下来,并包含传递给事件的参数值。
import { storiesOf } from '@storybook/react';
import { action } from '@storybook/addon-actions';
import Button from './index';
const stories = storiesOf('Button', module);
stories.add('onClick', () => {
return (
<Button onClick={action('onClick')}>default button</Button>
);
});
如您所见,控制组件向其父组件提供的信息,并改进/调试此输出,虽然很容易,但却非常重要。
无论何时,为您的所有组件创建故事
一方面,Storybook 似乎只适用于“哑组件”或只关心 UI 而不涉及任何逻辑业务的组件。
但实际上,它适用于任何类型的组件,甚至包括像 Redux 中的“容器”这样的智能组件。它可能需要一些更复杂的设置,例如自定义装饰器,但它仍然非常有用,因为每个组件都应该有自己的故事。
隔离智能组件可以帮助您优化数据工作流程,因为您的业务逻辑将被简化到最轻量级的形式,您可以专注于与您的组件相关的内容。
如果您对我在 Storybook 中如何设置 Redux 以及如何在故事中维护容器感兴趣,请在评论区告诉我。
结论
随着项目和团队的壮大,你的 Storybook 中会包含越来越多的组件。有些组件你几乎每天都会用到,而有些则很少用到,因此你需要定期查看这些组件的功能或使用方法。
Storybook 也非常适合向设计师或产品负责人展示已完成的工作。维护良好的 Storybook 能让他们快速了解哪些内容已完成或缺失,从而使所有人的工作内容更易于预测,也更便于改进。
所以,你的故事一定要谨慎对待!
这篇文章主要介绍我为了让 Storybook 发挥最大效用而遵循的一些规则。但我知道大家也一定有很多关于 Storybook 项目管理的宝贵建议,欢迎留言分享!
文章来源:https://dev.to/loicgoyet/how-i-manage-to-make-my-storybook-project-the-most-efficient-possible-2d8o



