React Tooling Part 2
In this section, we will discuss about webpack and the ecosystem around it.
webpack

Modern JS applications usually involved hundreds, if not thousands of files/functions. It would be painful and error-prone if we just include <script> tags in our html file manually.
This may not seems obvious to us now because currently we have only two components App and Movie. However, even with only two components, we need to bear in mind that React and ReactDOM are our dependencies and we need to ensure our code only run after they are loaded. Try to move our script tag above the React and ReactDOM script tags, and you realize our code no longer works because React code has not loaded yet.
Now imagine you have 50+ components and more than 20 dependencies, and there are some inter-dependencies between those files, manually analyzing and rearranging your script tags would be very painful, if not impossible.
webpack is the tools that solves this problem. It allows us to use JS module system like commonjs (e.g. const React = require('react')) and ECMAScript Module (e.g. import React from 'react') by parsing them and create a single bundled output file. On top of that, webpack has been extended to handle all types of files that involved in a modern web application, e.g. css, images, html etc.
Bundling code with webpack
To use webpack in your project:
-
Install it in your project:
npm install -D webpack webpack-cli -
Create a folder and call it
src. Move yourscript.jsinto this folder and rename it asindex.js. -
Add the following npm scripts:
{ "scripts": { //... "build": "webpack --mode=\"development\"" } } -
Run
npm run build
- webpack will produces a file call
main.jsindistfolder.- webpack will includes some additional code (known as webpackBootstrap). The code is the runtime of webpack to manage your dependencies and perform some magic e.g. lazy-loading (which we will explain later).
- If you update your
index.htmlto use script fromdist/main.js, the code should works as before.
Now that webpack is processing our file, let’s utilize webpack by splitting App and Movie into their own module/file:
- Create a file alongside
index.jsand call itmovie.js. - Cut and paste the
Moviecomponent intomovie.js. - At the bottom of the
movie.js, add the following statement:export default Movie; - Create another file and call it
app.js. - Cut and paste the
Appcomponent intoapp.js. - At the top of the
app.js, add the following statement:import Movie from './movie'; - At the bottom of the
app.js, add the following statement:export default App; - All
AppandMoviecode should be removed fromindex.jsnow. Add the following statement at the top ofindex.js:import App from './app';
Functionally, our code still works the same. However, now we organize our code with modules and dependencies between them are managed by webpack.
importstatement is used to include code that the module depends on. For our example above:app.jsdepends onmovie.jsindex.jsdepends onapp.js
exportstatement is used to declare the code that is exposed to other modules.- there are two types of
exportstatement, i.e. default export and named export. - We use default export in our files currently. You can read more about them here.
- there are two types of
Exercise
- Install webpack and configure build script based on the instruction above.
- Verify that your code still works as before.
- (Bonus) use named exports for
movie.js.
Commit: 030-webpack-app-code
Bundling external dependencies
Now webpack is managing dependencies between our codes (index.js, app.js and movie.js), let’s take one step further to utilize it to manage the our dependencies to React and ReactDOM.
-
Install React and ReactDOM:
npm install react react-dom. Note that there is no-Dflag as we need React and ReactDOM in our application code, not tools during development/building. -
At the top of your
index.js, add the following statements:import * as React from 'react'; import ReactDOM from 'react-dom'; -
At the top of
app.jsandmovie.js, add the following statement:import * as React from 'react'; -
Remove the unpkg script tags for React and ReactDOM in your
index.html. -
Run
npm run buildagain. Verify that your code still works as before.
Exercise
- Install React and ReactDOM as the dependencies of the project.
- Bundle them together by including changes as described above.
- Verify that your code still works as before.
Commit: 040-webpack-dependencies
webpack-dev-server and html-webpack-plugin
webpack-dev-server is a webpack feature that would ease your development by starting up a web server to serve your code. We will use it with html-webpack-plugin so that even our index.html will be managed by webpack.
-
install required packages as devDependencies:
npm i -D webpack-dev-server html-webpack-plugin -
create a file
webpack.config.jswith the following content.webpack.config.jsis the file that allows you to customize behavior of webpack. There are many configurations that you can do here, e.g. specify the entry points (if you entry point is notsrc/index.js) or the output folder (if you want to put the output in the folder with name other thandist).const HtmlWebpackPlugin = require('html-webpack-plugin'); /** * @type {import('webpack').Configuration} */ module.exports = { devServer: { port: 9200, }, plugins: [ new HtmlWebpackPlugin({ template: 'index.html', }), ], }; -
remove
<script src="dist/main.js"></script>fromindex.htmlbecause webpack will help you to inject it. -
add a
startnpm script:{ "scripts": { ... "start": "webpack-dev-server --mode=\"development\" --open" } } -
run
npm start(npm run startworks too). This should open your default browser with the url http://localhost:9200/. -
try modify your code now. webpack will help you to refresh browser.
Bundling for production
You may realize that the current output file of webpack has tons of comments and spaces, which is good for us to read and understand what it is doing, but bad to be included to production as those comments are not useful for our customers/users.
To create a production bundle for our app:
- Add the following npm script in
package.json:"build:prod": "webpack --mode=\"production\"" - run
npm run build:prod
Exercise
- configure webpack-dev-server as described and run it. Verify it recompile and refresh your browser when you make change to your code.
- configure build:prod npm scripts as described above. Verify the produced
main.jsis minified, and the file size is much smaller now.
Commit: 050-webpack-dev-server
Other features of webpack
Note that webpack is much more powerful than I’ve described above. The following common use cases of webpack are omitted here, but you should have a read after this workshop.
style-loaderandcss-loader: allow you to import.cssfile in your javascript file, which will be injected as<script>tag in html.- Hot Module Replacement used in conjunction with
react-hot-loaderallows you to tweak React components while maintaining state. dotenv-webpackallows you to define environment variables. This is very commonly used to set environment specific variables, e.g. API endpoints, variables to set logging behavior etc.webpack-mergeallows you to merge webpack configurations. This is useful when you have multiple webpack configurations for different purpose (development optimized for compilation time, production optimized for runtime performance), and this package allows you to merge webpack configurations instead of copy-pasting the configurations.
Babel

- In our current code, we use new JS feature/syntax like arrow-function,
constandclass. However, only modern browser recognize those JS syntax - it will cause parsing error in older browser like IE 11. You can confirm this by opening http://localhost:9200/ using IE. - Babel is the tool that allow us to write modern JS while maximizing backward compatibility.
- Babel is a compiler that compile your modern JS code (usually called ES2015+, or ES6+) to the older JS code.
- You can see Babel in action at https://babeljs.io/repl.
Compiling code with Babel
-
Babel allows few usage options, e.g. as a CLI tool or plugin to other build system.
-
Since we are using webpack, we will use
babel-loaderto integrate babel into our project. -
To include Babel as part of our webpack bundling process:
- Install required packages:
npm install -D @babel/core @babel/preset-env babel-loader - Create a
.babelrcfile with the following content. This file tells Babel what transformation to perform.{ "presets": ["@babel/preset-env"] } - Create a
.browserlistrcfile with the following content. This specifies what browser you supports.@babel/preset-envuse this information to decide what syntax it needs to transform and what can be leave it as-is.> 1% not ie <= 8 - Update
webpack.config.jsfile by addingmodule.rulessection, like the following content. This configuration tells webpack to run through all file ends with.jsor.jsxextension through thebabel-loader.module.exports = { module: { rules: [ { test: /\.(js|jsx)$/, exclude: /node_modules/, use: ['babel-loader'], }, ], }, devServer: { port: 9200, }, plugins: [ new HtmlWebpackPlugin({ template: 'index.html', }), ], }; - Terminate your webpack-dev-server if it’s running by Ctrl+C. Run
npm startstart it again. This is because configuration change will not be picked up by webpack-dev-server while it is running. - Open http://localhost:9200/ with IE and verify your code should works now.
- Install required packages:
Exercise
- Configure Babel as described above.
- Restart webpack-dev-server and verify that the application works in IE.
Commit: 060-babel-integration
Cleaning Up
As our code has been reorganized, with introduction of webpack, let’s update our npm scripts accordingly. Update format and lint script in package.json:
{
"scripts": {
...
"format": "prettier --write src/**/*.{js,jsx}",
"lint": "eslint src/**/*.{js,jsx} --quiet"
}
} 