
Learn how to use the webpack to build JavaScript, images, fonts and styles, and how to run the development
server.If you've used webpack 4 before, here are some of the differences from version 5:
- the "webpack-dev-server" command now looks like "webpack-serve"
- separate installation of file-loader, raw-loader and url-loader is no longer required, you can use the built-in asset modules
- polyfills for Node.js are no longer supported, so if, for example, you get an error for stream, you need to add the "stream-browserify" package as a dependency and {stream: "stream-browserify"} as an alias in your webpack settings
What is a webpack?
For the most part, sites are no longer written in pure HTML with little JavaScript - often they are only built using JavaScript. Therefore, it becomes necessary to build, minify and transpile the code. This is where webpack comes in handy.
Webpack is a module builder . It is used to package the code for use by the browser. It allows you to take advantage of the latest JavaScript features with Babel, or use TypeScript and compile it to cross-browser minified code. It also allows you to import static resources into JavaScript.
For developers, the webpack also provides a development server that can update modules and styles on the fly when the file is saved. Command line tools such as "vue create" and "react-create-app" use the webpack behind the scenes, but you can easily create your own webpack setup for these frameworks.
Webpack can do many other things as well, but this article will cover the basics of setting up and using it.
Installation
Create a project directory and initialize it:
mkdir webpack-tutorial
cd !$
//
cd webpack-tutorial
yarn init -yp // "package.json"
//
npm init -y
Install webpack and webpack-cli as development dependencies:
yarn add -D webpack webpack-cli
//
npm i -D webpack webpack-cli
- webpack - collector of modules and resources
- webpack-cli - webpack command line interface
Create a directory "src" to store application files. I'll start by creating a simple "index.js" file:
console.log(" !")
Great, we have a Node.js project with the main packages installed and an "index.js" file. Let's start configuring the webpack.
Basic setup
Let's start configuring the collector. Create a file "webpack.config.js" in the root directory of the project.
Point of entry
First of all, you need to define the entry point of the application, i.e. what files the webpack will compile. In the above example, we define the entry point as "src / index.js":
// webpack.config.js
const path = require('path')
module.exports = {
entry: {
main: path.resolve(__dirname, './src/index.js'),
},
}
Exit point
The exit point is the directory where the files compiled by the webpack are placed. Set the exit point to "dist". The "[name]" prefix matches the file name in src:
// webpack.config.js
module.exports = {
// ...
output: {
path: path.resolve(__dirname, './dist'),
filename: '[name].bundle.js',
},
}
The minimal setup for building the project is ready. Add the "build" script to the "package.json" file that runs the "webpack" command:
// package.json
"scripts": {
"build": "webpack"
}
Launch the webpack:
yarn build
//
npm run build
asset main.bundle.js 19 bytes [emitted] [minimized] (name: main)
./src/index.js 18 bytes [built] [code generated]
webpack 5.1.0 compiled successfully in 152 mss
The file βindex.bundle.jsβ is created in the βdistβ directory. The file has not changed, but we have successfully built the project.
Plugins
The plugin interface makes the webpack very flexible. Plugins are used both by the webpack itself and by third-party extensions. Some plugins are used in almost every project.
HTML template based plugin
We have a ready-made assembly, but it's useless without markup, which will load the assembly as a script. Since we want such an HTML file to be generated automatically, we use the html-webpack-plugin.
- html-webpack-plugin - Creates an HTML file based on a template
Install the plugin:
yarn add -D html-webpack-plugin
Create a "template.html" file in the "src" directory. We can add variables and other information to the template. Add the variable "title", otherwise the template will look like a regular HTML file with a container with the identifier "root":
<!-- src/template.html -->
<!DOCTYPE html>
<html lang="en">
<head>
<title><%= htmlWebpackPlugin.options.title %></title>
</head>
<body>
<div id="root"></div>
</body>
</html>
Add the "plugins" property to the webpack settings, where we define the plugin, the name of the output file (index.html) and the template:
// webpack.config.js
const path = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')
module.exports = {
// ...
plugins: [
new HtmlWebpackPlugin({
title: 'webpack Boilerplate',
template: path.resolve(__dirname, './src/template.html'), //
filename: 'index.html', //
}),
],
}
We start the assembly. The "dist" directory now contains the "index.html" file with the script included in it. Cool! If you open this file in a browser, you will see the message "How interesting!" in the console.
Let's add some content to the DOM. Let's change the content of the "index.js" file and restart the assembly.
// index.js
//
const heading = document.createElement('h1')
heading.textContent = ' !'
// DOM
const root = document.querySelector('#root')
root.append(heading)
Go to the "dist" directory and start the server (for this you need to globally install http-server : yarn global add http-server or npm i -g http-server).
http-server
In the browser tab that opens, you should see a heading that says "How interesting!" Also note the reduction in file size.
Cleaning
Install the clean-webpack-plugin, which cleans up the "dist" directory every time the project is built. This allows you to automatically delete old files that are no longer needed.
- clean-webpack-plugin - removes / cleans up the project build directory
// webpack.config.js
const HtmlWebpackPlugin = require('html=webpack-plugin')
const { CleanWebpackPlugin } = require('clean-webpack-plugin')
module.exports = {
// ...
plugins: [
// ...
new CleanWebpackPlugin(),
],
}
Modules and Loaders
The webpack uses loaders to parse files loaded by modules . These can be JavaScript files, static resources like images, or styles, and compilers like TypeScript and Babel. Webpack 5 has several built-in resource loaders.
Our project has an HTML file that loads some script, but it does nothing else. Let's think about what we want from the collector?
- Compile the latest JavaScript features into code that is compatible with all or most browsers
- Importing Styles and Converting SCSS to CSS
- Importing images and fonts
- Customizing React or Vue (optional)
First, let's set up Babel to compile JavaScript.
Babel (JavaScript)
Babel is a tool to enable future JavaScript today.
We are going to define a rule that all js files in a project (except files contained in the node_modules directory) will be transpiled using babel-loader. Babel requires several dependencies to work:
- babel-loader - transpiling files using Babel and webpack
- @ babel / core - transpiling ES2015 + to backward compatible JavaScript
- @ babel / preset-env - useful Babel default presets
- @ babel / plugin-proposal-class-properties - an example of a custom Babel configuration (setting instance properties in the body of the class, not in its constructor)
yarn add -D babel-loader @babel/core @babel/preset-env @babel/babel-plugin-proposal-class-properties
// webpack.config.js
module.exports = {
// ...
module: {
rules: [
// JavaScript
{
test: /\.js$/,
exclude: /node_modules/,
use: ['babel-loader'],
},
],
}
}
If you are setting up a TypeScript project, you should use typescript-loader instead of babel-loader for all JavaScript files that need transpilation. You are checking files with the "ts" extension and using the ts-loader.
So Babel is set up, but the plugin isn't there yet. You can verify this by adding the following code to the top of index.js:
// index.js
//
class Game {
name = 'Violin Charades'
}
const myGame = new Game()
//
const p = document.createElement('p')
p.textContent = `I like ${myGame.game}.`
//
const heading = document.createElement('h1')
heading.textContent = ' !'
// DOM
const root = document.querySelector('#root')
root.append(heading, p)
ERROR in ./src/index.js
Module build failed (from ./node_modules/babel-loader/lib/index.js):
SyntaxError: /Users/you/webpack-tutorial/src/index.js: Support for the experimental syntax 'classProperties' isn't currently enabled (3:8):
1 | // Create a class property without a constructor
2 | class Game {
> 3 | name = 'Violin Charades'
| ^
4 | }
To fix this, create a ".babelrc" file in the root of the project:
// .babelrc
{
"presets": ["@babel/preset-env"],
"plugins": ["@babel/plugin-proposal-class-properties"]
}
We start the build with yarn build. Everything works now.
Images
We want to import images into a JavaScript file, but JavaScript cannot do this. To make sure of this, create a directory "src / images", put an image there and try to import it in the file "index.js":
// index.js
import example from './images/example.png'
// ...
An exception will be thrown when starting the build:
ERROR in ./src/images/example.png 1:0
Module parse failed: Unexpected character ' ' (1:0)
You may need an appropriate loader to handle this file type, currently no loaders are configured to process this file. See https://webpack.js.org/concepts#loaders
As noted earlier, the webpack has some built-in downloaders for handling static files. For images, use the "asset / resource" type. Please note that we are talking about the type (type), and not about the loader (loader):
// webpack.config.js
module.exports = {
// ...
module: {
rules: [
//
{
test: /\.(?:ico|gif|png|jpg|jpeg)$/i,
type: 'asset/resource',
},
],
},
}
A new file appears in the "dist" directory.
Fonts and other embedded data
The webpack also has a built-in module to handle some embedded data such as fonts and SVG. To do this, it is enough to specify the type "asset / inline":
// index.js
import example from './images/example.svg'
// ...
// webpack.config.js
module.exports = {
// ...
module: {
rules: [
// SVG
{
test: /\.(woff(2)?|eot|ttf|otf|svg|)$/,
type: 'asset/inline',
},
],
},
}
Styles
Using style loaders is a prerequisite for using strings like "import 'file.css'" in your script.
Many people use CSS-in-JS , styled components, and other tools that allow JavaScript to style.
Sometimes we can limit ourselves to loading one CSS file. But perhaps you want to use PostCSS , which allows the latest CSS capabilities in the browser. Or do you want to use the Sass preprocessor .
I want to use all three - write code in Sass, process it with PostCSS, and compile to CSS.
- sass-loader - Loads SCSS and compiles it to CSS
- node-sass - Node Sass
- postcss-loader - CSS processing with PostCSS
- postcss-preset-env - useful PostCSS settings
- css-loader - loading styles
- style-loader - applying styles to DOM elements
yarn add -D sass-loader postcss-loader css-loader style-loader postcss-preset-env node-sass
As with Babel, PostCSS requires a separate configuration file:
// postcss.config.js
module.exports = {
plugins: {
'post-css-preset-env': {
browsers: 'last 2 versions',
},
},
}
To test the functionality of the named tools, let's create a file "src / styles / main.scss" containing Sass variables and an example of using PostCSS (lch):
// src/styles/main.css
$font-size: 1rem;
$font-color: lch(53 105 40);
html {
font-size: $font-size;
color: $font-color;
}
We import this file into index.js and add 4 loaders. Loaders are used by the webpack from right to left, so the last one should be sass-loader, then PostCSS, then CSS, and finally style-loader, which applies the compiled styles to DOM elements:
// index.js
import './styles/main.css'
// ...
// webpack.config.js
module.exports = {
// ...
module: {
rules: [
// CSS, PostCSS, Sass
{
test: /\.(scss|css)$/,
use: ['style-loader', 'css-loader', 'postcss-loader', 'sass-loader'],
},
],
},
}
Once built, you will notice that Sass and PostCSS have applied to the DOM.
Please note that we have set the preferences for development mode. For production use MiniCssExtractPlugin instead of style-loader, which exports minified CSS.
Development of
Typing yarn build (npm run build) every time to rebuild the project can be tedious. The larger the project, the longer it will take to build. Therefore, you need to have two webpack configuration files:
- Production settings, including minification, optimization and removal of all resource maps (source maps)
- Development settings including server start, update on every change, and resource maps
Instead of creating a "dist" directory, development mode can expect to store the information you need in memory.
To do this, you need to install webpack-dev-server.
- webpack-dev-server is a development server
yarn add -D webpack-dev-server
In order to demonstrate how to use a server for development, we can define the appropriate settings in the "webpack.config.js" file. In practice, it is best to have two configuration files, one with mode: production and one with mode: development. In the specially prepared webpack 5 boilerplate for you, I use webpack-merge to get the basic settings in a single file, and the special requirements are contained in the "webpack.prod.js" and "webpack.dev.js" files.
// webpack.config.js
const webpack = require('webpack')
module.exports = {
// ...
mode: 'development',
devServer: {
historyApiFallback: true,
contentBase: path.resolve(__dirname, './dist'),
open: true,
compress: true,
hot: true,
port: 8080,
},
plugins: [
// ...
//
new webpack.HotModuleReplacement(),
],
}
We've added mode: development and a "devServer" property. This property contains several standard settings - port number (8080), automatic browser opening, use of hot-module-replacement , which requires webpack.HotModuleReplacement (). This will allow the modules to update without a full page reload, i.e. if individual styles change, only they are updated, you do not need to reload JavaScript, which greatly speeds up development.
To start the server, use the "webpack serve" command:
// package.json
"scripts": {
"start": "webpack serve"
}
yarn start
After running this command, the browser will open at localhost: 8080. Now you can modify Sass and JavaScript and they will update on the fly.
Conclusion
I hope this article helped you get started with webpack. To make it easier to solve your daily tasks, I developed a webpack 5 boilerplate with Babel, Sass, PostCss, optimization for production and a server for development. Taking it as a basis, you can easily configure your webpack to work with React, Vue or TypeScript.