zl程序教程

您现在的位置是:首页 >  系统

当前栏目

浅析如何给css加上模块的功能以及CSS Modules用法介绍

模块CSS 如何 介绍 功能 用法 以及 浅析
2023-09-11 14:19:54 时间

一、如何给 css 加上模块的功能呢?

  我们知道,浏览器里的 JS 之前没有模块的概念,都是通过不同的全局变量(命名空间)来隔离,后来出现了 AMD、CMD、CommonJS、ESM 等规范。

  通过这些模块规范组织的 JS 代码经过编译打包之后,运行时依然会有模块级别的作用域隔离(通过函数作用域来实现的)。

  组件就可以放在不同的模块中,来实现不同组件的 JS 的作用域隔离。

  但是组件除了 JS 还有 CSS 呀,CSS 却一直没有模块隔离的规范,如何给 css 加上模块的功能呢?

  有人说 CSS 不是有 @import 吗?那个只是把不同的 CSS 文件合并到一起,并不会做不同 CSS 的隔离。

  CSS 的隔离主要有两类方案,一类是运行时的通过命名区分,一类是编译时的自动转换 CSS,添加上模块唯一标识。

1、运行时的方案最典型的就是 BEM,它是通过 .block__element--modifier 这种命名规范来实现的样式隔离,不同的组件有不同的 blockName,只要按照这个规范来写 CSS,是能保证样式不冲突的。

  但是这种方案毕竟不是强制的,还是有样式冲突的隐患。

2、编译时的方案有两种,一种是 scoped,一种是 css modules。

(1)scoped 是 vue-loader 支持的方案,它是通过编译的方式在元素上添加了 data-xxx 的属性,然后给 css 选择器加上[data-xxx] 的属性选择器的方式实现 css 的样式隔离。

  通过给 css 添加一个全局唯一的属性选择器来限制 css 只能在这个范围生效,也就是 scoped 的意思。

(2)css-modules 是 css-loader 支持的方案,在 vue、react 中都可以用,它是通过编译的方式修改选择器名字为全局唯一的方式来实现 css 的样式隔离。

<style module> 
.guang {
    color: red; 
} 
</style>  
<template>
    <p :class="$style.guang">hi</p>  
</template>
// 会被编译成:
<style module>
._1yZGjg0pYkMbaHPr4wT6P__1 { 
    color: red; 
} 
</style> 
<template> 
    <p class="_1yZGjg0pYkMbaHPr4wT6P__1">hi</p> 
</template>

  和 scoped 方案的区别是 css-modules 修改的是选择器名字,而且因为名字是编译生成的,所以组件里是通过 style.xx 的方式来写选择器名。

3、两种方案都是通过编译实现的,但是开发者的使用感受还是不太一样的:

  scoped 的方案是添加的 data-xxx 属性选择器,因为 data-xx 是编译时自动生成和添加的,开发者感受不到。

  css-modules 的方案是修改 class、id 等选择器的名字,那组件里就要通过 styles.xx 的方式引用这些编译后的名字,开发者是能感受到的。但是也有好处,配合编辑器可以做到智能提示。

4、此外,除了 css 本身的运行时、编译时方案,还可以通过 JS 来组织 css,利用 JS 的作用域来实现 css 隔离,这种是 css-in-js 的方案。

5、css-modules 的编译时方案是用的最多的,vue、react 都可以用,那它是怎么实现的呢?

  打开 css-loader 的 package.json,你会发现依赖了 postcss(css 的编译工具,类似编译 js 的babel):

  其中这四个 postcss-modules 开头的插件就是实现 css-modules 的核心代码。

  这四个插件里,实现作用域隔离的是 postcss-modules-scope,其他的插件不是最重要的,比如 postcss-modules-values 只是实现变量功能的。

  所以说,我们只要能实现 postcss-modules-scope 插件,就能搞懂 css-modules 的实现原理了。具体实现代码可看这篇文章:https://mp.weixin.qq.com/s/3xhNcfI4pTm0n-naSsOBwg

二、CSS Modules 用法

  学过网页开发就会知道,CSS 不能算编程语言,只是网页样式的一种描述方法。为了让 CSS 也能适用软件工程方法,程序员想了各种办法,让它变得像一门编程语言。从最早的Less、SASS,到后来的 PostCSS,再到最近的 CSS in JS,都是为了解决这个问题。

  而 CSS Modules 有所不同。它不是将 CSS 改造成编程语言,而是功能很单纯,只加入了局部作用域和模块依赖,这恰恰是网页组件最急需的功能。

1、局部作用域:默认就是局部作用域

import style from './App.css';
<h1 className={style.title}>Hello World</h1>

  其实就是需要用导入的 style.title 去设置 class 类名即可

2、全局作用域

  CSS Modules 允许使用:global(.className)的语法,声明一个全局规则。凡是这样声明的class,都不会被编译成哈希字符串。

import styles from './App.css';
<h1 className="title">Hello World</h1>

  这个就可以导入 styles 然后直接写 title

3、可定制hash类名

4、class组合

  在 CSS Modules 中,一个选择器可以继承另一个选择器的规则,这称为"组合"("composition")

//在App.css中,让.title继承.className
.className {
  background-color: blue;
}
.title {
  composes: className;
  color: red;
}

5、选择器也可以继承其他CSS文件里面的规则

// another.css
.className {
  background-color: blue;
}
// App.css可以继承another.css里面的规则。
.title {
  composes: className from './another.css';
  color: red;
}

6、输入变量

  CSS Modules 支持使用变量,不过需要安装 PostCSS 和 postcss-modules-values

npm install --save postcss-loader postcss-modules-values

  把postcss-loader加入webpack.config.js

// 接着,在colors.css里面定义变量
@value blue: #0c77f8;
@value red: #ff0000;
@value green: #aaf200;

// App.css可以引用这些变量
@value colors: "./colors.css";
@value blue, red, green from colors;
.title {
  color: red;
  background-color: blue;
}

  具体使用详见《阮一峰:CSS Modules 用法教程》https://www.ruanyifeng.com/blog/2016/06/css_modules.html