在这篇文章中,我们仔细研究了前端开发中的一项前沿技术:在JavaScript文件中使用CSS。
JS中的CSS是一种相对较新的技术,它允许开发人员直接在JavaScript代码中编写CSS代码,而不是使用单独的CSS文件。
主要好处是:
死代码/规则和树震: 通过使用JavaScript工具,您可以轻松检测未使用的CSS规则并将其从捆绑中删除。 CSS隔离: CSS类名称是动态生成的; 它们通常比常规CSS更小,更孤立。 下载大小: 小型CSS名称和CSS在应用程序捆绑中的嵌入导致更少的网络请求。 JavaScript的灵活性:尽管存在多种工具(SASS, Less等)来更有效地编写CSS,但没有任何东西可以打败完整编程语言的力量! 代码共享: 所有代码都位于同一位置,因此您可以轻松共享CSS和应用程序代码之间的颜色和大小等常数。 静态分析: 使用标准JavaScript工具可以确保没有使用无效的CSS规则或值。在JS中使用CSS显然不是解决所有性能问题的方法,并且有许多开发人员严厉批评了它背后的概念,例如,像这样的帖子 。还有几种解决方案可以通过简单的CSS代码实现重要的性能优势,例如 CSS模块。与往常一样,作为开发人员,我们只需选择最适合产品要求和团队偏好的工具和技术。
在本文中,我们将演示如何在React应用程序中使用JS接受CSS。
在JS工具中选择一个CSSJS工具中有几个开源CSS。看着像一个非穷尽示例列表 这个 有二十多个选项。
所有的工具提供的功能差不多,大部分时间都是框架不可知的,所以选择真的取决于你。
在我们的例子中,我们希望选择易于在堆栈中引入的工具,而不需要太复杂。因此,所有使用 高阶组件 (HOC)方法的工具都被排除在外。
HOC使用开发人员将组件传递给增加新功能的功能的想法。然后使用结果代替原始组件。
同样,我们希望获得的好处之一是静态类型分析,因此也会排除使用ES6模板文字编写CSS的工具。只有将CSS接受为Javascript对象的工具才被考虑列入。
在JS工具的所有现有CSS中,我们选择 TypeStyle 的原因如下:
它使用普通的JavaScript编写代码,而不是强制使用JavaScript模板等。 它有完整的TypeScript类型。对于支持它的编辑器,如Visual Studio Code,它可以在开发时进行完整的静态分析和未使用的代码检测。 它与框架无关,可以很容易地嵌入到大多数框架中,而不会从根本上改变您编写应用程序的方式。 它有用于媒体查询,颜色和动画的实用程序和相关软件包。 由于规则会附加到<style> 页面中的某个 页面上,所以在服务器端渲染(即所谓的关键路径)中生成最少的CSS 是自动的。要查看它的实际操作,请查看我们的React PWA应用程序。nearForm 最近在这篇博文中写到了它 。您可以在https://hackernews.nearform.com上看到该应用程序的实际操作, 并查看GitHub上的 源代码。
如何使用TypeStyle在JS中编写CSS在JS中编写CSS的核心思想是你调用传递CSS规则的样式函数。函数的返回值是您可以在需要时使用的类名称。
生成的类名称就像 f14svl5e。该名称(实际上是传递属性的散列)是从您传入的规则中生成的。没有碰撞。此外,调用此函数会自动将此规则附加到要注册的规则列表。
要开始,我们来定义我们的规则。我们在React中使用TypeStyle创建一个微调器:
const React = require('react')
const { style, media, keyframes } = require('typestyle')
const { px, rem, percent } = require('csx')
const green = rgb(0, 255, 0).darken(0.2).toString()
const animation = keyframes({
[percent(0)]: {
transform: 'rotate(0deg)'
},
[percent(100)]: {
transform: 'rotate(720deg)'
}
});
const spinnerContainerClassName = style({
width: rem(10),
height: rem(10)
})
const spinnerClassName = style(
{
stroke: green,
strokeWidth: 3,
strokeDasharray: percent(300),
strokeLinecap: 'round',
strokeDashoffset: 100,
fill: 'transparent',
animation: `${animation} 2s linear infinite`,
transformOrigin: 'center'
},
media({maxWidth: px(300)}, {
animationDuration: '0.5s'
})
)
function Spinner() {
return (
<svg className={spinnerContainerClassName} viewBox="0 0 60 60">
<circle className={spinnerClassName} cx="30" cy="30" r="15"/>
</svg>
)
}
class TodoApp extends React.Component {
render() {
return (
<Spinner/>
)
}
}
ReactDOM.render(<TodoApp />, document.body)
在这个例子中,我们演示了您通常使用的大部分功能。
首先,我们使用csxTypeStyle的相同开发人员定义了一种颜色 ,它是一个实用程序包。
然后我们定义动画制作微调器的规则。在这种情况下,我们利用更新的Javascript语法和计算密钥的概念。这些值只是CSS规则,所需的唯一更改是重命名其中的所有属性 camelCase。
keyframes 函数再一次 返回一个字符串 - 我们不关心它是什么,我们只需将它传递给样式即可。
然后我们定义这个例子所需的两个类:一个用于SVG本身,另一个用于旋转的内部圆。在后者中,我们重用动画名称; 这在所有生成的样式中都是唯一的,所以不会发生碰撞。
我们还使用该media 功能定义了媒体查询 。这将媒体查询作为第一个参数,将规则对象作为第二个参数。为了测试它,只需使窗口变小并且旋转速度增加。
最后,我们将返回的值作为类名称传递给className 属性的值, 我们完成了!
一旦应用程序被加载并且 style 函数被评估,TypeStyle <style> 在文档头中创建一个 标签并在其中附加所有规则。
这是生成的代码:
<body>
<svg class="f1f6g1bb" viewBox="0 0 60 60">
<circle class="f1wqo8mf" cx="30" cy="30" r="15"></circle>
</svg>
</body>
这是增加的 <style> 元素:
通用规则和高级媒体查询<style>
@keyframes fyn2kv5{0%{transform:rotate(0deg)}100%{transform:rotate(720deg)}}.f1f6g1bb{height:10rem;width:10rem}.f1wqo8mf{animation:fyn2kv5 2s linear infinite;fill:transparent;stroke:rgb(0,153,0);stroke-dasharray:300%;stroke-dashoffset:100;stroke-linecap:round;stroke-width:3;transform-origin:center}@media (max-width: 300px){.f1wqo8mf{animation-duration:0.5s}}
</style>
TypeStyle还允许您使用自定义选择器定义规则。这些规则会立即应用于文档,并且不会生成类名称。
假设我们想要在页面的中心显示我们的微调。我们利用 CSS Flexbox 模块,这个任务很简单。
typestyle.cssRule('html, body', {
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
minHeight: percent(100),
fontSize: '10pt' // This sets 1rem = 10px
})
回到媒体查询,在与他们合作时需要考虑一些事情。虽然CSS应用于顶部,但TypeStyle规则不是。无碰撞类名通常确保这不是问题,但针对相同类的媒体查询可能会显示一些意外行为。
以这个样式表为例:
div {
color: black;
}
@media (max-width: 600px) {
div {
color: red;
}
}
@media (max-width: 400px) {
div {
color: yellow;
}
}
将其应用于文档时,媒体查询将自上而下执行,因此,当窗口大小低于400像素时,div文本颜色为黄色。
使用TypeStyle时,通常将上面的样式表转换为以下定义:
const divClassName = style(
{
color: 'black'
},
media({maxWidth: 600}, {
color: 'red'
}),
media({maxWidth: 400}, {
color: 'yellow'
})
)
TypeStyle不保证类的生成顺序(尤其是因为这依赖于JS引擎),所以我们需要调整媒体查询以避免产生歧义。
上面的代码可以这样修复:
服务器端呈现和关键CSSconst divClassName = style(
{
color: 'black'
},
media({maxWidth: 600, minWidth: 401}, {
color: 'red'
}),
media({maxWidth: 400}, {
color: 'yellow'
})
)
TypeStyle使服务器端渲染(SSR)非常简单。核心思想是,一旦您在某个变量中准备好了服务器生成的HTML,就可以调用该getStyle函数以刷新所有已使用的样式。
这也有利于确保在响应中只发送使用的CSS规则 - 这就是所谓的 关键CSS。
以下是如何在服务器上使用React和TypeStyle生成完整HTML页面的简单示例:
老化性能的缺点和改进app.get('/', function (req, res) {
const body = ReactDOMServer.renderToStaticMarkup(<Application/>)
const html = ReactDOMServer.renderToStaticMarkup(
<html>
<head>
<title>SSR</title>
<style>{getStyles()}</style>
</head>
<body dangerouslySetInnerHTML= />
</html>
)
res.send(html);
});
在代码中嵌入TypeStyle当然有其缺点。特别是,这两个新库的增加使PWA应用代码大小增加了大约15KB。
好消息是,当这些额外的数据加载时,应用程序和用户交互根本没有被阻止。该应用程序已经在服务器上呈现,并且所有需要的CSS已经在具有HTML页面的设备上着陆; 因此浏览器拥有呈现网页所需的一切。
此外,关于增加 - 在快速网络环境中,这些额外数据几乎不可感知,在慢速或不可靠网络上,它会在第一次渲染时间上获得巨大收益。
这是使用纯CSS的原始分支上的页面加载的原始性能。
通过使用TypeStyle,我们几乎可以将慢速3G网络上的首次渲染时间缩短50%。
要概述TypeStyle如何在有限的情况下提供帮助,我们使用webpagetest使用的配置文件设置,在不同的移动网络条件下运行相同的比较 :
正如您从下面的图表中看到的,在每个网络请求非常昂贵的较慢网络中,这些好处更加明显。即使在大多数高性能网络中,在JS技术中使用CSS进行嵌入,第一次渲染时间也会缩短100ms。
在所有情况下,JS中的CSS最多增加100ms第一个互动。如前所述,如果我们查看用户转换率的增长,首次呈现时间的减少使其可以接受。
PWA的引入彻底改变了我们开发应用程序的方式。移动设备现在能够以几年前甚至无法想象的方式离线存储信息。
PWA介绍了在无情的网络环境中提供内容服务的各种新方法。我们不能忘记,首先需要一个网络来加载缓存。每个应用程序都是不同的,引入服务工作者可以让开发人员提供可以更好地适应特定用例的自定义缓存策略。
在这篇文章中,我们概述了如何在JS中使用CSS可以帮助减少应用程序大小以及首次加载它所需的网络请求数量。虽然这种技术不是所有现代Web应用程序性能问题的灵丹妙药,但它会导致第一次渲染时间的显着缩短。