This lab is intended to walk you through the basics of the Webpack module bundling system. Webpack is the tool most commonly used in the React community to transpile and bundle components, and is what is used under the hood by create-react-app. While that scaffolding tool means you don't need to know how to set up Webpack, it is good to be at least somewhat familiar with the concept (and this exercise will give you further practice working with npm modules).
This tutorial is adapted from one by Tyler McGinnis.
Webpack is a build tool. That is, it is a (command-line) application that is used to automatically take the source code you write and prepare that code to be run/executed, whether for development or deployment. There are numerous such build tools in existence: Gulp is the major competitor to Webpack, and most IDEs (like jGrasp or IntelliJ) provide them. However, Webpack is favored by React developers because of how easily and speedily it transpiles JSX.
At its core, Webpack is a module loader: it takes source files like JavaScript modules and bundles them into a few simplified files that can be part of your webpage. It takes your complicated source code structure (lots of files) and transforms it into a brand-new, "simplified" version (few files).
Your task for this lab will be to setup and configure Webpack to build a couple of simple components into a single running React site! (Note that I said "configure"; we use webpack by specifying a configuration file for how it should combine files). This tutorial will walk you through each step to get setup. a single running React site!
Be sure you have cloned this repository to work along with the tutorial.
There are a two things you'll need to do to get started:
-
First, you'll need to create a new
package.jsonfile to store information about the app you're building, including dependencies that Webpack will use. You can do this with the following command:cd path/to/project npm init
You will be prompted for a bunch of information to provide about your app. Give it the following details (just hit
<enter>to accept the details on any other prompts)nameshould be "webpack-tutorial"authorshould be your name
Once you're finished, you will be asked to confirm your choices (type
yes), and you'll have a brand newpackage.jsonfile ready to use! -
Globally install the
webpackprogram. This will allow you to run the program from the command-line and bundle your app.npm install -g webpack@1
While Webpack can be used to do simple bundling from the command-line (see the official tutorial for an example), it's most common to write down all of your bundling options inside a configuration file. This file is basically just a JavaScript file that defines a variable that represents all of the different options you'd want to pass to the webpack program. If you name this file webpack.config.js, Webpack will read in that configuration automatically. Thus "using Webpack" really involves creating this file.
-
Create a new file called
webpack.config.jsand open it up in your favorite editor (e.g., VS Code). -
Inside this file, add the following line of code:
module.exports = {}
This is the CommonJS version of the ES6
export default {}—you are defining an object (initially empty) that will be exported and used by the Webpack program. The rest of this tutorial will involve adding properties to this object.- Note that is it possible to use ES 6 style
importandexportcommands, but you need to name your filewebpack.config.babel.jsand will need to have some Babel libraries installed. See here for discussion. For this lab, stick to the CommonJS syntax.
- Note that is it possible to use ES 6 style
Webpack's basic job is to take your source files, changing them in some way, and producing a new version. Thus there are three things you'll need to specify:
- What files to transform (specifically: what JavaScript file makes your program start)
- What transformations to make
- Where to save the transformed files
We specify the first piece (what files to transform) by giving the exported object an entry property:
module.exports = {
//list of entry points
entry: [
__dirname + '/src/index.js'
]
}This indicates which file (relative to the folder you are currently in) is the "start" or "entry point" into your program—in a way, which file has "main" in it. In our case, src/index.js (the __dirname is a Node constant referring to the current working directory). Note that the entry property is actually an array, since it's possible to support multiple entry points.
We'll skip step 2 for a moment and also specify where to save the transformed files. This is specified as the output property of the config object:
module.exports = {
//... remember, we're adding to the previous code!
output: {
filename: "bundle.js",
path: __dirname + '/dist'
},
}The output property is itself an object with more details about the output (rather than an array of possible outputs). The above example says that we should output into the dist folder (in the current directory), combining the code into a file called bundle.js.
Now you've got the basic pieces to build your bundle! You can do this by running the webpack program in the project directory:
webpackThis will create a new file dist/bundle.js. If you view this file in VS Code, you'll see it contains some extra code that organizes the different modules into functions (to support variable scoping), one of which is the content of the index.js file!
-
The React-specific code is commented out, so you can even open
index.htmlin a browser and see the console log in the developer tools! Be sure and uncomment this code before you proceed to the next section. -
While you're at it, you should go ahead and install the
reactandreact-dompackages so your app can use them:npm install react react-dom --save
This command installs two packages at once (
reactandreact-dom), and saves the fact that we depend on them in the.package.jsonfile.
Now onto the middle step: actually transforming the files. To do this, we use what are called loaders. A loader is basically a plugin that is used to perform a particular transformation (e.g., transpiling JSX or even ES 6 syntax!). Webpack's strength is its set of loaders that enable it to handle pretty much any kind of file and transformation. (It's of course possible to write your own, but that's well beyond the scope of this tutorial).
The module property is used to specify the list of loaders:
module.exports = {
//...
module: {
loaders: [
{
test: /\.jsx?$/,
loaders: ["babel"],
}
]
},
}The module property is an object that itself contains a loaders property, which is an array of loaders to apply. Each loader is described as an object (see? The nesting really does occur!).
-
The
testproperty indicates which file types we want the loader to transform. This may look scary and confusion, but that's because it's using a Regular Expression (similar to how you split words in the JavaScript Challenge) to specify the file extension that we want to consider. This particular expression indicates files that end in.jsor.jsx(the later is often used for JSX React components). How to make sense of this expression and its crazy punctuation:- Regular expressions are like Strings, but surrounded by
/ /instead of" " - The
.in.jsneeds to be escaped, so has a\in front of it. Like\nfor newlines. - The
xin.jsxis optional (without it we have.js, which is fine), so is written with a?after it to indicate that it can be present or not. - The last
$indicates the "end of the line", so means we'll only talk about files that end in.js(e.g.,libray.js.csswouldn't get transformed).
- Regular expressions are like Strings, but surrounded by
-
the
loadersproperty is a list of which loaders we want to apply to files whose names match the "test". In our case, we use Babel to transform our JSX, so we'll be using"babel"as our loader.
Loaders such as Babel need to be installed individually using npm as if they were separate programs (since they are in fact separate libraries!). This we will need to install the babel-loader package to be able to apply Babel transformations:
npm install babel-loader --save-dev- (The
--save-devargument here is like--savein that it saves the dependency into yourpackage.jsonfile. However,--save-devlists the dependency as only needed for development, not for deployment. Thus if you wanted to upload your code to a web server (like on AWS), this would let that server know that it doesn't need to install Babel because you've already transpiled the code into a production build).
But because nothing is ever simple, the babel-loader actually requires an additional library (babel-core, which is the Babel program itself) to do its work. Thus we also need to install:
npm install babel-core --save-devBabel is able to perform all kinds of transformations, such as compiling JSX and converting ES6 syntax into older, browser-compatible versions. Babel is very modular, so each transformation we want to apply can be downloaded as an individual libraries called presets.
npm install babel-preset-react babel-preset-es2015 --save-devThis installs two packages: babel-preset-react (the babel transformation for React/JSX) and babel-preset-es2015 (the transformation for ES 6).
Of course, downloading to preset transformations doesn't automatically tell Babel to use them. To do that, we actually need to create another file that will contain which presets Babel should use. This file is called .babelrc ("rc" stands for "run commands"; note the leading . indicating a hidden file). Create this file in the same directory as webpack.config.js.
Your .babelrc file just contains some JSON indicating which presets to use (other options are possible as well):
{
"presets": ["react", "es2015"]
}Altogether, you've specified what Babel transformations to apply (in .babelrc), and told your Webpack config file to use Babel to modify any JavaScript files.
So finally, you should be able to use webpack to build a working version of your React program! Open the index.html file in a Browser to see your lovely app.
Some things to note:
- All your code is inside the
bundle.jsfile. - Whenever you change your code, you will need to "re-build" your application (run
webpackagain). There are further webpack plugins that can help automate this, such as ones that will automatically refresh the page when the source files change.
The webpage doesn't look great yet, because there is no styling (CSS) involved. To practice working with webpack loaders, add an import for main.css stylesheet to your index.js, and modify webpack.config.js so that it will bundle that file:
-
Add the import to your
index.jsfile. The file path should be relative to theindex.jsfile. -
Install the
style-loaderandcss-loaderwebpack loaders (remember to--save-dev). Together, these loaders are able to handle CSS files. -
Add another element to the
module.loadersarray in thewebpack.config.jsfile to specify thestyle-loadertransformation.- The
testshould be a regular expression for files ending in.css - The
loaderitself should be"style-loader!css-loader", which refers to the "css loader" module for the style-loader plugin.
- The
-
Re-build your application using
webpack. If you reload the page, you should now see it has a gray background!
There are lots of further configurations and options used by Webpack. For example, you can use the webpack-dev-server to have webpack run a local server that will automatically re-bundle modules when the files change. You are encouraged to check out that example if you have time.