webpack中的css-loader有什么作用?

10
我开始接触Webpack 2,并发现很多项目都在使用css-loader,但我不知道它的作用是什么。

你有读过 readme.md 吗?https://github.com/webpack-contrib/css-loader - Davin Tryon
3
是的,但我并没有觉得它有帮助。 - ofir fridman
也许你可以进一步说明哪些部分令人困惑? - Davin Tryon
1
Webpack 可以捆绑样式和 JavaScript。 css-loader 允许您在 bundle 中导入 *.css。 Webpack 也会遍历 css 中的 @importurl() - Davin Tryon
为什么我会想要使用它?例如,我理解为什么我会使用sass-loader,它将从scss文件创建css。 - ofir fridman
在sass被编译为css(使用sass-loader)之后,css loader会进行捆绑。据我所知。 - Davin Tryon
2个回答

8

编辑:在原来的回答中,我描述的是style-loader而不是css-loader。很容易忘记这些加载器的不同目的,因为css-loader只能与style-loader一起使用。


新答案

css-loader 让你更好地控制导入 .css 文件。

1. 把 url(image.png) => require('./image.png') 转换

现在使用 require,它使你能够使用例如 file-loaderurl-loader。 现在 url(image.png) 可以转换为:

url(/public-path/0dcbbaa701328a3c262cfd45869e351f.png)

或者使用 url-loader 的限制属性可以创建内联图片:

url(data:image/jpeg;base64,LzlqLzRBQ ... zdF3)

2. 支持CSS Modules

让我们考虑componentAcomponentB的样式:

componentA/style.css

.wrapper {
  background-color: blue;
}
.specificToComponentA {
  // rest of styles
}

componentB/style.css

.wrapper {
  background-color: red;
}
.specificToComponentB {
  // rest of styles
}

componentA 的外观:

import './style.css';

export default function () {
  document.body.innerHTML = `
    <div class="wrapper">
      <div class="specificToComponentA">componentA</div>
    </div>
  `;
}

componentB 看起来是这样的:

import './style.css';

export default function () {
  document.body.innerHTML = `
    <div class="wrapper">
      <div class="specificToComponentB">componentB</div>
    </div>
  `;
}

What color of background color those components would have? This problem is related with leaking of styles, it's hard to tell if they are will be red or blue (it's hard to predict order of styles created by style-loader). If you use CSS Modules approach you can deal with this problem. Now import styles to variable and this variable will contain object with mapping class names: componentA with CSS Modules looks:
import s from './style.css';

export default function () {
  document.body.innerHTML = `
    <div class="${s.wrapper}">
      <div class="${s.specificToComponentA}">componentA</div>
    </div>
  `;
}

s对象将包含:

{
  wrapper: "WO0HHIhH77",
  specificToComponentA: "jPYPsVTDZu"
}

componentA/style.css将被转换为

.WO0HHIhH77 {
  background-color: blue;
}
.jPYPsVTDZu {
  // rest of styles
}

使用 CSS Modules 的 componentB 看起来是这样的:

import s from './style.css';

export default function () {
  document.body.innerHTML = `
    <div class="${s.wrapper}">
      <div class="${s.specificToComponentB}">componentB</div>
    </div>
  `;
}

s对象将包含:

{
  wrapper: "C8EKTwiZfd", // Look, different than in componentA!!!
  specificToComponentB: "KI5jRsC2R5"
}

componentB/style.css 将被转换为

.C8EKTwiZfd { // Look, different than in componentA!!!
  background-color: red;
}
.KI5jRsC2R5 {
  // rest of styles
}

现在即使您不像在两个组件中使用wrapper这样的超级具体名称,您也可以确保它们不重叠,componentA保持蓝色,componentB保持红色。这是 CSS 模块化描述的样式封装的强大优势 - 可以通过css-loader实现。

旧回答

css-loader style-loader 更改了 js 和 css 的关系

让我们考虑这个项目的结构

├── components 
│   │
│   ├── componentA
│   │   ├── style.css
│   │   └── index.js
│   │
│   ├── componentB
│   │   ├── style.css
│   │   └── index.js
│   │
│   └── componentC
│       ├── style.css
│       └── index.js
│   
├── index.js  
└── index.html

index.js看起来像这样

import componentA from './components/componentA';
import componentB from './components/componentB';
import componentC from './components/componentC';

componentA();
componentB();
componentC();

1. 没有 css-loader style-loader

一般情况下,*.js 组件的代码如下所示:

export default function () {
  // logic of this component
}

index.html 包含

<link href="components/componentA/style.css" rel="stylesheet" type="text/css">
<link href="components/componentB/style.css" rel="stylesheet" type="text/css">
<link href="components/componentC/style.css" rel="stylesheet" type="text/css">
<script type="text/javascript" src="dist/bundle.js"></script>

现在,如果您想重构代码并禁用componentB,则必须从index.js中删除它。
import componentA from './components/componentA';
// import componentB from './components/componentB';
import componentC from './components/componentC';

componentA();
// componentB();
componentC();

由于CSS和JS已经解耦,因此您必须在index.html中对样式进行相同的处理。

<link href="components/componentA/style.css" rel="stylesheet" type="text/css">
<!-- <link href="components/componentB/style.css" rel="stylesheet" type="text/css"> -->
<link href="components/componentC/style.css" rel="stylesheet" type="text/css">
<script type="text/javascript" src="dist/bundle.js"></script>

这种在大型项目中的重复可能导致未使用的CSS代码 - 维护更加困难,因为您必须在不同的地方进行两个操作。
注意:SASS或LESS也存在相同的问题。只是将其从index.html移动到index.sass中:
@import './components/componentA';
@import './components/componentB'; // you must disable this manually
@import './components/componentC';

2. 使用 css-loader style-loader

现在你可以直接从组件中指向与其相关的样式(而不是像第一点那样将其放在单独的位置)。 例如,你的*.js组件将如下所示:

import './style.css';

export default function () {
  // logic of this component
}

index.html

<script type="text/javascript" src="dist/bundle.js"></script>

最重要的是,如果您拥有这种架构并想禁用componentB,您只需要执行以下操作:
import componentA from './components/componentA';
// import componentB from './components/componentB';
import componentC from './components/componentC';

componentA();
// componentB();
componentC();

就这些!不再需要在任何 .html.sass.less 中查找 componentB/style.css 样式的参考。如果您想添加新组件,同样只需导入 .js 文件即可添加 js 逻辑和 css 样式。这样更容易维护!


感谢您的出色回答。它帮助我真正理解了。 - ofir fridman
@ofirfridman 对于我的误导我感到抱歉,但我混淆了css-modules和style-loader。现在我必须描述两者。 - Everettss
2
你提到css-loader会将@import解释为require。这是否意味着webpack将接管该文件的解析?(听起来有点递归,但我想要确认一下)。我想相信这一点,但是我看到了css-loader中的“importLoaders”选项,这让我感到困惑,因为如果Webpack只是接管,那么这个选项就不必要了吧? - kng
1
@prk_001 我认为你自己已经回答了。在你的情况下,webpack 最有可能是杀鸡焉用牛刀。 - Everettss
@Everettss 谢谢,这正是我想的。我知道Webpack主要用于JS应用程序,在我的情况下,Grunt或Gulp就足够了。这很有道理,因为仅为样式编译设置Webpack似乎太麻烦了。 - prk_001

4
.js 文件中,ES6importCommonJSrequire() 允许你仅导入 JavaScript 文件(模块)到它中。因此,当你通过 import from './styles.css'.js 文件中包含 styles.css 时,你需要先将其转换为 .js。这正是 css-loader 能够提供帮助的地方。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接