在 CSS-in-JS 中使用模板字符串的核心原理是 JavaScript 的标签模板(Tagged Template Literals) 机制。下面详细解释其工作原理:
1. 标签模板的基本语法
标签模板是 JavaScript 的高级特性,允许你用自定义函数解析模板字符串:
javascript
// 标签函数
function tag(strings, ...values) {
console.log(strings); // 静态字符串数组
console.log(values); // 动态插入的值
return "处理后的结果";
}
// 使用标签函数解析模板字符串
const result = tag`Hello ${name}, you have ${count} messages.`;
执行过程:
strings
参数:["Hello ", ", you have ", " messages."]
values
参数:[name, count]
- 标签函数返回最终处理后的字符串或对象
2. CSS-in-JS 如何利用标签模板
在 CSS-in-JS 中,标签函数会将模板字符串解析为 CSS 对象或样式标签:
示例:基础实现
javascript
function css(strings, ...values) {
// 组合静态字符串和动态值
let result = '';
strings.forEach((string, i) => {
result += string + (values[i] || '');
});
// 生成唯一类名
const className = `css-${Math.random().toString(36).substr(2, 8)}`;
// 创建 style 标签并添加到 head
const style = document.createElement('style');
style.textContent = `
.${className} {
${result}
}
`;
document.head.appendChild(style);
return className; // 返回生成的类名
}
// 使用方式
const titleClass = css`
color: red;
font-size: 24px;
`;
// 应用样式
document.querySelector('h1').className = titleClass;
3. 动态样式的实现原理
当模板字符串中包含 JavaScript 表达式时,标签函数会在运行时动态计算这些值:
javascript
const Button = styled.button`
background-color: ${props => props.primary ? 'blue' : 'gray'};
font-size: ${props => props.size || 16}px;
`;
执行过程:
-
styled.button
返回一个 React 组件 -
组件渲染时,标签函数会:
- 接收组件的
props
- 执行模板字符串中的箭头函数
props => ...
- 生成最终的 CSS 字符串
- 创建或复用对应的 style 标签
- 接收组件的
4. 主流库的实现差异
不同 CSS-in-JS 库在底层实现上略有不同:
styled-components
- 使用 标签模板 解析 CSS 字符串
- 在运行时生成唯一的类名(如
sc-bdVaJa
) - 通过
StyleSheetManager
管理样式注入
emotion
-
支持 编译时优化:通过 Babel 插件将 CSS 提取为静态字符串
-
提供
css
和styled
两种 API -
示例:
javascript
javascriptimport { css } from '@emotion/react'; const style = css` color: ${props => props.color}; font-size: 16px; `;
linaria
- 纯编译时方案:完全不依赖运行时
- 通过 Babel 插件将 CSS 提取到单独的 CSS 文件
- 模板字符串中的动态表达式会被保留为 CSS 变量
5. 性能优化与缓存
为避免重复创建相同的 style 标签,库通常会实现缓存机制:
javascript
// 简化的缓存实现
const styleCache = new Map();
function css(strings, ...values) {
// 生成 CSS 内容的哈希值
const contentHash = generateHash(strings.join('') + values.join(''));
// 检查缓存
if (styleCache.has(contentHash)) {
return styleCache.get(contentHash);
}
// 生成新样式并缓存
const className = `css-${contentHash}`;
injectStyle(className, strings, values);
styleCache.set(contentHash, className);
return className;
}
6. 与 CSS Modules 的对比
特性 | CSS Modules | CSS-in-JS (模板字符串) |
---|---|---|
作用域 | 编译时生成局部类名 | 运行时生成唯一类名 |
动态性 | 仅支持 CSS 变量 | 完全支持 JavaScript 逻辑 |
样式注入 | 打包到 CSS 文件 | 动态插入 style 标签 |
语法 | 类 CSS 语法 | 类 CSS 语法 + JS 表达式 |
总结
CSS-in-JS 中模板字符串的核心原理是:
-
标签模板:JavaScript 提供的解析模板字符串的机制
-
运行时解析:在组件渲染时动态计算 CSS 值
-
样式注入:将生成的 CSS 插入到 DOM 中
-
唯一类名:避免全局样式冲突
-
缓存优化:避免重复创建相同的样式
这种方式结合了 CSS 的声明式语法和 JavaScript 的动态能力,为现代 UI 组件提供了强大而灵活的样式管理方案。