React App Framework - 4. css/theme
css
语言和插件
在React
技术栈中有各种各样的样式使用方案, 常见的如CSS-in-JS
、styled-components
、emotion
、styled-system
等, 但是这些方案有的不够直观, 有的不方便复用, 看了一下还是使用styled-jsx
比较全面, 支持常见的所有功能, 同时也是直接使用css
语法
同时在项目中常用的css
扩展语言是sass
, styled-jsx
也同时支持这种用法, 而我们引入的antd
需要修改主题配色的话, 还需要引入less
. 所以项目最终支持的css
种类为:
styled-jsx
+sass
(组件内用法)sass
(独立文件)less
(仅限antd
)
插件安装
styled-jsx
配置起来非常简单, 只需要在项目中引入styled-jsx
插件即可, 如下:
1 | { |
sass
create-react-app
里面已经默认安装了sass-loader
插件, 安装下sass
模块就可以使用了
1 | npm install -D node-sass |
less
less
是比较复杂的一个模块, 在create-react-app
中默认不安装, 需要手动安装, 由于我们项目已经eject
过, 所以直接给出配置方法
1 | const lessRegex = /\.less$/; |
主题配置
由于我们需要修改antd
的主题色, 同时这个颜色也需要在各处使用, 所以定义一份颜色配置, 在less-loader
里加入即可
这一步问题就来了, 由于我们代码是typescript
, webpack.config.js
只支持js
格式, 所以需要在js
中引入ts
的代码和变量, 这里我们使用typescript-require
来实现, 它的原理是通过修改require.extensions
的方式来实现, 给require.extensions[.ts]
加上一个ts
的处理函数, 这样就可以在js
中使用ts
的代码了
theme-config
1 | // src/config/theme.ts |
1 | // config/theme/get-theme.js |
less
less
在解决了ts
引用的变量问题后, 可以直接使用了
1 | // config/webpack.config.js |
sass
sass
会稍微麻烦一点, 因为sass
的编译不是使用变量配置, 而是使用prependData
的方式注入外部变量, 那就需要把变量对象转化为sass
字符串
1 | // config/theme/flatten-obj-sass.js |
1 | // config/webpack.config.js |
styled-jsx
styled-jsx
的配置会更麻烦点, 需要在styled-jsx
的babel-loader
中注入option
, 所以不能使用package.json
里面的babel.plugins
来注入插件了, 而是需要在webpack.config.js
中自己注入
1 | // config/webpack.config.js |
使用和问题
上面的配置完成后, 看起来就比较完美了, 在代码中可以使用三种方式来设置主题配置
- 使用
js
直接引入, 如color: ${THEME.primary_color};
- 使用
sass
变量, 如color: $primary_color;
- 使用
class
注入, 这是在App.scss
中使用sass
脚本语法自动生成的相关变量的class
, 如className='theme-color-primary_color'
但是在实际开发使用中却碰见了一个大问题: 修改primary_color
的值之后, 使用第二种方式引入的组件颜色不会改变
因为sass
和less
的变量注入都是在编译前注入的, 所以需要重启才能生效, 也是正常流程, 但是styled-jsx
里的sass
变量属性重启后都没效果, 所以猜想是缓存的原因
styled-jsx-sass
缓存问题
仔细排查一番以后发现了原因:
styled-jsx
的编译是通过babel-loader
注入的, 而babel-loader
在编译完成一份文件后, 会将所有编译参数和文件内容作为一个module
放入cache
中, 如果没有任何参数改变, 下次就不会走完整的编译流程, 而是直接使用现成的编译结果. 问题就出现在这里, 我们修改完theme
文件中的primary_color
变量后, 对于babel-loader
编译的组件来说实际上没有任何改变, 所以不会重新编译, 而sass-options
却是在编译时动态注入的, 不重新编译就不会注入新的变量, 所以导致了旧的组件代码修改sass
变量不会生效
排查到具体原因后解决起来就比较简单了, babel-loader
提供了customize
设置, 我们可以将theme
加入对应文件的编译参数即可
1 | // config/webpack.config.js |
1 | // config/theme/theme-cache-babel-custom-loader.js |
小结
这里通过sass
编译时动态注入变量的方式实现了全局统一主题配置, 但仍然存在的问题是不方便做主题切换, 看了一下antd
自己实现的动态主题切换发现是引入了less
页面编译, 这样不太友好而且会导致页面卡顿. 而且看了下sass
貌似没有动态主题切换的功能, 所以目前项目的多主题色只能通过预先设置好的主题配置文件来实现, 如果需要新增一套主题, 则需要新增一份配置以及切换选项, 无法实现自定义主题色的功能
但除开动态自定义主题的问题以外, 其他功能都比较完善了, 比较好用的点有
- 统一变量样式定义文件
jsx
动态注入js
或sass
变量sass
全局变量文件- 通过
className
实现快速样式设置 - 通过全局
className
实现主题切换
CSS variables
css
变量动态切换主题是最新的一种方案, 没有使用它的原因是:
- 即使使用了它, 也只能优化我们
sass
变量的写法, 无法对antd
的less
变量生效 - 某些语法写起来比较别扭, 也不支持如
sass
提供的一系列函数方法, 自定义程度暂时还不够高 - 兼容性不够好
但是感觉这个方法才是未来的方向, 可以实现在全局范围内实时替换所有主题变量, 再也不需要静态编译和修改配置文件了
React App Framework - 4. css/theme
https://mosby-zhou.github.io/2019/10-10-react-app-framework-css/