block-name__element-name--modifier-name
,也就是模块名 + 元素名 + 修饰器名关注点分离
的网页开发原则,因其采用组件结构,而组件又强制要求将 HTML、CSS 和 JS 代码写在一起。表面上看是技术的倒退,实际上并不是编程能力
。对于 HTML,衍生了 JSX 这种 JS 的语法扩展,你可以将其理解为 HTML-in-JS
;对于 CSS,衍生出一系列的第三方库,用来加强在 JS 中操作 CSS 的能力,它们被称为 CSS-in-JS
关注点混合
的新写法逐渐成为主流Scoping Styles
可以让CSS拥有独立的作用域,实现局部样式,防止样式冲突和影响组件外的内容Dead Code Elimination
避免无用的CSS样式堆积 Critical CSS
重要的CSS放在头部的style
标签内,其它的CSS异步加载可以减少渲染阻塞State-based styling
根据组件的状态动态地生成样式更彻底的封装
让组件拥有更好的移植性和重用性Steep learning curve
陡峭的学习曲线Runtime cost
运行时消耗Unreadable class names
代码可读性差 No interoperability
没有统一的 业界标准emotion
是新一代的CSS-IN-JS
解决方案CSS-in-JS
实现是通过生成唯一的CSS选择器来达到CSS局部作用域的效果src\main.jsx
import React from 'react'
import ReactDOM from 'react-dom'
import App from './App'
ReactDOM.render(
<App />,
document.getElementById('root')
)
src\App.jsx
import { css } from './@emotion/css'
const className = css`
color: red;
`;
function App() {
return (
<div className={className}>
App
</div>
)
}
export default App
src\@emotion\css\index.js
export { default as css } from './css';
src\@emotion\css\css.js
import { serializeStyles } from '../serialize';
import { insertStyles } from '../utils';
function css(...args) {
const serialized = serializeStyles(args);
insertStyles(serialized);
return 'css' + "-" + serialized.name;
}
export default css;
src\@emotion\serialize\index.jsx
export { default as serializeStyles } from './serializeStyles';
src\@emotion\serialize\serializeStyles.jsx
import { hashString } from '../utils';
function serializeStyles(args) {
let styles = '';
const strings = args[0];
styles += strings[0];
const name = hashString(styles);
return { name, styles }
}
export default serializeStyles;
src\@emotion\utils\index.jsx
export { default as insertStyles } from './insertStyles'
export { default as hashString } from './hashString';
src\@emotion\utils\hashString.jsx
function hashString(keys) {
let val = 10000000;
for (let i = 0; i < keys.length; i++) {
val += keys.charCodeAt(i);
}
return val.toString(16).slice(0, 6);
}
export default hashString;
src\@emotion\utils\insertStyles.jsx
function insertStyles(serialized) {
const className = 'css' + "-" + serialized.name;
const rule = "." + className + "{" + serialized.styles + "}";
const tag = document.createElement('style');
tag.setAttribute('data-emotion', 'css');
tag.appendChild(document.createTextNode(rule));
document.head.appendChild(tag);
}
export default insertStyles;
src\App.jsx
import { css } from './@emotion/css'
+const className = css(
+ {
+ color: 'red'
+ }
+);
function App() {
return (
<div className={className}>
App
</div>
)
}
export default App
src\@emotion\serialize\serializeStyles.jsx
import { hashString } from '../utils';
function serializeStyles(args) {
var styles = '';
var strings = args[0];
+ if (strings.raw === undefined) {
+ styles += handleInterpolation(strings);
+ } else {
+ styles += strings[0];
+ }
var name = hashString(styles);
return { name, styles }
}
+function handleInterpolation(interpolation) {
+ switch (typeof interpolation) {
+ case 'object': {
+ return createStringFromObject(interpolation);
+ }
+ }
+}
+function createStringFromObject(obj) {
+ var string = '';
+ for (var key in obj) {
+ var value = obj[key];
+ string += key + ":" + value + ";";
+ }
+ return string;
+}
export default serializeStyles;
style prop
,但也支持自动供应商前缀、嵌套选择器和媒体查询npm install @emotion/react --save
vite.config.js
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
export default defineConfig({
plugins: [react(
{
+ jsxRuntime: 'classic'
}
)]
})
src\App.jsx
+/** @jsx jsx */
+import { css, jsx } from './@emotion/react'
+const styles = css(
+ {
+ color: 'red'
+ }
+);
function App() {
return (
+ <div css={styles}>
App
</div>
)
}
export default App
src\@emotion\react\index.jsx
export { default as css } from './css';
export { default as jsx } from './jsx';
src\@emotion\react\css.jsx
import { serializeStyles } from '../serialize';
function css(...args) {
return serializeStyles(args);
}
export default css;
src\@emotion\react\jsx.jsx
import { serializeStyles } from '../serialize';
import { Insertion } from '../utils';
function Emotion(props) {
const serialized = serializeStyles(props.css);
const { css, ...newProps } = props
newProps.className = 'css' + "-" + serialized.name;
const WrappedComponent = props.type;
return (
<>
<Insertion serialized={serialized} />
<WrappedComponent {...newProps} />
</>
)
}
function jsx(type, props, ...children) {
return (
<Emotion {...props} type={type}>
{children}
</Emotion >
)
}
export default jsx;
src\@emotion\serialize\serializeStyles.jsx
import { hashString } from '../utils';
function serializeStyles(args) {
+ if (typeof args === 'object' && args.styles !== undefined) {
+ return args;
+ }
var styles = '';
var strings = args[0];
if (strings == null || strings.raw === undefined) {
styles += handleInterpolation(strings);
} else {
styles += strings[0];
}
var name = hashString(styles);
return { name, styles }
}
function handleInterpolation(interpolation) {
switch (typeof interpolation) {
case 'object': {
return createStringFromObject(interpolation);
}
}
}
function createStringFromObject(obj) {
var string = '';
for (var key in obj) {
var value = obj[key];
string += key + ":" + value + ";";
}
return string;
}
export default serializeStyles;
src\@emotion\utils\Insertion.jsx
import { useLayoutEffect } from 'react';
import insertStyles from './insertStyles';
function Insertion({ serialized }) {
useLayoutEffect(() => {
insertStyles(serialized);
});
return null;
};
export default Insertion;
src\@emotion\utils\index.jsx
export { default as insertStyles } from './insertStyles';
export { default as hashString } from './hashString';
+export { default as Insertion } from './Insertion';
npm install @emotion/styled --save
src\App.jsx
+import styled from './@emotion/styled'
+const Button = styled.button(
{
color: 'red'
}
+);
function App() {
return (
+ <Button>Button</Button>
)
}
export default App
src\@emotion\styled\index.jsx
import { serializeStyles } from '../serialize';
import { Insertion } from '../utils';
function createStyled(tag) {
return function (...args) {
function Styled(props) {
const serialized = serializeStyles(args);
const className = 'css' + "-" + serialized.name;
const newProps = { ...props };
newProps.className = className;
const FinalTag = tag;
return (
<>
<Insertion serialized={serialized} />
<FinalTag {...newProps} />
</>
)
}
return Styled;
}
}
const newStyled = createStyled.bind();
const tags = ['button', 'div'];
tags.forEach(function (tagName) {
newStyled[tagName] = newStyled(tagName);
});
export default newStyled;
src\App.jsx
import styled from './@emotion/styled'
const Button = styled.button(
{
background: 'green',
color: 'red'
+ }, props => ({
+ color: props.color
+ })
);
function App() {
return (
+ <Button color="white">Button</Button>
)
}
export default App
src\@emotion\styled\index.jsx
import { serializeStyles } from '../serialize';
import { Insertion } from '../utils';
function createStyled(tag) {
return function (...args) {
function Styled(props) {
+ const serialized = serializeStyles(args, props);
const className = 'css' + "-" + serialized.name;
const newProps = { ...props };
newProps.className = className;
const FinalTag = tag;
return (
<>
<Insertion serialized={serialized} />
<FinalTag {...newProps} />
</>
)
}
return Styled;
}
}
const newStyled = createStyled.bind();
const tags = ['button', 'div'];
tags.forEach(function (tagName) {
newStyled[tagName] = newStyled(tagName);
});
export default newStyled;
src\@emotion\serialize\serializeStyles.jsx
import { hashString } from '../utils';
+function serializeStyles(args, props) {
if (typeof args === 'object' && args.styles !== undefined) {
return args;
}
var styles = '';
var strings = args[0];
if (strings.raw === undefined) {
+ styles += handleInterpolation(props, strings);
} else {
styles += strings[0];
}
for (var i = 1; i < args.length; i++) {
styles += handleInterpolation(props, args[i]);
}
var name = hashString(styles);
return { name, styles }
}
+function handleInterpolation(props, interpolation) {
switch (typeof interpolation) {
case 'object': {
+ return createStringFromObject(props, interpolation);
}
+ case 'function': {
+ if (props !== undefined) {
+ var result = interpolation(props);
+ return handleInterpolation(props, result);
+ }
+ }
}
}
function createStringFromObject(obj) {
var string = '';
for (var key in obj) {
var value = obj[key];
string += key + ":" + value + ";";
}
return string;
}
export default serializeStyles;
src\App.jsx
import styled from './@emotion/styled'
+function Hello({ className }) {
+ return <button className={className}>Hello</button>
+}
+const RedHello = styled(Hello)`
+ color:red;
+`
function App() {
return (
+ <RedHello>Button</RedHello>
)
}
export default App;
.css-1wvgi8y-Parent{background:green;}
.css-10c6c3i-Child{color:red;}
.css-1wvgi8y-Parent .eesff8e1{color:blue;}
<div class="css-1wvgi8y-Parent eesff8e0">
<div class="css-10c6c3i-Child eesff8e1">App</div>
</div>
npm install @emotion/babel-plugin --save
vite.config.js
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
export default defineConfig({
plugins: [react(
{
jsxRuntime: 'classic',
babel: {
+ plugins: ['@emotion/babel-plugin']
}
}
)]
})
src\App.jsx
import styled from '@emotion/styled'
const Child = styled.div({
color: 'red'
})
const Parent = styled.div({
background: 'green',
[Child]: {
color: 'blue'
}
})
function App() {
return (
<>
<Parent>
<Child>Child</Child>
</Parent>
<Child>Child</Child>
</>
)
}
export default App
src\App.jsx
import styled from '@emotion/styled'
const Container = styled.div`
width:200px;
height:200px;
background:lightgray;
&:hover{
background:pink;
}
& > p {
color:green;
}
`
function App() {
return (
<Container>
Container
<p>span</p>
</Container>
)
}
export default App
src\App.jsx
import styled from '@emotion/styled'
const Button = styled.button`
color:red
`
function App() {
return (
<Button as="a">
Button
</Button>
)
}
export default App
src\App.jsx
/** @jsx jsx */
import { jsx, css } from '@emotion/react'
const base = css`
color:white;
`
const warning = css`
background:orange;
`
function App() {
return (
<button css={[base, warning]}>Button</button>
)
}
export default App
src\App.jsx
/** @jsx jsx */
import { jsx, css, Global } from '@emotion/react'
const reset = css`
body{
margin:0;
}
a{
color:red;
}
`
function App() {
return (
<>
<Global styles={reset} />
<a>我是a标记</a>
</>
)
}
export default App
src\App.jsx
/** @jsx jsx */
import { jsx, css, keyframes } from '@emotion/react'
const bounce = keyframes`
from {
transform: translateX(0);
}
to {
transform: translateX(100px);
}
`
const base = css`
width:100px;
height:100px;
background: green;
position: absolute;
animation: ${bounce} 1s ease infinite alternate;
`;
function App() {
return (
<div css={[base]}>
文本
</div>
)
}
export default App
标签模板
功能raw
属性,保存的是转义后的原字符串function tag(stringArr, ...values) {
console.log(stringArr.raw);
let output = "";
let index;
for (index = 0; index < values.length; index++) {
output += stringArr[index] + values[index];
}
output += stringArr[index]
return output;
}
let v1 = 1;
let v2 = 2;
let result = tag`a${v1}b${v2}c`;
console.log(result);