In this article, we will learn how to configure a project that use Webpack and Babel in front-end. It is a first step to work with larger project in the future. So, let’s get started.
Table of contents
What is Webpack ?
-
Definition of Webpack
Webpack is a
Module bundle
. It takes a bunch of assets such as source, images, css, … to turn them into something that we will send to client. It means that it will package all dependencies into bundles that browser can understand.By default, Webpack only knows how to process
.js
or.json
files. To do with other format files, we need to useLoader
such asHtmlWebPackPlugin
for html file, … -
Some functionalities in webpack
- Hot module reloading: updates React component without refresh website.
- Lazy loading
- Bundle splitting
- Hashing
- Source maps: debug minified version.
-
Folder structure of Js application that use Webpack and Babel
- node_modules - dist/ - index.html - src/ - index.js - package.json - webpack.config.js
When Webpack run, Javascript files in source folder
/src
will be bundled into a single Javascript file which will be placed automatically into the/dist
folder./dist
will be used to serve our applicatioin to the browser. -
Some modes in Webpack
There are two modes:
-
development: This mode will remain the structure of file, so, we can easily read source code.
In order to use development mode, we can configure in package.json file:
"scripts": { "start": "webpack-dev-server --mode development --open --hot", "build": "webpack --mode development" }
-
production:
To production mode, all files will be minified. So, these files will be reduced the files’s size.
-
What is Babel ?
Babel is a toolchain that is mainly used to convert ECMAScript 2015+ code into a backwards compatible version of JavaScript in current and older browsers or environments.
Setup some packages Webpack and Babel
-
For webpack
To work with Webpack easily, we need to install some additional packages.
-
webpack
npm install --save-dev webpack
Webpack does not compile anything. It simply bundles all things together.
--save-dev
will saves these packages to devDependencies of package.json, which means these are development packages and user may avoid installing these packages. -
webpack-dev-server
npm install --save-dev webpack-dev-server
Under the hood,
webpack-dev-server
is a mini Node.js Express server. It uses a library called SockJS to emulate a web socket.webpack-dev-server
is a localhost server which we will use to preview how our web application works. The server will automatically send reload events to the browser to refresh the pages as soon as we make changes to the code. This should be used for development only.To run our project with
webpack-dev-server
, we need to configure in package.json file:"script": { "start:dev": "webpack-dev-server --config ./webpack.config.js --mode development", "build": "webpack --config ./webpack.config.js --mode production" }
And run the following command in our terminal/console:
npm run start:dev
-
webpack-cli
npm install --save-dev webpack-cli
It provides a flexible set of commands for developers to increase speed when setting up a custom webpack project.
We can refer the link of Webpack CLI
-
webpack-merge
npm install --save-dev webpack-merge
Assuming that we have two modes such as: production and development, we want that each mode will have corresponding webpack configuration file. So, we need
webpack-merge
package to do that.
-
-
Webpack plugin
-
uglifyjs-webpack-plugin
npm install --save-dev uglifyjs-webpack-plugin
It will be used to minify Javascript bundle made by Webpack.
-
html-webpack-plugin
andhtml-loader
html-loader
is used to processhtml
file.html-webpack-plugin
will inject js, css into html files, and it is reponsible for create output files in./dist
folder. Andhtml-webpack-plugin
is needed by webpack-dev-server to launch a web preview of our plugin.npm install --save-dev html-webpack-plugin html-loader
If we want to remove the
/dist
folder with every Webpack build, it means that the automatic generateddist/bundle.js
file should be removed, anddist/index.html
file will be added automatically. We do not want to createdist/index.html
file manually for every Webpack build again, so, we can usehtml-webpack-plugin
.It will simplify creation of HTML files to serve our webpack bundles. This is especially useful for webpack bundles that include a hash in the filename which changes every compilation.
We can refer link about html-webpack-plugin.
Update some plugins in webpack.config.js file:
const HtmlWebPackPlugin = require("html-webpack-plugin"); module.exports = { module: { rules: [ ... }, plugins: [ new HtmlWebPackPlugin({ template: "./src/index.html", filename: "./index.html" }) ] };
-
clean-webpack-plugin
Because of automatically generating
dist/index.html
anddist/bundle.js
files, we can delete the content of our/dist
folder with every Webpack build. So, we need to installclean-webpack-plugin
.npm install --save-dev clean-webpack-plugin
-
-
For hot reload
-
react-hot-loader
If we want to use hot loading module when we have changes in our files, we should use
react-hot-loader
.npm install --save-dev react-hot-loader
Add this plugin into webpack.config.js file:
plugins: [ new webpack.HotMoudleReplacementPlugin() ], devServer: { contentBase: './dist', hot: true }
In
src/index.js
file, we need to define that hot reloading is available:import React from 'react'; import ReactDOM from 'react-dom'; const title = 'React with Webpack and Babel'; ReactDOM.render( <div>{title}</div>, document.getElementById('app') ); module.hot.accept();
- For Resource
-
file-loader
npm install --save-dev file-loader
The
file-loader
resolvesimport / require()
on a file into a url and emits the file into the output directory.To know more information about
file-loader
, we can refer link.For example:
In other-file.js, we have:
// It means we will import or require the target files in one of the bundle's file import img from './file.png';
Then, we have to add
file-loader
into webpack.config.js file.rules: [ { test: /\.(png|jpe?g|gif)$/i, loader: 'file-loader', options: { name: '[name].[ext], outputPath: 'assets/images' } } ]
With the above configuration, images will be emitted to
dist/assets/images/file.png
.
-
CSS loader and plugin
-
css-loader
andstyle-loader
npm install --save-dev css-loader style-loader
We will use
css-loader
andstyle-loader
to import.css
file into Javascript file (whichsass-loader
generates) and finally inject into DOM instyle
tag. -
sass-loader
andnode-sass
npm install --save-dev sass-loader node-sass
To load
.scss
files into Javascript file using ES6 import syntax and compile them to CSS, we need to installsass-loader
andnode-sass
which will do actual compilation. -
postcss-loader
,cssnano
, andautoprefixer
npm install --save-dev postcss-loader cssnano autoprefixer
To minify and add prefixes to CSS (for browser compatibility) we need to use
postcss-loader
and some its dependencies such ascssnano
,autoprefixer
to do its actual works.In webpack, we can use postcss-loader with autoprefixer and other PostCSS plugins. So, in webpack.config.js or webpack.prod.js or webpack.dev.js, we have:
module.exports = { module: { rules: [ { test: /\.css$/, use: ["style-loader", "css-loader", "postcss-loader"] } ] } }
Then, we have to
create postcss.config.js
file:module.exports = { plugins: [ require('autoprefixer') ] }
To know more information about configuration of css plugin, we can reference to link.
-
mini-css-extract-plugin
npm install --save-dev mini-css-extract-plugin
This plugin extracts CSS into separate files. It creates a CSS file per JS file which contains CSS. It supports On-Demand-Loading of CSS and SourceMaps.
It builds on top of a new webpack v4 feature (module types) and requires webpack 4 to work.
Compared to the extract-text-webpack-plugin:
- Async loading
- No duplicate compilation (performance)
- Easier to use
- Specific to CSS
Refer to link.
-
optimize-css-assets-webpack-plugin
npm install --save-dev optimize-css-assets-webpack-plugin
This plugin will search for CSS assets during the Webpack build and optimize / minimize the CSS (by default it uses
cssnano
package).We can refer this link.
-
-
For Babel
-
babel-loader
It is the Webpack loader responsible for taking in the ES6 code and making it understandable by the browser. It will load ES6 modules in node.js using import syntax (node.js does not support
import
syntax). So, we need to installbabel-loader
and@babel/core
.npm install --save-dev babel-loader @babel/core
A loader has basic three parts:
- test - use regex to determine the type of files
- exclude - the folders that we want Webpack omits
- use: loader - the loader that we want to Webpack use to process files.
-
@babel/preset-env
It is used to compile Javascript ES6 down to ES5. And
babel-preset-es2015
package is deprecated.With ES6 arrow function,
@babel/preset-env
will convert arrow function to traditional function.npm install --save-dev @babel/preset-env
-
@babel/preset-react
In order that the syntax of React will be understood by other browsers, so we need Babel to transpile React code
JSX
to Javascript.npm install --save-dev @babel/preset-react
Then, we will find
.babelrc
file to add some configuration lines:{ "presets": [ "@babel/preset-env", "@babel/preset-react" ] }
Finally, let’s inform Webpack in webpack.config.js file about files with JSX extension to make sure that they run through the transpiling step:
module.exports = { entry: './src/index.js', module: { rules: [ { test: /\.(js|jsx)$/, exclude: /node_modules/, use: ['babel-loader'] } ] }, resolve: { extensions: ['*', '.js', '.jsx'] }, output: { path: __dirname + '/dist', publicPath: '/', filename: 'bundle.js' }, devServer: { contentBase: './dist' } };
-
babel-preset-transform-object-rest-spread
npm install --save-dev babel-preset-transform-object-rest-spread
It is a plugin to support Object rest/spread syntax if our babel preset doesn’t support it.
-
-
For React.js
Install some packages for react.js:
// Use must-downloaded react, react-dom packages npm install --save react react-dom // if we want to use typescript with react npm install --save-dev @types/react @types/react-dom // use Prop-types to document our React components // https://reactjs.org/docs/typechecking-with-proptypes.html npm install --save-dev prop-types
-
For Redux
We will install some packages for redux, like that:
// Use redux
npm install --save redux react-redux react-router-dom
// If we want to program asynchronous with redux, use redux-thunk
npm install --save redux-thunk
// we can use typescript versions of above packages
npm install --save-dev @types/react-redux @types/react-router-dom
Wrapping up
-
Understanding how Webpack works, we can easily configure all project that use Webpack and Babel.
-
Remember that all above plugins or loaders that are necessary to work.
-
Below is the content of
webpack.config.js
file that we need to know:const webpack = require("webpack"); const path = require('path'); const HtmlWebPackPlugin = require('html-webpack-plugin'); const FaviconsWebpackPlugin = require('favicons-webpack-plugin'); const CopyWebpackPlugin = require('copy-webpack-plugin'); const assets = [{ from: 'assets', to: 'assets' } ]; module.exports = { // Where to start bundling entry: { app: "./src/index.js", home: './src/home.js', contact: './src/contact.js' }, // Where to output output: { // Output to the same directory or use dist child directory path: path.resolve(__dirname, 'dist'), // __dirname publicPath: '/', // Capture name from the entry using a pattern filename: "bundle.js" }, // How to resolve encountered imports module: { rules: [ { test: /\.css$/, use: ["style-loader", "css-loader"], }, { test: /\.(ts|tsx|js|jsx)$/, exclude: /node_modules/, use: "babel-loader" // or we can use awesome-typescript-loader }, { test: /\.html$/, use: [ { loader: "html-loader", options: { minimize: true } } ] }, { test: /\.scss$/, use: [ 'style-loader', 'css-loader', 'postcss-loader', 'sass-loader' ] }, { test: /\.(woff(2)?|ttf|eot|svg)(\?v=\d+\.\d+\.\d+)?$/, use: [{ loader: 'file-loader', options: { name: '[name].[ext]', outputPath: 'assets/fonts/' } }] }, { test: /\.ico?$/, use: [{ loader: 'file-loader', options: { name: '[name].[ext]', outputPath: './' } }] }, ] }, // What extra processing to perform plugins: [ new FaviconsWebpackPlugin('./assets/images/logo.png'), new webpack.DefinePlugin({ SUBDIRECTORY: JSON.stringify(require("./package.json").subdirectory) }), new uglifyJsPlugin(), new HtmlWebPackPlugin({ template: "./src/index.html", filename: "./index.html" }), new CleanWebpackPlugin(), new webpack.HotModuleReplacementPlugin(), new CopyWebpackPlugin(assets) ], // Adjust module resolution algorithm resolve: { extensions: ['.ts', '.tsx', '.js', '.jsx', '.json'] }, devServer: { colors: true, contentBase: './dist', historyApiFallback: true, hot: true, inline: true, port: 3000, progress: true, stats: { cached: false } } };
Refer:
https://blog.linguinecode.com/post/how-to-setup-webpack-dev-server-react-babel
https://gaearon.github.io/react-hot-loader/getstarted/
https://www.valentinog.com/blog/babel/
https://www.valentinog.com/blog/webpack/
https://levelup.gitconnected.com/what-is-webpack-4fdb624597ae
https://tech.3si.vn/2017/11/24/meo-toi-uu-hoa-webpack-bundle/
https://www.robinwieruch.de/webpack-setup-tutorial
https://survivejs.com/webpack/what-is-webpack/
https://www.robinwieruch.de/webpack-advanced-setup-tutorial
https://www.robinwieruch.de/minimal-react-webpack-babel-setup
https://www.robinwieruch.de/minimal-react-webpack-babel-setup#react-webpack
https://www.robinwieruch.de/webpack-babel-setup-tutorial
http://vuilaptrinh.com/2018-05-25-huong-dan-viet-code-javascript-tot-hon-voi-webpack/