当前比较流行的 CSS-in-JS 项目有 styled-jsx 和 styled-components 两个,说实话 styled-jsx 可以通过插件支持 PostCSS 差不多已经完美,使用 styled-components 的话开发思路需要一些转变,相对 styled-jsx 来说有些繁琐(支持 css prop 但有点内联 style 的意思),倒是符合 React 的组件哲学。
create-react-app 工程中支持 styled-jsx 有两种方式,一是使用 babel macro ,一是使用 babel plugin,这两种方式都有一些问题,下面分别进行说明。
使用 babel macro create-react-app 原生支持 babel macro,而 styled-jsx 自身也支持 babel macro,因此不需要对工程做任何配置即可以直接 styled-jsx,示例如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 import classNames from 'classnames' ;import {resolve} from 'styled-jsx/macro' ;const example = resolve` .App { text-align: center; } .App-logo { animation: App-logo-spin infinite 20s linear; height: 40vmin; pointer-events: none; } .App-header { background-color: #282c34; min-height: 100vh; display: flex; flex-direction: column; align-items: center; justify-content: center; font-size: calc(10px + 2vmin); color: white; } .App-link { color: #61dafb; } @keyframes App-logo-spin { from { transform: rotate(0deg); } to { transform: rotate(360deg); } } ` ;class App extends Component { render ( ) { return ( <div className ={classNames(example.className, "App ")}> <header className ={classNames(example.className, "App-header ")}> <img src ={logo} className ={classNames(example.className, "App-logo ")} alt ="logo" /> <p > Edit <code > src/App.js</code > and save to reload. </p > <a className ={classNames(example.className, "App-link ")} href ="https://reactjs.org" target ="_blank" rel ="noopener noreferrer" > Learn React </a > </header > {example.styles} </div > ); } }
使用 bable macro 存在如下几个问题:
显然这种使用 resolve 定义样式的方式与我们所熟知的 <style jsx>...</style>
方式不一致;
使用类选择器时需要显式使用 classNames 进行连接;
不能使用 styled-jsx 插件[1],这样使用 styled-jsx 的意义就大大降低了;
对 typescript 的支持不好,当前 ‘styled-jsx/macro’ 和 ‘styled-jsx/style’ 两个模块在 @types/styled-jsx 包中均未定义,因此如有需要,需要自己定义 .d.ts
文件;
可以为 CRA 工程简单的定义所需的 .d.ts
文件以解决编译错误:
1 2 3 ~$ vi src/example.d.ts declare module 'styled-jsx/macro' declare module 'styled-jsx/style'
当然,styled-components 也可以以 babel macro 的方式使用,并使用 polished 来实现类似 PostCSS 提供的功能。
使用 babel plugin 这是熟悉的 styled-jsx 使用方式:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 class App extends Component { render ( ) { return ( <div className ="App" > <header className ="App-header" > <img src ={logo} className ="App-logo" alt ="logo" /> <p > Edit <code > src/App.js</code > and save to reload. </p > <a className ="App-link" href ="https://reactjs.org" target ="_blank" rel ="noopener noreferrer" > Learn React </a > </header > { /*language=CSS*/ } <style jsx > {` .App { text-align : center; } .App-logo { animation : App-logo-spin infinite 20s linear; height : 40vmin ; pointer-events : none; } .App-header { background-color : #282c34 ; min-height : 100vh ; display : flex; flex-direction : column; align-items : center; justify-content : center; font-size : calc (10px + 2vmin ); color : white; } .App-link { color : #61dafb ; } @keyframes App-logo-spin { from { transform : rotate (0deg ); } to { transform : rotate (360deg ); } } `} </style > </div > ); } }
但此时需要 eject 以支持 styled-jsx 配置(注意:eject 要求 git 工程工作目录是 clean 的):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 ~$ yarn eject ~$ vi config/webpack.config.js { test : /\.(js|mjs|jsx|ts|tsx)$/, include: paths.appSrc, loader: require.resolve('babel-loader' ), options: { customize: require.resolve( 'babel-preset-react-app/webpack-overrides' ), plugins: [ [ require.resolve('babel-plugin-named-asset-import' ), { loaderMap: { svg: { ReactComponent: '@svgr/webpack?-prettier,-svgo![path]' , }, }, }, ], ['styled-jsx/babel' , { "plugins" : ["styled-jsx-plugin-postcss" ] }], ], ... },
此处添加了 PostCSS 插件,因此可以使用各种 PostCSS 的特性:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 ~$ yarn add styled-jsx-plugin-postcss ~$ yarn add postcss-easy-media-query ~$ vi postcss.config.js const postcssEasyMediaQuery = require(`postcss-easy-media-query`); module.exports = () => ({ plugins: [ postcssEasyMediaQuery({ breakpoints: { tablet: 600, desktop: 1024 } }), ], });
可惜的是,在 react-app-rewired 逐渐淡出的情况下,yarn eject 显得有些无奈。
混合使用 babel macro 与 babel plugin 在某些特殊需求下,可能会用到:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 import classNames from 'classnames' ;import {resolve} from 'styled-jsx/macro' ;import _JSXStyle from 'styled-jsx/style' ;const example = resolve` .App { text-align: center; } .App-logo { animation: App-logo-spin infinite 20s linear; height: 40vmin; pointer-events: none; } ` ;class App extends Component { render ( ) { return ( <div className ={classNames(example.className, "App ")}> <header className ="App-header" > <img src ={logo} className ={classNames(example.className, "App-logo ")} alt ="logo" /> <p > Edit <code > src/App.js</code > and save to reload. </p > <a className ="App-link" href ="https://reactjs.org" target ="_blank" rel ="noopener noreferrer" > Learn React </a > </header > {example.styles} { /*language=CSS*/ } <style jsx > {` .App-header { background-color : #282c34 ; min-height : 100vh ; display : flex; flex-direction : column; align-items : center; justify-content : center; font-size : calc (10px + 2vmin ); color : white; } .App-link { color : #61dafb ; } @keyframes App-logo-spin { from { transform : rotate (0deg ); } to { transform : rotate (360deg ); } } `} </style > </div > ); } }
注意不要删除 import _JSXStyle
这条没有用到的 import 指令,否则会出现类似如下的报错:
1 2 3 4 5 6 Module parse failed: Identifier '_JSXStyle' has already been declared (8:7) You may need an appropriate loader to handle this file type . | var _jsxFileName = "/home/runsisi/workingcopy/test/app/src/App.js" ; | import _JSXStyle from "styled-jsx/style" ; > import _JSXStyle from "styled-jsx/style" ; | import React, { Component } from 'react' ;
参考资料 [1] Can you use Styled JSX plugins with Babel macros?
https://spectrum.chat/styled-jsx/general/can-you-use-styled-jsx-plugins-with-babel-macros~2d2331c7-07d6-4bef-a7df-dab24fc0055b
[2] Static Type Checking
https://reactjs.org/docs/static-type-checking.html#typescript
[3] A starter template for TypeScript and React
https://github.com/Microsoft/TypeScript-React-Starter#typescript-react-starter
[4] Creating TypeScript typings for existing React components
https://templecoding.com/blog/2016/03/31/creating-typescript-typings-for-existing-react-components/
[5] Clarify how to add a new declaration file
https://github.com/Microsoft/TypeScript/issues/21344
[6] Adding Custom Type Definitions to a Third-Party Library
https://www.detroitlabs.com/blog/2018/02/28/adding-custom-type-definitions-to-a-third-party-library/