Hello super devs, π
How is everybody doing there? Good?
Are you watching lot of sports on Olympic Games? Has your country conquered many medals?
Remember to leave your chair sometimes and go outside to make some exercises also. There is no git revert HEAD
for life.
As you can see above it took me a little bit to publish this article. π Never mind about OG anymore. π
Today I am going to talk a little bit about Webpack and we are going to try to recreate a very simple React application, step by step.
OK, but what the heck is Webpack?
Webpack πΈοΈ π¦
Webpack is an open-source JavaScript module bundler. I know, blah blah blah ...
Let's break into pieces so it becomes easy (or not) to understand:
- module: a component or part of a program that contains one or more routines.
- bundler: A group of objects held together, wrapped in a package.
Until yesterday, browsers couldn't handle code divided into multiple modules. The Webpack's mission is to wrap all the source code into a single file containing all the application code.
Do you really need to know about it? Sincerely you do not. I particularly like to understand how things work under the hood. Believe me, it can surprise you.
If you are still here, it is time to get hands dirty!
Project Bare-bones π π¦΄
Let's start by creating a project structure similar to the image below:
-
package.json
file:
{
"name": "webpack-fun",
"version": "0.0.1",
"description": "webpack fundamentals",
"license": "MIT"
}
Installing the Webpack package
npm install --save-dev webpack webpack-cli
Here we are installing the Webpack package as a development dependency as well as its cli (Command Line Interface).
Once done that, we should define the initial Webpack settings (webpack.config.js
file):
Webpack Configuration File
const path = require("path");
const config = {
entry: "./src/index.js",
output: {
path: path.resolve(__dirname, "build"),
filename: "main.js"
}
};
module.exports = config;
Don't you worry I will explain it: π
path:
NodeJS
path module used for handling file paths.-
config: Object that holds the Webpack configuration.
- entry: Where Webpack looks to start building the bundle. The context is an absolute string to the directory that contains the entry files.
- output: contains set of options instructing Webpack on how and where it should output your bundles, assets the bundle or load with Webpack.
module.exports:
NodeJS
special object that represents the current module, and exports is an object that will be exposed as a module.
Defining the build script (package.json
) ποΈ
Now, we need to define the build script responsible for triggering the Webpack bundling.
{
// ...
"scripts": {
"build": "webpack --mode=development"
},
// ...
}
I guess we are ready to test the application. Let's add some dummy code in a brand new src/index.js
file, just to verify if it works:
const consoleLogTricks = name => {
console.log("Look at this:")
console.group("Question:")
console.log(`Did you now about it, ${name}?`)
console.log("Probably yes!")
console.groupEnd()
};
Now, if we run the build script created previously (npm run build
), a new bundled file should be created at /build/main.js
. It is the bundled content of our index.js
file.
Isn't it amazing? Well, nothing special I guess. π₯± π₯±
Let's try to mimic a React-like application. Under the src
directory create a file called App.jsx
.
π‘ People often use the .js
extension which is fine.
As my personal preference, when I am creating components I use the .jsx
one. Their icons also change on VSCode and I know what that's about. π βοΈ
-
src/App.jsx
.
const App = () => {
return null;
}
// Remember to export :)
export default App;
- Import the App component in the
index.js
file we created previously:
import App from "./App"
const welcome = user => {
console.log(`Welcome ${user}`)
}
App();
We are almost there. At the moment your application is not doing too much. It is missing some packages that will help us to transform it in a minimal React application.
Go ahead and install them: πͺ
npm install --save react react-dom
Done that, it is time to rewrite your index.js
and App.jsx
files and use the packages we have just installed.
-
index.js
import React from "react";
import ReactDOM from "react-dom";
import App from "./App";
ReactDOM.render(<App />, document.getElementById("root"));
-
App.jsx
import React from "react";
const App = () => {
return (
<div>
<h1>Hello from Webpack!</h1>
</div>
);
}
export default App;
Now we need to create an index.html
file that will be the entry point of our application and load our bundled JavaScript code.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>Minimal React App</title>
</head>
<body>
<div id="root"></div>
<script type="text/javascript" src="./main.js"></script>
</body>
</html>
Loaders π
We have an issue here. Do you remember that Webpack is a JavaScript bundler? Our components are using JSX
that is a syntax extension to JavaScript.
ποΈ If it confuses you, please refer to Introducing JSX.
The loaders come to our rescue.
- Installing the necessary Babel packages:
npm install @babel/core babel-loader @babel/preset-react --save-dev
- Setting up a loader in the
webpack.config.js
file, under themodule
property.
This loader is responsible to transform JSX code into regular JavaScript.
// ...
module: {
rules: [
{
test: /\.js$/,
loader: "babel-loader",
query: { presets: ["@babel/preset-react"] }
}
]
}
// ...
We should be able to bundle our application "properly" now. π π π₯³
π₯€ <Hydration Time>
π₯€
I know, it is quite overwhelming all this "setup" process. Luckily, you won't be configuring Webpack from scratch that often or maybe you never will.
Understanding at least the basics of how it works may be useful to you someday. Who knows? Β―_(γ)_/Β―
Take your time to wrap up things in your head, eat a snack, drink a glass of Tang / Kool-aid and come back here when you feel ready.
No pressure at all! See you in 5 minutes. π
π₯€ </Hydration Time>
π₯€
There is just one important detail. If we try to make any async
operation (e.g. REST API operations), it may happen that some browsers won't understand what is going on.
Babel has the polyfill
package for solving this issue, so let's go for it. πͺ
- Installing
polyfill
:
npm install --save @babel/polyfill
- Add it into the
entry
property in ourwebpack.config.js
file.
const config = {
+ entry: ['@babel/polyfill', './src/index.js'],
- entry: "./src/index.js",
output: {
// ...
}
// ...
}
Transpilers π€ βοΈ
This word looks weird. Actually it sounds like a pile of Transformers. π₯ π
Bad jokes aside, it is the term used to mean that a source code is transformed from a language to another. (Maybe Transformed + Compiled ?)
Anyway, the question is why do we need a transpiler?
It is known that most browsers don't really support the newest JavaScript features such as ES6, ES7, ES11 and so on.
The function of a transpiler is (guess what? π«) to transpile those new features into standard ES5.
- Installing the preset:
npm install @babel/preset-env --save-dev
- Adding the
@babel/preset-env
plugin in thewebpack.config.js
file.
// ...
{
test: /\.js$/,
loader: 'babel-loader',
query: {
// π H e r e ! π
presets: ['@babel/preset-env', '@babel/preset-react']
}
}
Finally we are able to write JavaScript code using all the latest features. π¨βπ»
Adding CSS π
A web application without CSS is like a cheese burger without the hamburger. π π§
I mean, it is totally possible but it is not the same thing. There is a flavor missing somewhere. π€£
- Let's create a CSS file on
src/index.css
:
β οΈ The commercial usage of this file is prohibited by law β οΈ
.wrapper {
empty-cells: show;
background-color: mediumaquamarine;;
color: blanchedalmond;
}
- Import it on
index.js
file:
import './index.css'
- Apply it in the
App.jsx
component:
const App = () => {
return (
<div className="wrapper">
<h1>Hello from Webpack</h1>
</div>
)
}
π€Ί TouchΓ© moment: Yes, we need to install more loaders in order to make our CSS styles work as well. How did you know that? π€
npm install style-loader css-loader --save-dev
In short:
-
style-loader: Generates and injects a
<style>
element which contains all of the application styles. - css-loader: Transforms CSS into a JavaScript module and allows minification. E.g.(Input: CSS β‘ Output: JavaScript)
Please, don't forget to also add the loaders in your webpack.config.js
file, otherwise all our effort and the RSI (Repetitive Strain Injury) acquired by typing npm install
hundred times will be in vain: π π
{
rules: [
{
// ... previous config
},
// β¬οΈ π π β¬οΈ
{
test: /\.css$/,
loaders: ['style-loader', 'css-loader'],
},
// β¬οΈ βοΈ βοΈ β¬οΈ
];
}
Webpack Development Server π₯οΈ β¬οΈ
One thing that drive me nuts is having to manually refresh the page every time I make changes in the application. π₯΄
Don't stress yourself, at this point of the article you have already mastered using npm
to install packages. π π
- Install the server package:
npm install --save-dev webpack-dev-server
- Define the start script in your
package.json
file.
{
// ...
"scripts": {
"build": "webpack --mode=development",
// π π π π
"start": "webpack-dev-server --mode=development"
},
// ...
}
- Add the
devServer
property intowebpack.config.js
file:
const config = {
entry: './src/index.js',
output: {
// ...
},
+ devServer: {
+ contentBase: path.resolve(__dirname, 'build'),
+ compress: true,
+ port: 3000
+ },
// ...
};
Running the npm start
script in your terminal should start the server at http://localhost:3000
.
Error Handling π«
The way Webpack shows error differs a little if compared to an application created using create-react-app
.
Very often a error is shown but not its real location.
Source maps provide us with the source code that is actually causing the error.
This time, as an rare exception, you won't need to install anything. π π₯³ π―ββοΈ
Just add the devtool
property in our webpack.config.js
file and a source map will be generated (build/main.js.map
).
const config = {
entry: './src/index.js',
output: {
// ...
},
devServer: {
// ...
},
+ devtool: 'source-map', π
// ..
};
Minifying the source code in production
Since we are using Webpack in its latest version, no further configuration is need in order to minify the source code.
Ah, ok. I almost forget to explain what minifiyng
code means.
Minifying means that your code that was previously classified as illegible by your workmates becomes officially gibberish. π π€£ π π
Minifying is the process that removes both comments, whitespaces, newline characters, replaces variable names with a single character and so on, in order to minimize code and reduce file size (TLDR; to optimize performance).
As result of minifying we would have something like this:
!function(e){var t={};function n(r){if(t[r])return t[r].exports;var o=t[r]={i:r,l:!1,exports:{}};return e[r].call(o.exports,o,o.exports,n),o.l=!0,o.exports}n.m=e,n.c=t,n.d=function(e,t,r){n.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:r})},n.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},n.t=function(e,t){if(1&t&&(e=n(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;
We need to add a npm
script (package.json
) to do so.
β οΈ Note: --mode=production
. β οΈ
"scripts": {
+ "build": "webpack --mode=production",
// ...
},
Conclusion, Frenzies and Farewell ππ‘π
Indeed, there a lot more concepts to cover regarding Webpack but hopefully this introduction is enough for you to go and start exploring it by your own. πΊοΈ π
Albert Einstein once said: π§
If you can't explain it to a six year old, you don't understand it yourself.
After finish reading this whole article to my 2 years old sibling, he starts to uncontrollably crying.
I guess it is happy crying because it looks like he got it even being younger than the expected! πΆ
Sorry, I must go now. He is becoming really loud. πββοΈπ¨
Maybe I should read him my other article about Regex. π¬ π€
Congratulations for reaching the end π and I wish you all a great weekend. π
See ya!
Top comments (0)