如何把 CSS 变量全局化

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

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

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

  • 使用了 gulp 来批处理文件,手动是不可能手动的。
  • 使用 craco-plugin-style-resources-loader 来做 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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,670评论 5 460
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,928评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,926评论 0 320
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,238评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,112评论 4 356
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,138评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,545评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,232评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,496评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,596评论 2 310
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,369评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,226评论 3 313
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,600评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,906评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,185评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,516评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,721评论 2 335