精简 Styled-components

Avatar of Allie Stehney
Allie Stehney

DigitalOcean 为您旅程的每个阶段提供云产品。立即开始使用 200 美元的免费额度!

我喜欢使用 styled-components。它们允许你在 JavaScript 中编写 CSS,使你的 CSS 非常接近单个组件的 JavaScript。作为一个喜欢剖析网页并将其分解成可重用组件的前端开发者,styled-components 的理念让我感到快乐。这种方法简洁且模块化,我不必在某个巨大的 CSS 文件中搜索我需要的类是否已经存在。我也不必将类添加到那个永无止境的 CSS 文件中,并且为使那个已经巨大的文件变得更大而感到内疚。

然而,当我继续使用 styled-components 的道路时,我开始意识到,虽然将 CSS 样式分离出来使其特定于单个组件是很棒的,但我开始为了基于每个组件组织我的样式而重复自己很多。我一直在为每个组件创建新的 CSS 声明,因此,创建新的 styled-components,并且在我的 CSS 中看到了大量的重复。不,styled-components 并不总是完全相同,但其中三个 CSS 行中的两行会与另一个组件中三个 CSS 行中的两行匹配。我真的需要在我的每个需要这些样式的地方都重复这段代码吗?

flexbox 为例。Flexbox 非常适合响应式布局。它会以某种方式对齐项目,并且只需进行最少的更改,就可以调整以在不同的屏幕尺寸上看起来不错。因此,我发现自己经常编写以下代码:

display: flex;
flex-direction: row; /* the default; in react native, column is the default */

几乎同样频繁地,我发现自己编写以下代码:

display: flex;
flex-direction: column;

上面两段代码片段相当常见:第一个获取所有子元素并将其并排(从左到右)排列成一行。第二个获取所有子元素并将其上下(从上到下)排列成一列。每个代码片段都可以变得更具体;但是,我们可以添加不同的属性来进一步指定我们希望子元素如何在页面上布局。例如,如果我们希望元素在可用的屏幕宽度上均匀分布,我们可以将以下行添加到每个代码片段中:

justify-content: space-evenly;

此外,还有其他属性,例如align-items,我们可以添加它们来进一步自定义这些元素的布局。因此,如果我们有三个不同的组件都需要 flexbox 布局,但具有其他不同的属性,我们如何才能以非重复的方式使用 styled-components 呢?

最初,为每个组件创建三组样式是有意义的,如下所示:

// component one
const ComponentOne = styled.div`
  display: flex;
  flex-direction: row;
  justify-content: flex-start;
`


// component two
const ComponentTwo = styled.div`
  display: flex;
  flex-direction: row;
  justify-content: space-between;
`


// component three
const ComponentThree = styled.div`
  display: flex;
  flex-direction: row;
  justify-content: space-evenly;
`

上面列出的样式将完成这项工作。所有三个组件的子元素都排成一行——从左到右排列——每个子元素之间的间距不同。但是,将相同的两行代码重复三次会增加 CSS 膨胀。

为了避免重复自己,我们可以扩展基本组件到每个其他组件,然后将我们需要的附加样式添加到这些组件中:

// flex row component
const ExampleFlex = `
  display: flex;
  flex-direction: row;
`


// component one
const ComponentOne = styled(ExampleFlex)`
  justify-content: flex-start;
`


// component two
const ComponentTwo = styled(ExampleFlex)`
  justify-content: space-between;
`


// component three
const ComponentThree = styled(ExampleFlex)`
  justify-content: space-evenly;
`

感觉好多了。这个版本——我们在其中从<ExampleFlex />组件扩展——不仅消除了重复代码,而且还将与以行显示项目相关的代码保存在一个位置。如果我们需要将与项目方向相关的代码更新为列,我们可以在一个地方进行操作,而不是三个地方。

重要说明:当你从另一个组件扩展样式时,从该基本组件继承的样式需要像上面示例中那样列在基本组件之后。将<ComponentOne />放在<ExampleFlex />上方会导致错误,提示:Uncaught ReferenceError: Cannot access ‘ExampleFlex’ before initialization.

为了更进一步地说明这个想法,下面的演示展示了一个你可能需要在页面上的两个不同的 UI 元素上使用类似样式的情况,但这些元素各自都有其细微的差异。

如你所见,位于页面顶部的导航和位于页面底部的页脚都需要在较大的屏幕上以行方向布局,然后在移动设备上切换到列布局。但是,这两个元素的不同之处在于,页面顶部的导航需要左对齐以在右侧留出空间供徽标使用,而页脚链接需要右对齐。由于这些差异,为每个元素创建两个不同的 styled 组件是有意义的;用于顶部导航的<Navigation />元素和用于页面底部导航的<Footer />组件。

顶部导航样式如下所示:

const Navigation = styled.div`
  align-items: center;
  display: flex;
  flex-direction: row;
  justify-content: space-between;
  padding: 10px;
  width: calc(100% - 20px);


  @media (max-width: 768px) {
    flex-direction: column;
  }
`

而底部页脚样式如下所示:

const Footer = styled.div`
  align-items: center;
  display: flex;
  flex-direction: row; 
  justify-content: flex-end;
  padding: 10px;
  width: calc(100% - 20px);


  @media (max-width: 768px) {
    flex-direction: column;
  }
`

这两个元素唯一的区别是什么?justify-content 属性。此外,<LeftSideNav />组件使用display: flex以及相同的媒体查询。

除了这三个组件共享许多相同的 CSS 之外,<NavItem />组件和<FooterNavItem />是非常相似的链接组件,但也存在细微的差异。那么我们如何才能精简它呢?

查看下面的示例,你将看到可以在多个组件中重复使用的 CSS 可以提取到它自己的组件中,然后我们从中扩展以进行可能需要的特定更改,以创建更具体的组件。

通过实施这些更改,包含所有 styled-components 的 JavaScript 文件比之前的版本缩短了 10 行。虽然这看起来像是微不足道的差异,但随着应用程序的增长,这些更改可以帮助最大程度地减少与应用程序一起发送的 CSS,因为样式会不断添加。

还有“as”多态属性!

除了将样式从一个组件扩展到另一个组件之外,styled-components 还为我们提供了一个名为“as”的多态属性,只要指定了新元素,就可以将给定组件的样式应用于另一个元素。这在两个元素的用户界面可能看起来相同但这两个元素背后的底层 HTML 功能不同的情况下很有帮助。

以按钮和样式化为按钮的链接为例。虽然它们乍一看似乎具有类似的功能——它们都可以被点击以触发操作——但两者在功能上具有不同的用途。按钮非常适合提交表单或更改当前页面布局,但链接会将你带到某个资源。

我在下面创建了一个 Pen 来演示这一点。乍一看,这两个按钮看起来一样。但是,右侧的那个实际上是一个<a>元素,其样式像按钮一样,而左侧的那个是实际的<button>元素。这两个按钮将来可以成为站点导航的一部分,其中一个需要链接到外部站点。在第一次尝试构建这两个元素时,查看我们查看的第一个示例中的每个组件的代码是有意义的。

如果我们知道这两个元素将具有完全相同的样式,我们可以将每个元素都样式化为<Button />,将 styled-component 与我们正在创建的按钮的 JavaScript 代码组合在一起。然后我们将完全相同的样式应用于我们指定的<Link />组件:

查看上面的笔,你可以看到我们已删除了与这两个元素的样式相关的重复 CSS。与其重复用于链接组件的按钮的 CSS,我们可以应用传递给 as 属性的值,如下所示:

<Button as="a" href="#" ... >I am a Link!</Button>

这使我们能够将元素更改为 HTML 标签,同时保留我们已为 Button 定义的样式。


扩展基线样式——也许甚至将其中一些样式放在单个globalStyles.js文件中——是精简 styled-components 代码的有效方法,使其更易于管理。将 CSS 最小化不仅可以提高网站的性能,还可以避免开发人员将来需要浏览大量 CSS 代码。