React基础前端

如何把 CSS 变量全局化

2020-10-01  本文已影响0人  CondorHero

已经在知乎混着发了三篇文章了,就快要熟悉了 !😂。

前言: 今天是十月一号,没错就是国庆假期的第一天,本该今天在回家的路上,却在在公司码字整理博客。可伶没有候补到回家的票 🎫。一直以为只要添加候补,最后 12306 都能给票,结果发现是我太天真了,被 12306 着实教训了一把,还好早上六点多买了明天的。真是可惜,我以为我会比我给我妈买的手机 📱 先到家,还是我慢了一步 😂。

正文: 今天主要是写一个 npm 包,所以如果你还不回发布 npm 的话,请查看文章 如何发布一个 npm 依赖包 。最近没开发新的项目,忙着改一些 UAT 的 bug,所以有点时间去捣鼓项目框架的问题,然后最近我利用空闲点的时间,成功切入到另一个项目,把 scss 改成 less ,被做完变量的全局化,然后事了拂衣去,深藏功与名 😂。讲下思路,在我升级项目的时候:

为什么我要执着,去做 less 变量全局化呢?

原因是,一般我们的项目为了风格统一,例如「字体 | 按钮」大小、颜色、等等,往往是按一套标准的设计的,样式管理我们又能细分为两种:

  1. 公共类名来管理:例如某些按钮除了字不一样,大小颜色等完全一样,这时候我们会把它的样式写成公共样式,采用公共类名来管理
  2. 全局变量来管理:网页的颜色、字体等部分统一的样式,可以采用全局变量来管理。

但是通过全局变量来管理,有个缺点就是每次使用全局变量必须手动 @import ,这当然是 CSS 作用域影响的,我们无法改变这一点,这可就有点难受了。

变量全局化的解决思路

在写 React 项目的时候,我向来都是遇事不决, Vue 哲学。Vue 肯定有解决方案,果然:vue-cli => 向预处理器 Loader 传递选项 哇塞,直接支持 🤪,天底下还有比 Vue 更好用的框架吗 😂, 无奈用的 React 🤷‍♀️。

好歹有 Vue,作为参考,我们在 React 根据 Vue-cli 的 CSS 相关 自动化导入 篇里提到的 style-resources-loader 去研究。

一看介绍,哇咔咔这个东西就是我们想要的:

  1. share your variables, mixins, functions across all style files, so you don't need to @import them manually.
  2. override variables in style files provided by other libraries (e.g. ant-design) and customize your own theme.

而且 Vue 都给我们封装好了,vue-cli-plugin-style-resources-loader React 又默默留下了泪水 😭。

根据 style-resources-loader 的使用说明:

CSS processor resources loader for webpack.

需要配合 webpack 如果项目是手配的就好了,可惜我用的 React 是通过 craco 来配置 webpack 的,好不容易找到了 craco-style-resources-loader 可惜只支持 CSS,看了这个库的灵感来源:

This is a craco plugin to add style-resources-loader with create-react-app version >= 2.
Inspired by: https://github.com/tilap/craco-sass-resources-loader

好嘛也支持 SCSS:craco-sass-resources-loader

结果死活找不到 less 版本,不过既然 craco-style-resources-loader 都能根据 craco-sass-resources-loader 来模仿实现,那我也试试呗,毕竟去看了源码还是挺简单的。

未解之谜

动手解决问题之前这里有个未解之谜。

Antd 在 create-react-app 中使用 => 自定义主题 教程里面讲:

引入 craco-less 来帮助加载 less 样式和修改变量,通过 lessmodifyVars 提供给 less-loader 达到变量覆盖功能。这是用到了 less 的 modifyVars。

但是 [less option](http://lesscss.org/usage/#less-options) 还有一个 Global Variables 属性可以给 less 注入全局变量。我配置了,但是并不能使用,估计是 craco 的导致的。

modifyVars 和 Global Variables 的使用参考:使用webpack+vue+less开发,使用less-loader,配置全局less变量

动手解决问题

我们的思路是给 webpack 配置 style-resources-loader 插件,让项目支持 CSS 变量的全局化。 我 React 项目中用到的是 Antd 推荐的一个库 craco 来配置 webpack 的。所以我们现在要做的就是借助 craco 的 API 来重写 webpack,把 style-resources-loader 插件加上去。在动手之前需要了解前置知识:

虽然是英文写的,但是很简单。要想添加一个新插件,我们需要新建一个文件:例如 craco-plugin-log-craco-config.js 。然后在 craco 的配置文件 craco.config.js 里面使用。craco-plugin-log-craco-config.js 文件里面的函数 overrideCracoConfig 函数第一个形参是 webpack 的配置,第二个形参是在craco.config.js 使用插件传过来的一些参数。

好了,接下来我给我们要发布到 npm 的库起名:craco-plugin-style-resources-loader 👍

借助 overrideWebpackConfig 函数配置 webpack 的 loader ,代码如下:

module.exports = {
    overrideWebpackConfig: ({ webpackConfig, pluginOptions }) => {
        /* ============== start 校验传进来的参数是否正确 =============== */ 
        // Check webpack config
        if (
            !webpackConfig ||
            !webpackConfig.module ||
            !webpackConfig.module.rules ||
            typeof webpackConfig.module.rules !== 'object'
        ) {
            throw new Error('craco-sass-resources-loader error: no valid webpackConfig.module.rules');
        };

        // Check variable pluginOptions
        if ( Object.prototype.toString.call(pluginOptions) !== '[object Object]' ) {
            throw new Error('craco-sass-resources-loader error: variable pluginOptions should is an object');
        };

        // Check styleType property
        const mapStyleType = [ 'stylus', 'css', 'scss', 'sass', 'less' ];
        const styleType = pluginOptions.styleType;
        if ( typeof styleType !== 'string' ) {
            throw new Error('craco-sass-resources-loader error: variable styleType is not a string');
        } else if (!mapStyleType.includes(styleType)) {
            throw new Error('craco-sass-resources-loader error: The value of variable styleType can only be 「stylus | css | scss | sass | less」');
        };

        /* ============== end =============== */ 

        /* ============== start 如果你熟悉 webpack 就知道我们此处是找到 loader 配置 oneOf 参数,加上 style-resources-loader 插件=============== */ 
        // Add the loader rule where needed
        const output = { ...webpackConfig };
        Object.keys(output.module.rules).forEach((ruleKey, ruleIndex) => {
            const rule = output.module.rules[ruleKey];
            if (Object.prototype.hasOwnProperty.call(rule, 'oneOf')) {
                rule.oneOf.forEach((oneOf, oneOfIndex) => {
                    if ( oneOf.test && oneOf.use ) {
                        let useCssProcessor = false;

                        switch ( styleType ) {
                            case 'scss':
                            case 'sass':
                                useCssProcessor = `${oneOf.test}`.includes('scss') || `${oneOf.test}`.includes('sass');
                                break;
                            case 'css':
                                useCssProcessor = `${oneOf.test}`.includes('css') || `${oneOf.test}`.includes('style');
                                break;
                            default:
                                useCssProcessor = `${oneOf.test}`.includes(styleType);
                                break;
                        };

                        if ( useCssProcessor ) {
                            const options = {};

                            // more option go link: https://www.npmjs.com/package/style-resources-loader#patterns
                            pluginOptions.patterns && (options.patterns = pluginOptions.patterns);
                            pluginOptions.injector && (options.injector = pluginOptions.injector);
                            pluginOptions.globOptions && (options.globOptions = pluginOptions.globOptions);
                            pluginOptions.resolveUrl && (options.resolveUrl = pluginOptions.resolveUrl);

                            output.module.rules[ruleIndex].oneOf[oneOfIndex].use.push({
                                loader: 'style-resources-loader',
                                options,
                            });
                        };
                    };
                });
            };
        });
        /* ============== end =============== */
        return output;
    }
};

上面给支持了 scss/sass/css/less, 然后可以直接发布到 npm 上。

安装之后,使用也很简单,如果你只有一个文件可以在 craco.config.js 里面这么使用 :patterns 参数为字符串就行

const cracoPluginStyleResourcesLoader = require('craco-plugin-style-resources-loader');
const path = require('path');

module.exports = {
    plugin: cracoLessResourcesLoader,
    options: {
        patterns: path.join(__dirname, 'src/less/common.less'),
        /* 
            Please enter supported CSS processor type
            1. if u use css processor,please type css string
            2. if u use less processor,please type less string
            3. if u use sass or scss processor,please type sass or scss string,Choose one of the two
            4. if u use stylus processor,please type stylus string
        */
        styleType: 'less'
    }
};

如果有多个文件需要变量全局化 📃, patterns 参数为数组就行:

const cracoPluginStyleResourcesLoader = require('craco-plugin-style-resources-loader');
const path = require('path');

module.exports = {
    plugin: cracoLessResourcesLoader,
    options: {
        patterns: [
            path.join(__dirname, 'src/less/common.less'),
            path.join(__dirname, 'src/less/global.less')
        ],
        /* 
            Please enter supported CSS processor type
            1. if u use css processor,please type css string
            2. if u use less processor,please type less string
            3. if u use sass or scss processor,please type sass or scss string,Choose one of the two
            4. if u use stylus processor,please type stylus string
        */
        styleType: 'less'
    }
};

options 还有另外有些配置,其参数都来自 👉 style-resources-loader

不知道 npm 下载的统计准不准,就发布了一天现在下载已经 158 了 👍。

下载统计

完 ~~~

写作时间 Thursday, October 1, 2020 16:54:15

上一篇下一篇

猜你喜欢

热点阅读