React 项目的css-in-js
React 项目的css-in-js(简称JSS)
TS的定义声明
- 文件名为:
*.d.ts
- 只包含类型声明,不包含逻辑
- 不会被编译,也不会被webpack、vite打包
1. css modules(配置typescript-plugin-css-modules
完成类型申明)
-
定义css的类型申明文件
custom.d.ts
在
custom.d.ts
文件中定义声明,需要用到ts的两个关键字:declare 和 module, 声明的对象就是css文件在我们在import 以css为后缀的文件都会遵循以下的约定,约定的内容是,将会导出key所在的对象,而原始的类名和相应的值都会转换为这个对象,而最后将会export导出 css对象
declare module "*.css"{ // 在我们在import 以css为后缀的文件都会遵循以下的约定 const css:{[key:string]: string} export default css }
-
.css
文件的文件命名规则:xxx.module.css
xxx.module.css
是css模块化的命名约定 -
在
.tsx
的文件中使用css文件import style form './index.module.css' function App() { const [list, setList] = useState([]) useEffect(()=> { fetch('xxxx').then(async res => { if(res.ok){ setList(await res.json()) }else{ return Promise.reject() } }) },[]) return ( <div className={style.list}> { list.map((item,i) => ( <div className={i%2>0? style.odd: style.even}> {item.name} </div> ))} </div> ) }
-
安装
typescript-plugin-css-modules
插件,生成css
文件所对应的引用类型(ts比js多了类型约束的优点,给css对象添加对应的引用类型)npm i typescript-plugin-css-modules --save-dev
-
配置
typescript-plugin-css-modules
文件,打开tsconfig.json
,在compilerOptions
中添加plugins
字段,注册启用这个插件"plugins": [ { "name": "typescript-plugin-css-modules" } ]
2. emotion
-
安装
emotion
依赖:@emotion/react
和@emotion/styled
npm i @emotion/react @emotion/styled
-
在
index.tsx
中使用emotion
有一段行内样式
import { Card } from 'antd' export const LoginCard = () => { return ( <div style={{ display: "flex", justifyContent: "center" }}> <Card> <button>登录</button> </Card> </div> ) }
转换为
emotion
- 原生html标签的用法:
styled.div
- 组件标签的用法:
styled(Card)
import { Card } from 'antd' import styled from '@emotion/styled' export const LoginCard = () => { return ( <Container> <ShadowCard> <button>登录</button> </ShadowCard> </Container> ) } const Container = styled.div` display: flex; flex-direction: column; align-items: center; min-height: 100vh; ` const ShadowCard = styled(Card)` width: 400px; min-height: 400px; box-shadow: rgba(0,0,0,0.1) 0 0 10px; `
- 原生html标签的用法:
-
设置主题变量
-
在入口文件中使用
ThemeProvider
,进行主题注入import ReactDOM from 'react-dom/client' import { ThemeProvider } from "@emotion/react" // 导入provider import { RouterProvider } from 'react-router-dom' // 导入router实例 import { router } from './router/index.tsx' const theme = { // 颜色定义规范 colorBg: '#f6f6f6', colorBgW: '#fff', colorBgGray: '#ccc', colorFont: '#333', colorFontLight: '#666', colorFontActive: ' #409eff', } ReactDOM.createRoot(document.getElementById('root')!).render( <ThemeProvider theme={theme}><RouterProvider router={router} /></ThemeProvider> )
-
组件中使用
export const ListItem = ({ title, name, amount }) => { return <ItemWrap> </ItemWrap> } const ItemWrap = styled.div` overflow: hidden; padding: 10px 0; background: ${p => p.theme.colorBgW}; border-radius: 6px; `
-
styled引用主题变量时,ts类型推断报错 font-size: ${p => p.theme.fontSizeM};
*.d.tsimport '@emotion/react'; declare module '@emotion/react' { export interface Theme { colorBg: string; colorBgW: string; colorBgGray: string; colorFont: string; colorFontLight: string; colorFontActive: string; fontSize: string; fontSizeS: string; fontSizeM: string; fontSizeL: string; fontSizeLX: string; colorTheme: string; colorFontPrimary: string; } } `
3. 将styled-components中的px转换为vw
使用postcss-px-to-viewport将px转换为vw做适配时,styled组件中的px不能进行转换
-
安装
babel-plugin-styled-components-px2vw
yarn add babel-plugin-styled-components-px2vw -D
-
配置
vite.config.js
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
import postcsspxtoviewport from 'postcss-px-to-viewport'
const param = {
unitToConvert: 'px', // 默认值`px`,需要转换的单位
viewportWidth: 375, // 视窗的宽度,对应设计稿宽度
// viewportHeight: 667, // 视窗的高度, 根据375设备的宽度来指定,一般是667,也可不配置
unitPrecision: 6, // 指定`px`转换为视窗单位值的小数位数
propList: ['*'], // 转化为vw的属性列表 propList: ["*","!letter-spacing"],这表示:所有css属性的属性的单位都进行转化,除了letter-spacing的
viewportUnit: 'vw', // 指定需要转换成视窗单位
fontViewportUnit: 'vw', // 字体使用的视窗单位
selectorBlackList: ['ignore'], // 指定不需要转换为视窗单位的类 selectorBlackList: ['wrap'],它表示形如wrap,my-wrap,wrapper这样的类名的单位,都不会被转换
mediaQuery: false, // 允许在媒体查询中转换`px`
minPixelValue: 1, // 小于或等于`1px`时不转换为视窗单位
replace: true, // 是否直接更换属性值而不添加备用属性
landscape: false // 是否处理横屏情况 是否添加根据landscapeWidth生成的媒体查询条件 @media (orientation: landscape)
// exclude: [/node_modules/], // 忽略某些文件夹下的文件或特定文件 用正则做目录名匹配
// landscapeUnit: "vw", // 横屏时使用的单位
// landscapeWidth: 1134 // 横屏时使用的视窗宽度
}
export default defineConfig({
plugins: [react({
babel: {
plugins: [
[
'babel-plugin-styled-components-px2vw',
param
]
]
},
})],
// css
css: {
// postCss 配置https://vitejs.cn/config/#css-modules postCss vite内联了,所以pistcss的配置直接在vite.config.ts中配置,不需要额外新建 postcss.config.ts文件
postcss: {
plugins: [
postcsspxtoviewport(param),
]
},
}
})