2020 年 Facebook 新技术栈中关于 Atomic CSS 和 JavaScript 的 9 点值得学习
回顾早期的 Facebook
为什么他们不能升级到最新的技术栈?
FB 关注的两大重点
Facebook 教给我们的神奇四侠方法
感谢您的阅读,我是 Tharun Shiv,又名开发者 Tharun
回顾早期的 Facebook
Facebook 成立初期,是一个服务器渲染的 PHP 网站。我们见证了它随着时间的推移,进行了多少改进,添加了多少功能。我一直非常欣赏Facebook提供的功能、UI、动画和贡献。
无论是网站的流畅度,回复帖子时表情符号的动画,还是最新的暗黑模式、加载阶段,以及诸如 Marketplace、直播、游戏等众多功能。当我思考他们为开发者社区做出的贡献时,我就会想起 React 和 React Native ❤
为什么他们不能升级到最新的技术栈?
Facebook 是一家庞大的公司,汇聚了众多高智商人才。用最新、最好的技术栈重建整个网站难道不应该很容易吗? ……
这根本不容易!对于一家规模如此庞大的公司来说,每次社区推出更好的技术时,都不可能编写一个可扩展的新网站。
FB 关注的两大重点
- 尽早提供必要的功能
- 默认将用户体验 (UX) 视为工程过程的一部分
Facebook 教给我们的神奇四侠方法
重新思考 CSS
如果你开发过任何生产级的 Web 开发应用程序,你就会知道它包含多少 CSS 代码行和文件。即使是入门级的 CSS 应用程序,数千行代码也是很常见的。想象一下像 Facebook 这样的应用程序需要多少 CSS 代码。
首先,他们将主页缩减了 80%
首先,Facebook 是由世界上最优秀的工程师开发的,而且如果他们能将其优化 80%,那么它就是超级工程。让我们看看他们是如何做到这一点的。
1. 生成原子 CSS
那么原子 CSS 到底是什么呢?它是一种 CSS 的编写方式。顾名思义,“原子”就是不可分割的。所以这种 CSS 是不可分割的。样式是通过类来实现的,每个类只有一个样式。是的,你没看错。
Atomic CSS 的使用示例:
<div class="Bgc(#0280ae.5) C(#fff) P(20px)">
Lorem ipsum
</div>
.Bgc\(\#0280ae\.5\) {
background-color: rgba(2,128,174,.5);
}
.C\(\#fff\) {
color: #fff;
}
.P\(20px\) {
padding: 20px;
}
输出:
了解有关 Atomic CSS 的更多信息,请访问https://acss.io/
为如此庞大的平台手动编写代码并非人力所能及,因此他们使用工具生成 Atomic CSS。Atomic CSS 具有对数增长曲线,因为它与唯一样式声明的数量成正比,而不是与我们编写的样式和特性的数量成正比。[太棒了!]
2. 管理未使用的 CSS
CSS 体积会随着时间推移而增大的另一个原因是,由于大量新特性的引入,代码中会引入新编写的 CSS。页面中可能会加载一些未被移除的旧 CSS。这又会导致下载的 CSS 大小增加。因此,他们将样式与组件放在一起,以便可以并行删除它们,并且只在构建时将它们拆分成单独的包。使用 Atomic CSS 和工具也解决了这个问题。
他们还解决了 CSS 优先级问题,因为当使用工具生成包和渲染页面时,顺序可能会混乱,因此他们使用了受React Native样式 API 启发的熟悉语法。他们也不再支持 CSS 后代。
3. 更改字体大小以提高可访问性
他们已经完全改用rem来表示尺寸了。尺寸的表示方式有很多,比如 px、em、rem 等等。rem是一种更智能的表示尺寸的方式,因为它会根据根元素的尺寸来调整元素的大小。所以,如果根元素的尺寸是 16px,那么 2rem 的元素尺寸就是 32px。
例子:
html {
font-size: 16px;
}
.heading-text {
font-size: 1.2rem;
}
.caption {
font-size: 0.9rem;
}
4. 构建代码时处理
他们还通过使用其他技术改进了网站,例如在构建时使用 Atomic CSS 以及连接各个类。
示例生成的代码
.class0 { font-weight: bold; }
.class1 { font-weight: normal; }
.class2 { font-size: 0.9rem; }
function MyComponent(props) {
return <span className={(props.isEmphasized ? 'class0 ' : 'class1 ') + 'class2 '} />;
}
5. 使用 CSS 变量进行主题设置
_ 旧版 Facebook 的主题是如何处理的?_ 他们过去是根据主题应用类,用更高优先级的规则覆盖现有样式。但使用 Atomic CSS 后,这种方法不再适用。现在,他们使用 CSS 变量,CSS 属性在页面加载时就已经存在,无需重新加载页面即可更改主题。使用这种方法的优势在于:
- 将所有主题合并到单个样式表中
- 更改主题时无需重新加载页面
- 不同的页面可以有不同的主题,无需下载
- 同一页面上的不同功能可以同时具有不同的主题
例子
.light-theme {
--card-bg: #eee;
}
.dark-theme {
--card-bg: #111;
}
.card {
background-color: var(--card-bg);
}
重新思考 JavaScript
1. 直接在 JavaScript 中使用 SVG
之前,图标加载完其他内容后,经常会出现闪烁的情况。现在,这个问题可以通过使用内联 SVG 来解决,只需创建一个组件来返回该 SVG 即可。由于 SVG 现在与 JavaScript 捆绑在一起,可以与其他组件一起加载,从而提升了性能。因此,后续加载的图标再也不会出现闪烁的情况。
function MyIcon(props) {
return (
<svg {...props} className={styles({/*...*/})}>
<path d="M17.5 ... 25.479Z" />
</svg>
);
}
以这种方式使用 SVG 的另一个优点是,我们可以更改这些 SVG 的属性,而无需重新加载网站。
2.增量代码下载
使用这种方法,页面会分几个步骤加载。每个步骤都专注于显示尽可能少的有价值内容。这样,我们可以防止用户过早离开页面。
3. 仅在必要时交付依赖项
在这种技术中,服务器仅在需要时才会交付依赖项,例如实验驱动的依赖项和数据驱动的依赖项。假设他们正在对功能进行 A/B 测试,则只有在显示依赖项时才会交付依赖项,否则将不会交付。
const Composer = importCond('NewComposerExperiment', {
true: 'NewComposer',
false: 'OldComposer',
});
考虑到用户的 feed 中充满了图片,它们将只传递这些组件所需的依赖项。
此功能由 GraphQL Relay 实现。
... on Post {
... on PhotoPost {
@module('PhotoComponent.js')
photo_data
}
... on VideoPost {
@module('VideoComponent.js')
video_data
}
}
4. JavaScript 预算介绍
之前我们看到页面包含多个功能以及页面加载的多个阶段。现在,他们必须确保每个阶段都能持续受控。为了实现这一点,他们引入了按产品划分的 JavsScript 预算。
所以这些预算是基于性能目标、技术约束和产品考虑的。他们分配了页面级别的预算、产品边界和团队边界。
Facebook 工程团队的官方博客上提供了更多关于 CSS、JS、数据和导航的细节。我已尽力简化。本文及其示例均受官方博客文章的启发。感谢您的阅读。
作者: