Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What is the purpose of css-loader in webpack

I'm starting to work with Webpack 2 and I saw that a lot of projects are using the css-loader and I didn't find what is the purpose for it.

like image 258
ofir fridman Avatar asked Jan 26 '17 16:01

ofir fridman


2 Answers


EDIT: In original answer I was describing style-loader not css-loader. It's easy to forget different purpose of those loader since css-loader can only be used with style-loader.


New answer

css-loader gives you more control over importing .css files.

1. Transforms url(image.png) => require('./image.png')

Since now require is used, it's enables you to use for example file-loader or url-loader. Now url(image.png) can be converted to:

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

or with limit property of url-loader creates inline picture:

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

2. Enables CSS Modules

Let's consider styles of componentA and componentB:

componentA/style.css

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

componentB/style.css

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

componentA looks:

import './style.css';

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

and componentB looks:

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 object will contain :

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

componentA/style.css will be converted to

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

and componentB with CSS Modules looks:

import s from './style.css';

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

s object will contain :

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

componentB/style.css will be converted to

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

And now even if you don't use super specific name like wrapper in both components you are sure that they don't overlap and componentA stays blue and componentB red. It's great power of encapsulation of styles described as CSS Modules - it is possible with help of css-loader.

OldAnswer

css-loader style-loader change couples js and css

Let's consider this structure of project

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

index.js looks like this

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

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

1. Without css-loader style-loader

each of *.js components in general looks like this

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

index.html contains

<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>

Now if you want to refactor your code and for example disable componentB you must remove it from index.js

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

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

and since css and js are decoupled you must do the same for styles in 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>

This duplication in large project can lead to leaving dead css code - it's harder to maintain since you must do two things in separate places.

NOTE: SASS or LESS have the same problem. It's only moved from index.html to index.sass:

@import './components/componentA';
@import './components/componentB'; // you must disable this manually
@import './components/componentC';

2. With css-loader style-loader

Now you point to styles related to some component directly from it (not in separate place like in 1. point) For example your *.js component will look like

import './style.css';

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

and index.html

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

The most important is that if you have this architecture and want to disable componentB, only what you do is

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

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

It's all! No more looking for reference of styles componentB/style.css in any .html or .sass or .less. The same goes if you want to add new components: by simply importing .js file you add js logic and css styles. This is much easier to maintain!

like image 173
Everettss Avatar answered Oct 16 '22 08:10

Everettss


In .js file ES6 import or CommonJS's require() allows you to import only JavaScript files(modules) into it. So when you include your styles.css, for instance, in .js file via import from './styles.css', you'll need to convert in into .js in the first place. And here is where css-loader comes to the rescue.

like image 26
GProst Avatar answered Oct 16 '22 09:10

GProst