Building Apps with Webpack
Introduction to Building Apps with Webpack
What can Webpack bundle up?
- JavaScript files
- stylesheets
- images
- fonts
- text files
Webpack Project Setup
Set Up package.json
Project metadata: https://docs.npmjs.com/cli/v7/configuring-npm/package-json
npm init -y // create a package.json file (-y: using the default values for the metadata fields)
Install Webpack and Webpack CLI
npm install --save-dev webpack webpack-cli //--save-dev: We want these tools to be developer dependencies because they will not be used when the final product is running
Make an entry point
A Webpack project requires an entry point where it will find the main file to bundle. The default Webpack entry point is index.js
in a src
folder, although this can be changed.
Define the Build Command
Scripts: https://docs.npmjs.com/cli/v7/using-npm/scripts
The webpack
of the command just runs Webpack, --watch
tells Webpack to automatically look for updates to our file and rebuild if any changes occur.
"scripts": {
"build": "webpack --watch",
},
We run the build command in the terminal with:
npm run build
When we’ve set up the package.json, entry point, and build command, Webpack is ready to go!
Packing One JavaScript File
Default entry point: src/index.js
Default output: dist/main.js
Packing Multiple JavaScript Files
Build tools allow us to do just that. We can improve source code readability with functions, comments, and more. Webpack will remove much of this when creating the content served to the end-user.
In a typical static web project, we would have to put all three scripts into our HTML page and include them in the right order. Build tools like Webpack help make including resources easier.
Webpack will allow us to use import
and export
statements on all our front-end files, not just JavaScript. When we build Webpack stitches all our files together as if we wrote them as one.
Creating a Webpack Config
Webpack automatically looks for a configuration file named webpack.config.js
.
'development'
mode is used when we develop our app, producing a more readable version of the output. We switch to 'production'
when we have a finished version, which makes the output less readable and more compact.
module.exports = {
mode: 'development'
}
Defining Entry and Exit Points
The entry point allows us to define a path relative to where our webpack.config.js
is located.
Unlike the entry point, the exit point requires an absolute path, which is best specified with the path
Node module.
const path = require('path');
module.exports = {
entry: './application/home.js',
output: {
filename: 'fast.js',
path: path.resolve(__dirname, 'built'),
},
// ...
}
Viewing Our App with Webpack Dev Server
We would first install webpack-dev-server
as a development dependency in a local environment:
npm install --save-dev webpack-dev-server
Next, we need an HTML document, and it should embed the JavaScript from our exit point:
<script src="./dist/main.js"></script>
To use webpack-dev-server
, we’ll add start
command to package.json
inside of the scripts
section.
"build": "webpack --watch",
"start": "webpack serve"
This is building our project and then serving it to the browser.
The build
command is going to compile our project as well as update it when we make changes.
The serve
command is going to serve our build and refresh when the build changes.
In order to have our project update any time we make changes, we would typically run each of these commands in a separate terminal.
We would first run the build
command in one terminal, to create the bundle and wait for updates.
npm run build
We would next launch a second terminal and run the start
command to serve the site.
npm run start
Introduction to Webpack Rules
Webpack uses rules to know what to do with different file types. Webpack expects an array of rules
in a configuration option called module
. The syntax looks like:
module.exports =
{
module:
{
rules: []
}
}
A rule has a test
configuration option defined as a regular expression. If a file matches the regular expression, Webpack will use the rule on that file. For example, if we define test
as \.txt$\i
, the rule will apply to files ending in .txt
.
The other part of the rule needs to tell Webpack what to do with files that match the test
. That part of the rule varies by file type.
For .txt
files, the rule inside of rules
would look like:
rules:
[
{
test: /\.txt$/i,
type: 'asset/source'
}
]
Here, type: 'asset/source'
is telling Webpack that .txt
files are an asset that can be added directly to the source code, not requiring much processing. Once we add a rule for a file type, we can import files of that type into our code. Here’s an example with a .txt
file:
import Text from './example.txt';
document.querySelector('h1').innerHTML = Text;
Adding CSS to Our Builds
While we import .txt
files as assets, other file types often need loaders to get bundled by Webpack. Instead of a type
attribute, files that use one or several loaders require a use
attribute.
CSS files use two loaders, css-loader
and style-loader
. css-loader
takes the CSS out of a .css
file and adds it to the JavaScript code. style-loader
takes the output of css-loader
and puts it in a style tag in the HTML. We need both loaders and to apply css-loader
first. We specify multiple loaders with an array, and Webpack applies them in reverse order.
The rule for CSS files looks like:
{
test: /\.css$/i,
use: ['style-loader','css-loader']
}
We can add this rule anywhere within our rules array.
In a local environment, we’d also install the loaders as developer dependencies.
npm install --save-dev style-loader css-loader
We can then import CSS files directly into our JavaScript using another form of the import
statement:
import './style.css';
import Text from './example.txt';
document.querySelector('h1').innerHTML = Text;
When we build and start our preview server, the CSS is applied to the HTML!
Adding Images to Our Builds
Since Webpack 5, images and fonts no longer need a loader, and can use Webpack’s asset
system. Their rules are similar to that of .txt
files.
{
test: /\.(png|svg|jpg|jpeg|gif)$/i,
type: 'asset/resource'
}
Notice we use type
with asset/resource
rather than asset/source
. asset/resource
creates a file in the build and imports it into the code as a URL. You may want to explore more about asset types.
When an image rule has been defined in webpack.config.js
, we can import images into our JavaScript as if they were code:
import Square from '../square.png';
const img = document.createElement('img');
img.src = Square;
const body = document.querySelector('body');
body.appendChild(img);
When we build, the image is in the site!
Adding Fonts to Our Builds
{
test: /\.(woff|woff2|eot|ttf|otf)$/i,
type: 'asset/resource'
}
Unlike the other resources we’ve seen so far, font files are imported in CSS, not in JavaScript. We use a font in our CSS like so:
@font-face {
font-family: 'Roboto-Black';
src: url('../Roboto-Black.ttf');
}
h1 {
font-family: 'Roboto-Black';
}
The url
is the location of the font file. We use font-family
to define a name for our font that we can use later in the CSS.
When we build the project, we can see our fonts!