资讯专栏INFORMATION COLUMN

CSS Module

mingzhong / 3230人阅读

摘要:样式变多后,命名将更加混乱。缺点是不能利用成熟的预处理器或后处理器,和伪类处理起来复杂。经过这样混淆处理后,名基本就是唯一的,大大降低了项目中样式覆盖的几率。就只能使用预处理器自己的语法来做样式复用了。

一 前言

CSS 是前端领域中进化最慢的一块。由于 ES2015/2016 的快速普及和 Babel/Webpack 等工具的迅猛发展,CSS 被远远甩在了后面,逐渐成为大型项目工程化的痛点。也变成了前端走向彻底模块化前必须解决的难题。
模块化解决了JS作用域的问题,但是CSS还是会存在样式覆盖的问题,因为最后打包最终会生成一份文件而不是开发时候的那样分模块分作用域。
所以,我们今天讨论的是如何在模块化的工程下放心的写CSS样式而不担心样式覆盖,推荐的方案就是CSS Module。

二 正文1.CSS 模块化遇到了哪些问题

CSS 模块化重要的是要解决好两个问题:CSS 样式的导入和导出。

样式导入:灵活按需导入以便复用代码;

样式导出:导出时要能够隐藏内部作用域,以免造成全局污染。

Sass/Less/PostCSS 等前仆后继试图解决 CSS 编程能力弱的问题,结果它们做的也确实优秀,但这并没有解决模块化最重要的问题。Facebook 工程师 Vjeux 首先抛出了 React 开发中遇到的一系列 CSS 相关问题。总结如下:

全局污染

CSS 使用全局选择器机制来设置样式,优点是方便重写样式。缺点是所有的样式都是全局生效,样式可能被错误覆盖,因此产生了非常丑陋的 !important,甚至 inline !important 和复杂的选择器权重计数表,提高犯错概率和使用成本。
Web Components 标准中的 Shadow DOM 能彻底解决这个问题,但它的做法有点极端,样式彻底局部化,造成外部无法重写样式,损失了灵活性。

命名混乱

由于全局污染的问题,多人协同开发时为了避免样式冲突,选择器越来越复杂,容易形成不同的命名风格,很难统一。样式变多后,命名将更加混乱。

依赖管理不彻底

组件应该相互独立,引入一个组件时,应该只引入它所需要的 CSS 样式。但现在的做法是除了要引入 JS,还要再引入它的 CSS,而且 Saas/Less 很难实现对每个组件都编译出多带带的 CSS,引入所有模块的 CSS 又造成浪费。JS 的模块化已经非常成熟,如果能让 JS 来管理 CSS 依赖是很好的解决办法。
Webpack 的 css-loader 提供了这种能力。

无法共享变量

复杂组件要使用 JS 和 CSS 来共同处理样式,就会造成有些变量在 JS 和 CSS 中冗余,Sass/PostCSS/CSS 等都不提供跨 JS 和 CSS 共享变量这种能力。

代码压缩不彻底

由于移动端网络的不确定性,现在对 CSS 压缩已经到了{{BANNED}}的程度。很多压缩工具为了节省一个字节会把 "16px" 转成 "1pc"。但对非常长的 class 名却无能为力,力没有用到刀刃上。

上面的问题如果只凭 CSS 自身是无法解决的,如果是通过 JS 来管理 CSS 就很好解决,因此 Vjuex 给出的解决方案是完全的 CSS in JS,但这相当于完全抛弃 CSS,在 JS 中以 Object 语法来写 CSS,估计刚看到的小伙伴都受惊了。直到出现了 CSS Modules。

2.CSS解决方案一览

CSS 模块化的解决方案有很多,但主要有两类:

彻底抛弃 CSS,使用 JS 或 JSON 来写样式

Radium,jsxstyle,react-style 属于这一类。
优点是能给 CSS 提供 JS 同样强大的模块化能力。
缺点是不能利用成熟的 CSS 预处理器(或后处理器),Sass/Less/PostCSS,:hover 和 :active 伪类处理起来复杂。

另一类是依旧使用 CSS,但使用 JS 来管理样式依赖,

代表是 CSS Modules。
CSS Modules 能最大化地结合现有 CSS 生态和 JS 模块化能力,API 简洁到几乎零学习成本。
发布时依旧编译出多带带的 JS 和 CSS。它并不依赖于 React,只要你使用 Webpack,可以在 Vue/Angular/jQuery 中使用。

3.为什么是CSS Module

CSS Modules 能最大化地结合现有 CSS 生态和 JS 模块化能力,API 简洁到几乎零学习成本。

发布时依旧编译出多带带的 JS 和 CSS。它并不依赖于 React,只要你使用 Webpack,可以在 Vue/Angular/jQuery 中使用。

4.使用CSS Module

CSS Modules 内部通过 ICSS 来解决样式导入和导出这两个问题。分别对应 :import 和 :export 两个新增的伪类。

:import("path/to/dep.css") {
  localAlias: keyFromDep;
  /* ... */
}
:export {
  exportedKey: exportedValue;
  /* ... */
}

但直接使用这两个关键字编程太麻烦,实际项目中很少会直接使用它们,我们需要的是用 JS 来管理 CSS 的能力。结合 Webpack 的 css-loader 后,就可以在 CSS 中定义样式,在 JS 中导入。

启用 CSS Modules

// webpack.config.js
css?modules&localIdentName=[name]__[local]-[hash:base64:5]

加上 modules 即为启用,localIdentName 是设置生成样式的命名规则。
也可以这样设置:

         test: /.less$/,
         use: [
                "style-loader",
                {
                    loader: "css-loader",
                    options: {
                        modules: true,
                        localIdentName: "[name]__[local]-[hash:base64:5]",
                    },
                },
               ],
        },

样式文件Button.css:

.normal { /* normal 相关的所有样式 */ }
.disabled { /* disabled 相关的所有样式 */ }
/* components/Button.js */
import styles from "./Button.css";

console.log(styles);

buttonElem.outerHTML = ``

生成的 HTML 是

注意到 button--normal-abc53 是 CSS Modules 按照 localIdentName 自动生成的 class 名。其中的 abc53 是按照给定算法生成的序列码。经过这样混淆处理后,class 名基本就是唯一的,大大降低了项目中样式覆盖的几率。同时在生产环境下修改规则,生成更短的class名,可以提高CSS的压缩率。

上例中 console 打印的结果是:

Object {
  normal: "button--normal-abc53",
  disabled: "button--disabled-def884",
}

CSS Modules 对 CSS 中的 class 名都做了处理,使用对象来保存原 class 和混淆后 class 的对应关系。

通过这些简单的处理,CSS Modules 实现了以下几点:

所有样式都是 local 的,解决了命名冲突和全局污染问题

class 名生成规则配置灵活,可以此来压缩 class 名

只需引用组件的 JS 就能搞定组件所有的 JS 和 CSS

依然是 CSS,几乎 0 学习成本

样式默认局部
使用了 CSS Modules 后,就相当于给每个 class 名外加加了一个 :local,以此来实现样式的局部化,如果你想切换到全局模式,使用对应的 :global。

.normal {
  color: green;
}

/* 以上与下面等价 */
:local(.normal) {
  color: green; 
}

/* 定义全局样式 */
:global(.btn) {
  color: red;
}

/* 定义多个全局样式 */
:global {
  .link {
    color: green;
  }
  .box {
    color: yellow;
  }
}

Compose 来组合样式
对于样式复用,CSS Modules 只提供了唯一的方式来处理:composes 组合

/* components/Button.css */
.base { /* 所有通用的样式 */ }

.normal {
  composes: base;
  /* normal 其它样式 */
}

.disabled {
  composes: base;
  /* disabled 其它样式 */
}

import styles from "./Button.css";

buttonElem.outerHTML = ``

生成的 HTML 变为

由于在 .normal 中 composes 了 .base,编译后会 normal 会变成两个 class。

composes 还可以组合外部文件中的样式。

/* settings.css */
.primary-color {
  color: #f40;
}

/* components/Button.css */
.base { /* 所有通用的样式 */ }

.primary {
  composes: base;
  composes: primary-color from "./settings.css";
  /* primary 其它样式 */
}

对于大多数项目,有了 composes 后已经不再需要 Sass/Less/PostCSS。但如果你想用的话,由于 composes 不是标准的 CSS 语法,编译时会报错。就只能使用预处理器自己的语法来做样式复用了。

class 命名技巧
CSS Modules 的命名规范是从 BEM 扩展而来。

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/113527.html

相关文章

  • Vue中scoped csscss module比较

    摘要:官方文档可以直接在能跑起来的项目中使用。所以在使用的父组件中使用的子组件也要开启。能正常编译,而则会编译的不符合预期,所以平时养成良好的参数书写顺序也很重要。总结总结综上所述,前期进行不麻烦的配置,实现的效果比更优,这里推荐使用。scoped css 官方文档 scoped css可以直接在能跑起来的vue项目中使用。 使用方法: h1 { color: #f00; } 使用scop...

    Faremax 评论0 收藏0
  • react配置less,并使用less module(webpack.config.js配置)

    摘要:网上看了很多配置,最后才发现后就不用就可以使用特性,那么仿照的配置即可的使用将文件名为配置暴露配置文件安装依赖修改文件配置,一共修改三处第一处增加定义变量第二处仿照配置第三处修改函数内部,增加图片描述测试是否安装成功 网上看了很多配置,最后才发现create-react-app2.0后就不用eject就可以使用css module特性,那么less仿照css的配置即可 1.css mo...

    imingyu 评论0 收藏0
  • react配置less,并使用less module(webpack.config.js配置)

    摘要:网上看了很多配置,最后才发现后就不用就可以使用特性,那么仿照的配置即可的使用将文件名为配置暴露配置文件安装依赖修改文件配置,一共修改三处第一处增加定义变量第二处仿照配置第三处修改函数内部,增加图片描述测试是否安装成功 网上看了很多配置,最后才发现create-react-app2.0后就不用eject就可以使用css module特性,那么less仿照css的配置即可 1.css mo...

    sPeng 评论0 收藏0
  • webpack项目轻松混用css module

    摘要:前言前言本文讲述开启模块功能之后,如何与引用的包中样式文件不产生冲突。在不做特殊处理的前提下,样式文件将会被转译成。前言 本文讲述css-loader开启css模块功能之后,如何与引用的npm包中样式文件不产生冲突。 比如antd-mobilenpm包的引入。在不做特殊处理的前提下,样式文件将会被转译成css module。 一、产生问题的原因 { test: /.css$/, ...

    LiuZh 评论0 收藏0
  • 从零开始配置webpack(基于babel 7版本)

    摘要:支持转义转义需要的依赖,支持装饰器。在中增加的配置数组中。压缩文件安装依赖在中的中增加配置打包前先清空输出目录在中增加的配置至此,配置已经基本能满足需求。 webpack 核心概念: Entry: 入口 Module:模块,webpack中一切皆是模块 Chunk:代码库,一个chunk由十多个模块组合而成,用于代码合并与分割 Loader:模块转换器,用于把模块原内容按照需求转换成...

    waltr 评论0 收藏0
  • 一看就懂之webpack基础配置

    摘要:一看就懂之基础配置一简介本质上,是一个现代应用程序的静态模块打包器。属性表示的是的上下文目录,配置入口文件的时候,如果入口文件使用的是相对路径,那么就是相对于所在的目录。通常用于指定以何种方式导出库,通常用于指定接收库的名称。 一看就懂之webpack基础配置 一、webpack 简介 本质上,webpack 是一个现代 JavaScript 应用程序的静态模块打包器(module b...

    woshicixide 评论0 收藏0

发表评论

0条评论

mingzhong

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<