大家好,我是考100分的小小码 ,祝大家学习进步,加薪顺利呀。今天说一说原子化 JSS 方案 broken-css,希望您对编程的造诣更进一步.
技术背景
对于现代 UI 框架来说,选择使用 JSS 逐渐让人可以接受,而不再被视为“大逆不道”之举。 然而, JSS 作为管理样式的前沿解决方案,有些 JSS 方案可以说被称为前沿的前沿。如:
- linaria
一个零运行时开源的 JSS 方案,在编译期将你的 JSS 抽离解压出来,不用维持一个运行时来解析 JSS 并使得浏览器可以并行的下载 CSS 和 JSS ,具有更高的性能和体积优势。
- stylex
Facebook 内部闭源的原子化的 JSS 方案,其最大的特点是在编译期将 JSS 编译为原子 CSS 解压出来,达到最大程度的复用
const styles = stylex.create({
blue: {color: 'blue'},
red: {color: 'red'}
});
function MyComponent(props) {
return (
<span className={styles('blue', 'red')}>
I'm blue </span> ) }
会被编译为
.c0 { color: blue}
.c1 { color: red}
const styles = stylex.create({
blue: {color: 'blue'},
red: {color: 'red'}
});
function MyComponent(props) {
return (
<span className={"c0 c1"}>
I'm blue
</span>
)
}
受到 linarira 和 stylex 的启发,broken-css 同样是一个零运行时原子化的 JSS 方案,不同点在于 broken-css 选择的 API 的形式,并非 react-native-like ,而是使用了模板字符串函数,这使得 broken-css 可以很容易的支持动画和伪类相关的 CSS 规则,并且使用起来也非常符合传统的使用方式。
介绍
使用
首先你需要安装以下两个库:
- yarn add @broken-css/core
- yarn add -D @broken-css/webpack-loader
@broken-css/core
@broken-css/core 做的事情很简单,只是给 @broken-css/webpack-loader 一个信号,告诉这里需要编译,实际上我们可以看下 @broken-css/core 的源码,你会发现其非常的简洁
export const css = (_literal: TemplateStringsArray, ..._DOES_NOT_SUPPORT_EXPRESSIONS: never[]): string => {
throw new SyntaxError('Do not call css() on runtime!')
};
因为 css … “ 表达式,在编译后会被替换成一段字符串,所以在运行期间,这个函数其实不会被真正执行到,所以并不会抛出这个错误。
@broken-css/webpack-loader
@broken-css/webpack-loader 完成核心步骤,即将代码 JSS 替换成原子化的 CSS 类名 ,并将编译后的原子化 CSS 导入到相应的 JS 文件中,使得 webpack 接管导入 CSS 的流程,以便使用相关 loader 和 plugin 。
例子
假如我们有两个组件 Foo 和 Bar
// Foo.tsx
import { css } from "@broken-css/core";
import React, { FC } from "react";
const Foo: FC = () => {
return (
<div className={css`
color: red;
font-size: 24px;
border: 1px solid black;
@keyframes shake {
10%, 90% {
transform: translate3d(-1px, 0, 0);
}
20%, 80% {
transform: translate3d(2px, 0, 0);
}
30%, 50%, 70% {
transform: translate3d(-4px, 0, 0);
}
40%, 60% {
transform: translate3d(4px, 0, 0);
}
}
&:hover {
animation: shake 0.82s cubic-bezier(.36,.07,.19,.97) both;
transform: translate3d(0, 0, 0);
backface-visibility: hidden;
perspective: 1000px;
}
&::after {
content: ' after';
color: brown;
}
`}>foo</div>
);
}
export default Foo;
// Bar.tsx
import { css } from "@broken-css/core";
import React, { FC } from "react";
const Bar: FC = () => {
return (
<div className={css`
color: red;
font-size: 24px;
border: 1px black solid;
`}>bar</div>
);
}
export default Bar;
在编译后会变成
// Foo.tsx
import { css } from "@broken-css/core";
import React, { FC } from "react";
const Foo: FC = () => {
return <div className={"_0e91 _b38a _43fe _b04b _4b6c"}>foo</div>;
};
export default Foo;
;require("../node_modules/.cache/broken-css-webpack-loader/broken.css");
// Bar.tsx
import { css } from "@broken-css/core";
import React, { FC } from "react";
const Bar: FC = () => {
return <div className={"_43fe _b04b _f617"}>bar</div>;
};
export default Bar;
;require("../node_modules/.cache/broken-css-webpack-loader/broken.css");
/** broken.css **/
@keyframes shake {
10%, 90% {
transform: translate3d(-1px, 0, 0);
}
20%, 80% {
transform: translate3d(2px, 0, 0);
}
30%, 50%, 70% {
transform: translate3d(-4px, 0, 0);
}
40%, 60% {
transform: translate3d(4px, 0, 0);
}
}
._0e91:hover {
animation: shake 0.82s cubic-bezier(.36,.07,.19,.97) both;
transform: translate3d(0, 0, 0);
backface-visibility: hidden;
perspective: 1000px;
}
._b38a::after {
content: ' after';
color: brown;
}
._43fe {color: red;}
._b04b {font-size: 24px;}
._4b6c {border: 1px solid black;}
._f617 {border: 1px black solid;}
原子化
如例子所演示的那样,broken-css 会根据样式的内容计算出一个哈希值来表示这个样式规则,同时这个哈希值会被当做类名替换到相应的 JS 文件中,因为哈希是根据其内容计算的,来自两个文件相同的样式计算出的哈希值是一样的,因此在后续的去重步骤中可以将其筛选掉,从而达到复用的目的。 这里需要多说一点的是,对于样式 border: 1px solid black; 和 border: 1px black solid; 来说, broken-css 并不会视为是同一种样式,尽管他们的效果是一样的。如果要做到这种程度的复用,需要考虑太多的边界情况,我希望有一种简单通用的方法来解决这个问题,还在研究中。
体积优势
在使用 broken-css 后,你的 CSS 体积在刚开始不会明显的减少,但随着项目的发展,越来越多的样式存在重复,使得复用的可能性大大增多,体积就会降下来,如果把体积的变化汇成一条线的话,传统的 CSS 体积增长是一条直线,而 broken-css 则是一条曲线。 配图源于《Atomic CSS-in-JS》
伪类支持
broken-css 支持伪类选择器,对于形如 &::after { … } 的规则,broken-css 会根据整体样式规则计算出哈希值,然后将 & 替换成对应的哈希值。
// a.js
const cls1 = css` color: red; `
// b.js
const cls2 = css` &:hover { color: red; font-size: 24px; } `
会编译为
// a.js
const cls1 = 'c1'
/*
color: red;
*/
// b.js
const cls2 = 'c2 c3'
/*
&:hover {
color: red;
font-size: 24px;
}
*/
.c1, .c2:hover { color: red; }
.c3:hover { font-size: 24px; }
从而达到更细粒度的复用,~~但是对于现有的版本来说,只会编译成 ,已经实现,~~有一些情况会导致样式冲突,已经切换为老的实现
@规则支持
@ 规则的支持是自然而然的,因此你可以自由的使用动画和媒体查询等规则,broken-css 不会对他们做任何处理,只是简单的解压到最终的 CSS 文件中。 这里我纠结的一点在于要不要在 @keyframes 命名的隔离,但是在后续的思考中发现这并不是简单的事情,例如命名 scope 的范围,是隔离在每一次 css … “ 调用期间,这样的话怎么做复用?全局范围的话,意味着需要维持一张状态表,并且分析全局的 CSS 代码将相应的命名替换掉,同样的很复杂。
CSS 变量
broken-css 将 CSS 变量视作一个普通的样式声明,并没有任何特殊的处理,同样的会根据其内容计算出一个哈希值,并分配一个唯一的类名
const cls1 = css` --main-color: red; backgroud-color: var(--main-color); `
const cls2 = css` backgroud-color: var(--main-color); `
会被编译为
const cls1 = 'c1 c2'
const cls2 = 'c2'
.c1 { --main-color: red; }
.c2 { backgroud-color: var(--main-color); }
问题
智能语法提示
我不太熟悉其他的 IDE ,如果你使用 VSCode ,这个问题可以很完美的解决, broken-css 的 API 形式兼容了 vscode-styled-components 扩展。
stylelint
broken-css 在编译期间会 JSS 抽离解压出来,在转换成原子化的 CSS 后,会将后续的行为委托给 webpack ,因此你可以自由的选择是否使用 stylelint-webpack-plugin ,并且 vscode-styled-components 支持书写期间的 lint 检查。
我所在的字节电商广告前端团队,现有大量HC。实习,初级和资深前端都可。可以加微信私聊(yunfeihe),或者简历直发 wujiantao@bytedance.com ,邮件标题请注明【简历】。
作者:何云飞
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
转载请注明出处: https://daima100.com/12760.html