Webpack
Learn how to build an app using webpack.
We'll cover the following
Packaging an app with webpack
In the beginning, preparing JavaScript for the browser was simple. You would simply put your page’s functionality in a single script and load it with a <script>
tag. Then jQuery took off, and your script had a dependency. No problem: jQuery + app = two <script>
tags. But then other libraries came around, which depended in turn on other libraries. Using Backbone.js meant you had to load it after Underscore.js. As more app functionality moved into the browser, the once simple act of loading code in the right order became a challenge. And what about concatenating and minifying scripts for performance? What about loading “below-the-fold” scripts asynchronously? What about development mode?
Only a few years ago, bespoke in-house tools were the norm for addressing these problems. But now, at last, there’s a Swiss army knife for building JavaScript: webpack (always lowercase). Raw scripts go into webpack and compiled, concatenated, minified bundles come out. With a little bit of configuration (and unfortunately, sometimes a lot), webpack can generate any bundle you can imagine.
In this section, you’re going to harness the power of webpack to build a small demo app for the carousel component.
-
To get started, open the
test-driven-carousel
project from the last chapter. Then installwebpack
as a dev dependency along withwebpack-cli
and a library calledbabel-loader
that lets webpack use Babel to compile scripts:$ npm install --save-dev webpack@4.26.1 webpack-cli@3.1.2 babel-loader@8.0.4 + webpack@4.26.1 + webpack-cli@3.1.2 + babel-loader@8.0.4
-
Then create a file called
webpack.config.js
in the root of the project:// webpack.config.js module.exports = { mode: 'development', // 1 entry: { carousel: './src/Carousel.js', // 2 }, module: { // 3 rules: [ { test: /\.js$/, loader: require.resolve('babel-loader'), // 4 }, ], }, };
- Using
mode: 'development'
provides good defaults for running code locally, optimizing for ease of debugging at the expense of bundle size and runtime performance. By contrast,mode: 'production'
would give you a minified bundle.- Each entry point defines a separate bundle for webpack to build. Here we’re telling webpack to build a bundle called
carousel.js
that containssrc/Carousel.js
and its dependencies.- The
module
block is where we tell webpack how to treat different kinds of modules via an array of rules. A “module” in webpack can be any kind of file(JSON, CSS, even images)but we’re only concerned with JS modules, so we have just one rule.- This rule says, “Use babel-loader for all
.js
files.” The loader will run each script through Babel with the config found in the root of our project.
-
The
webpack-cli
package added an executable namedwebpack
. When it runs, it looks for a config file to tell it what to do. Although it should find thewebpack.config.js
in the root of the project by default, make the connection explicit by passing it a--config
flag in a newbuild
script:// package.json ... "scripts": { "test": "jest", "lint": "eslint . && prettier-eslint --list-different **/*.js", "format": "prettier-eslint --write **/*.js", "build": "webpack --config webpack.config.js" }, ...
-
Now try building your project:
$ npm run build Hash: ccc06a7f00b25a1780c4 Version: webpack 4.26.1 Time: 619ms Built at: 2018-12-02 18:48:29 Asset Size Chunks Chunk Names carousel.js 106 KiB carousel [emitted] carousel Entrypoint carousel = carousel.js [./src/Carousel.js] 5.31 KiB {carousel} [built] [./src/CarouselButton.js] 269 bytes {carousel} [built] [./src/CarouselSlide.js] 1.46 KiB {carousel} [built] + 11 hidden modules
If you look in the dist
dir of the project, you’ll see the carousel.js
bundle. As the output indicates, that bundle includes Carousel
, CarouselButton
, and CarouselSlide
.
Sharp-eyed readers will notice that the size of the bundle (106 KiB) is many times greater than the total size of the three carousel components. The gap is explained by the “hidden modules” mentioned at the end of the output. By default, webpack is silent regarding modules from node_modules
. In this case, those modules are the imports from the react
and prop-types
packages. If you’re curious to see them, run webpack
again with the --display-modules
flag.
A bit of housekeeping before we continue. ESLint is unaware that dist/carousel.js
is generated code, so it considers that code to be within its purview:
$ npx eslint .
/Users/tburnham/code/test-driven-carousel/dist/carousel.js
16:24 error Missing trailing comma comma-dangle
45:48 error 'Symbol' is not defined no-undef
...
✖ 81 problems (81 errors, 0 warnings)
36 errors and 0 warnings potentially fixable with the `--fix` option.
81 errors! Fortunately, ESLint allows you to ignore directories the same way that Git does, by listing patterns in an ignore file:
//.eslintignore
dist/
It’s also a good idea to tell Prettier to ignore that directory:
//.prettierignore
dist/
Additionally, if you’re using VS Code, you might consider adding dist
to the files.exclude
section of your Workspace Settings since you shouldn’t have to view or edit its contents directly:
//.vscode/settings.json
{
"files.exclude": {
"dist": true,
"node_modules": true,
"package-lock.json": true
},
"[javascript]": {
"editor.tabSize":
},
"editor.formatOnSave": true
}
Get hands-on with 1400+ tech skills courses.