译文:在JavaScript应用程序中包含CSS的多种方法
如有翻译不准确,请多指正。
欢迎来到这个在前端开发领域极具争议性的话题!我相信读到这篇文章的大多数人都曾遇到过关于#如何在JavaScript应用程序中处理CSS#相关的热门问题。
在这篇文章之前,我想声明一句:没有什么硬性标准可以确定在React、Vue或Angular应用程序中处理CSS的哪一种方法是最好的。每个项目都是不同的,每个方法都有自己的优点!这样说好像有点含糊不清,但我知道的是,我们所处的开发社区中有很多人去不断寻求新信息,他们希望以更加有意义的方式推动web向前发展。
暂且把关于这个主题先入为主的概念放在一边,让我们来看看迷人的CSS体系结构世界吧!
首先让我们一起来盘点一下这些方法
简单地搜索“如何将CSS添加到[在此处插入框架]”就能出现一系列关于如何将样式应用于项目的意见。为了帮助大家进行筛选摘除,我们将从各更高级的角度研究一些常用的方法及其目的。
选项1:传统样式表
我们完全可以用一种熟悉的方式开始:一个传统样式表,完全可以在应用程序之中链接一个外部样式表,这样就可以完工了。
我们可以按照我们的习惯编写CSS,然后继续我们的生活。这根本没有什么问题,但是随着应用程序越来越大、越来越复杂,维护单个样式表就越来越难了。解析数千行CSS(这些CSS负责设计整个应用程序的样式)对于从事该项目的任何开发人员来说都是一件痛苦的事情。
样式级联看起来很美好但它也变得很难管理,因为你或项目上的其他开发者编写的一些样式会在应用程序的其他部分引入回归。我们之前也遇到过这些问题,并且引入了Sass之类的东西(以及最近的PostCSS)来帮助我们处理这些问题。
我们可以继续沿着这条路走下去,利用PostCSS的强大功能来编写非常模块化的CSS部分,这些部分通过@import规则串在一起。这需要在webpack配置中进行一些工作才能正确设置,但这些事情大家肯定可以处理!
无论你决定用哪种编译器,你都可以通过标题中的标记获得一个包含应用程序所有样式的CSS文件。根据应用程序的复杂程度,这个文件可能会变得非常臃肿,难以异步加载,并且应用程序的其余部分会出现租用者阻塞。(当然,阻塞并不总是一件坏事,但是为了所有的意图和目的,我们将在这里泛化一点,尽可能避免渲染阻塞脚本和样式。)
这并不是说这种方法毫无优点,对于小型应用程序,或者由不太关注前端的团队构建的应用程序,单个样式表可能是一个不错的选择。它在业务逻辑和应用程序风格之间提供了清晰的分离,而且由于它不是由我们的应用程序生成的,因此完全在我们的控制范围内,以确保我们编写的内容与输出的内容完全一致。此外,浏览器缓存单个CSS文件相当容易,因此返回的用户在下次访问时不必重新下载整个文件。
但是假设我们正在寻找一个更健壮的CSS架构,它允许我们利用工具的强大功能。它可以帮助我们管理一个需要更多微妙方法的应用程序,输入CSS模块。
选项2:CSS模块
单个样式表中的一个相当大的问题是回归的风险。编写使用相当非特定选择器的CSS可能最终会改变应用程序中完全不同区域的另一个组件。这就是所谓的“作用域样式”派上用场的地方。
带作用域的样式允许我们以程序化的方式生成特定于组件的类名。因此,将这些样式限定到该特定组件,以确保其类名是唯一的。这将导致自动生成的类名,例如header__2lexd。最后一点是一个哈希值,它是唯一的,因此即使你有另一个名为header的组件,也可以对其应用标题类,并且我们的作用域样式将生成一个新的哈希后缀,如下所示:header__15qy_。
CSS模块提供了控制生成的类名的方法(具体取决于实现),但是我将把这个问题留给CSS模块文档来讨论。
所有这些都完成之后,我们仍然在生成一个CSS文件,该文件通过<link>标头中的标记传递给浏览器。这带来了同样的潜在缺点(渲染阻塞、文件大小膨胀等),当然也有好处主要是缓存作用。但是,由于这种方法的作用域样式附带了另一个警告:删除全局作用域——至少在最初是这样。
假设你想使用.screen-reader-text全局类,该类可以应用于应用程序中的任何组件。如果使用CSS模块,则必须使用:global伪选择器,该选择器将其中的CSS明确定义为可被应用程序中其他组件全局访问的对象。只要将包含global声明块的样式表导入到组件的样式表中,就可以使用该全局选择器。这不是一个很大的缺点,但是需要逐渐适应。
这是一个:global伪选择器的示例:
你可能会冒着将一堆用于字体,表格以及大多数站点的通用元素的全局选择器放到一个单独的global选择器中的风险。幸运的是,通过诸如PostCSSNested或Sass之类的魔术,你可以将部分代码直接导入选择器中,以使事情更简洁:
这样,你可以不使用:global选择器来编写局部文件,而直接将其直接导入到主样式表中。
需要习惯的另一点是如何在DOM节点中引用类名。我将在这里以Vue,React和Angular的各个文档为例。我还将为你提供一些示例,说明这些类引用在React组件中的使用方式:
同样,CSSModules方法具有一些很好的用例。对于希望利用范围样式,同时保持静态但已编译样式表的性能优势的应用程序,CSS模块可能是最适合你的选择!
在这里也要注意,CSS模块可以与你喜欢的CSS预处理功能结合使用。Sass,Less,PostCSS等都可以使用CSS模块集成到构建过程中。
但是,假设你的应用程序可以通过包含在JavaScript中而受益。也许获得对组件各种状态的访问权并根据不断变化的状态做出反应也将是有益的。如果你也想轻松地将关键CSS集成到的应用程序中去,那就输入CSS-in-JS吧。
选项3:CSS-in-JS
CSS-in-JS是一个相当广泛的主题。有几个包可以使CSS-in-JS的编写变得尽可能轻松。像JSS、Emotion和样式组件之类的框架只是构成本主题的众多包中的一小部分。
作为大多数这些框架的粗略解释,CSS-in-JS基本上以相同的方式运行。你编写与单个组件关联的CSS,并且构建过程将编译该应用程序。发生这种情况时,大多数CSS-in-JS框架只会输出在任何给定时间在页面上呈现的组件的关联CSS。CSS-in-JS框架通过在应用程序的<head>中的<style>标记内输出该CSS来实现此目的。这为你提供了一个非常重要的CSS加载策略!另外,很像CSS模块,样式是作用域的,并且类名是散列的。
当你在应用程序中导航时,已卸载的组件将从<head>中删除其样式,而已装入的传入组件将在其位置附加其样式。这为你的应用程序提供了性能优势的机会。它删除一个HTTP请求,不会阻止渲染,并确保你的用户在任何给定时间仅下载他们需要的内容以查看该页面。
CSS-in-JS提供的另一个有趣的机会是能够引用各种组件状态和功能以呈现不同的CSS。这可能很简单,例如根据某些状态变化来复制类切换,也可能像主题化一样复杂。
因为CSS-in-JS是一个相当热门的话题,所以我意识到人们正在尝试采用许多不同的方法。现在,我分享了许多其他人对CSS的高度评价,尤其是在利用JavaScript编写CSS方面。我对这种方法的最初反应是相当负面的。我不喜欢交叉污染两者的想法,但我想保持开放的态度。
让我们来看看我们作为前端开发人员需要的一些功能,甚至可以考虑采用这些方法。
1. 如果我们要编写CSS-in-JS,则必须编写真实的CSS。多个软件包提供了编写模板文字CSS的方法,但是要求你用驼峰式大小写属性-即padding-left变成paddingLeft。那不是我个人愿意牺牲的东西。
2. 某些CSS-in-JS解决方案要求你在要设置样式的元素上内联编写样式。这样做的语法,尤其是在复杂的组件中,开始变得非常忙碌,而这又不是我愿意牺牲的东西。
3. 使用CSS-in-JS必须为我提供强大的工具,否则这些工具很难通过CSS模块或dangol的样式表来完成。
4. 我们必须能够利用具有前瞻性的CSS(例如嵌套和变量)。我们还必须能够合并Autoprefixer之类的东西以及其他附加组件,以增强开发人员的体验。
对于框架,有很多要求,但是对于那些一生中大部分时间都围绕着我们喜欢的语言来研究和实现解决方案的人来说,我们必须确保我们能够继续以最好的语言编写相同的语言我们可以快速浏览一下使用样式化组件的React组件的外观:
我们还需要解决CSS-in-JS解决方案的潜在缺点——而且绝对不要试图引发更多的闹剧。使用这样的方法,我们很容易陷入一个陷阱,这会导致我们得到一个膨胀的JavaScript文件,其中可能包含数百行CSS——而这一切甚至在开发人员看到任何组件的方法或其HTML结构之前就已经出现了。
然而,我们可以将此视为一个机会,来非常仔细地检查我们是如何以及为什么以这种方式构建组件的。通过更深入地考虑这一点,我们可以潜在地利用它,并使用更多可重用组件编写更简洁的代码。
此外,此方法绝对模糊了业务逻辑和应用程序样式之间的界限。但是,有了一个有据可查且经过深思熟虑的体系结构,该项目中的其他开发人员可以轻松地采用这种想法,而不会感到不知所措。
写在最后
有多种方法可以处理任何项目上的CSS体系结构的难题,并且可以在使用任何框架时进行处理。作为开发人员,我们拥有如此众多的选择,这既令人兴奋,又令人难以置信。但是,我认为,最终我们会在超短的社交媒体对话中迷失的首要原因是——每种解决方案都有其优点和效率低下的弊病。这是关于我们如何谨慎,周到地实施使我们自己/或其他可能接触代码的开发人员的系统的感谢,感谢我们花时间建立该结构。