These post is the second of a trilogy.
🔎 Focus on | 🖌 CSS Handling | Parts |
---|---|---|
(Index) | #️⃣ | |
development only | inline CSS | 1️⃣ |
both dev & prod | mini-css-extract-plugin | 📌 |
production only | CSS modules | 3️⃣ |
Example Code 📜
Final Product 🤖
By completing this starge you will get a good starting point for your personal webpack configuration. In production mode it is better not to inject CSS directly into the bundle because you can get a Flash of Unstyled Content (FOUC) - the inlined CSS it's applied only when the bundle it's executed.
We implement the extraction of CSS in a separate file that is executed at the same time as the rest.
Flow of Thought 🏮
- Add build script
- Download the loader
- Create the loader function
- Connect to useRules
- Add the plugin in webpack.config.js
Implementation 🤓
- 1 - Add build script
In packages.json
add the build script that will bundle our code and store it in adist
folder.
package.json
{
...
"scripts": {
"start": "webpack-dev-server --env development",
"build": "webpack --env production"
},
...
}
- 2 - Download the loader
In the terminal invoke npm i -D mini-css-extract-plugin
.
- 3 - Create the loader function
Add the package just downloaded in loaders.js
. Then exports a new function named extractCSS - it's almost the sameto the one built in the first phase. The difference stays in the fact that style-loader is is replaced with MiniCssExtractPlugin.loader.
loaders.js
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
// ... stage one created functions
exports.extractCSS = (config = {}) => {
// basic rule
const rule = {
test: /\.css$/i,
use: [MiniCssExtractPlugin.loader, 'css-loader'],
};
return addConfigs(rule, config);
};
- 4 - Connect to useRules
In useRules the implementation is fair simple:
- import the just created function
- add its case in the loaders object
- add the instruction relative to production in instructions object ##### useRules.js
const { loadCSS, extractCSS } = require('./loaders'); //[1]
module.exports = (env) => {
const loaders = {
css: (i) => {
switch (i) {
case 'inline':
return loadCSS();
case 'MCEP': //[2]
return extractCSS();
default:
throw new Error(`The instruction ${i} is not covered`);
}
},
};
// developer interface
const instructions = {
css: {
development: 'inline',
production: 'MCEP', //[3] Mini-Css-Extract-Plugin
},
};
// business logic - already seen in stage one
let message = '[useRules] ';
const rules = Object.entries(instructions).map(([key, value]) => {
const i = instructions[key][env];
message += key + '|' + i;
return loaders[key](i);
});
console.info(message);
return { rules };
};
- 5 - Add the plugin in webpack.config.js
In order to properly work, MiniCssExtractPlugin
need to be imported [1] and loaded [2] in the plugins section in webpack.config.js
:
webpack.config.js
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
// [1]
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const useRules = require('./config/useRules');
module.exports = (env) => ({
devServer: {
open: true,
stats: 'errors-only',
},
plugins: [
new HtmlWebpackPlugin({
title: 'Webpack Inline CSS',
}),
new MiniCssExtractPlugin({ // [2]
filename: '[name].[hash].css'
})
],
module: useRules(env),
});
Checking the Outcome 😎
-
npm start
: the web server will start and open up your bundle in a tab. Open devTools and peek in Network section. Reload the page. See? There is no trace of any CSS file - it is installed in the JavaScript. -
npm run build
: adist
folder will be generated. Get into that and serve itcd dist && serve
(you may need tonpm i serve -g
before). Now, the result is the same as before but open again the devTools, get in network. You see that? It's a separate CSS file. No more Flash of Unstyled Content!
In console you should see log such as
[useRules] css|inline
or[useRules] css|MCEP
Upgrade the last-stage 🔝
avaiable soon
Top comments (3)
Coming from very traditional web development background, webpack is weird. Im uber confused after reading its document: It suggests that I import the css/saas file from a js file? WTF? Then if I want to separate the result css files I need to use a plugin to extract? What? Please tell me that I'm wrong, because I think that is just plain stupid.
I'm also new to Webpack, but the gist I've gotten from it is this: Webpack's goal is to make it easy to compile your development files into simple build products through a pipeline that you define in its configuration. By default, Webpack will read in your .CSS files and then use JS to compile and inject them directly into the build product.
This specifics of this injection leads to a big, crowded
<style>
tag, which can be ugly for production environments. The extractor plugin used in the article above pulls that<style>
tag out and builds a discrete .CSS file instead, passing that into your eventual build products like one would normally do for a stylesheet.The advantage of this process is that you can use a mix of compiled and uncompiled Sass (or your favorite CSS preprocessor), any frameworks (Bootstrap, for example, is written in Sass/SCSS), and plain CSS when you're styling your site. Webpack handles the nitty-gritty of compiling it into one solid piece of CSS, and then injects or extracts it according to your config.
Webpack also has some neat tricks for minimizing your build products and optimizing their structure, to attempt to help your site be as performant as possible, though mileage may vary on that front.
All these features make it a very versatile, powerful tool!
For those who may come after and want a shorter, direct solution with no plug-ins, your answer is here: style-loader