React 项目的css-in-js

React 项目的css-in-js(简称JSS)

TS的定义声明

  • 文件名为:*.d.ts
  • 只包含类型声明,不包含逻辑
  • 不会被编译,也不会被webpack、vite打包

1. css modules(配置typescript-plugin-css-modules完成类型申明)

  1. 定义css的类型申明文件 custom.d.ts

    custom.d.ts文件中定义声明,需要用到ts的两个关键字:declaremodule, 声明的对象就是css文件

    在我们在import 以css为后缀的文件都会遵循以下的约定,约定的内容是,将会导出key所在的对象,而原始的类名和相应的值都会转换为这个对象,而最后将会export导出 css对象

    declare module "*.css"{ // 在我们在import 以css为后缀的文件都会遵循以下的约定
    	const css:{[key:string]: string}
      export default css
    }
    
  2. .css文件的文件命名规则: xxx.module.css

    xxx.module.css是css模块化的命名约定

  3. .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>
      )
    }
    
  4. 安装typescript-plugin-css-modules 插件,生成css文件所对应的引用类型(ts比js多了类型约束的优点,给css对象添加对应的引用类型)

    npm i typescript-plugin-css-modules --save-dev
    
  5. 配置 typescript-plugin-css-modules文件,打开tsconfig.json,在compilerOptions中添加plugins字段,注册启用这个插件

    "plugins": [
      { "name": "typescript-plugin-css-modules" }
    ]
    

2. emotion

  1. 安装 emotion依赖:@emotion/react@emotion/styled

    npm i @emotion/react @emotion/styled
    
  2. 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;
    `
    
  3. 设置主题变量

  • 在入口文件中使用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.ts

    import '@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不能进行转换

  1. 安装 babel-plugin-styled-components-px2vw

    yarn add babel-plugin-styled-components-px2vw -D
    
  2. 配置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),
      ]
    },
  }
})

posted @ 2021-12-26 14:56  shine_lovely  阅读(265)  评论(0编辑  收藏  举报