Webpack is a module bundler that allows us to organize our code, make it modular, therefore creating more maintainable applications. It has become one of the most popular tools for modern web development.
Using task runners and other tools, we had the ability to compile all the modules into a single source before. But webpack allows us to import a lot of other assets as modules beside JS, including CSS, HTML, images and fonts. It also adds the ability to easily use packages from npm.
How does it work?
Webpack basically takes modules with dependencies and generates static assets.
Webpack processes our application and builds a dependency graph recursively. This graph includes every module needed by our app. The modules are packaged into a small number of bundles – often only one – to be loaded by the browser.
Webpack can be configured by creating a JS file ‘webpack.config.js’ with configuration options which will be passed in webpack start command.
If you ever encountered some of the following:
- loading dependencies out of order,
- including unused code in production,
- loading the same library twice, scoping issues in JS or CSS,
- needing to optimize packages and assets loading but fearing you will break something
…then you could probably benefit from using webpack. It handles all the above and you can stop worrying about dependencies and load order.
The starting point of the dependency graph built by webpack is known as an entry point. It tells webpack where to start and follows the imports to know what to include in the bundle. You can also think of the entry as the application root file or the first file to kick off your app.
The entry point is defined using webpack’s entry property in our webpack configuration object. You can define many entries and they will generate as many final bundles to use in different parts of your application.
For example, if we can have a web app with a client side and an admin side, we do not need all the dependencies used for the client side on the admin side as well. We can add two entries in the webpack configuration file and it will generate two separate bundles.
After all the needed modules are bundled together, we still need to tell webpack where to emit them. For this, we use the webpack output property that sets the location for the new bundles.
The output.filename and output.path properties are used to specify the name of our bundle and the location where we want it to be emitted. In our example, using [name].js as filename, the 2 output bundles will have the same names as the entries: ‘client.js’ and ‘admin.js’.
Loaders are processed in reverse array order, meaning that in our example css-loader will run before style-loader.
Below are some popular loaders classified by purpose:
babel-loader – loads ES2015+ code and transpiles it to ES5 using Babel
- Files and Templating:
html-loader – exports HTML as string and require to static resources
file-loader – emits the file into the output folder and returns its corresponding URL
style-loader – adds CSS to DOM by inserting a <style> tag
css-loader – resolves CSS imports and returns CSS code
less-loader – loads and compiles LESS files
sass-loader – loads and compiles SASS/SCSS files
postcss-loader – loads and transforms CSS using PostCSS
- Linting and Testing:
mocha-loader – loads tests that use mocha
eslint-loader – preLoader using ESLint
jshint-loader – preLoader using JSHint
Webpack provides 3 stages for running loaders: preLoaders, loaders and postLoaders, executed in this order. Code linting needs to be done before the file is processed and added to the bundle, so in this case we use preLoaders.
The purpose of plugins is to do everything else that a loader cannot do. An interesting fact is that webpack is built on the same plugin system that you are using in your webpack configuration.
A very useful and popular plugin is ExtractTextPlugin, a plugin that extracts text from a bundle into a different file. It can be used for separating CSS from JS. As webpack builds the dependency graph, this plugin moves all the required *.css modules into a separate file. The benefit is that your styles are no longer inline into the JS bundle but in a separate CSS file. So, a complex application, with a lot of style modules will be faster using ExtractTextPlugin because the CSS bundle will be loaded in parallel to the JS bundle.
Plugins can be installed using npm. Below, you can find a webpack configuration example using ExtractTextPlugin to create a CSS bundle named ‘style.css’.
The plugin is required, instantiated in the plugins array and used in module.rules. You can specify the output file name, the loaders and fallback loaders to use and some other useful options.
Getting started with webpack
First we need to install webpack. From our project folder, we can open a terminal window and run the following npm command to install and save webpack in our local project packages:
npm install -–save-dev webpack
webpack is started using:
npm run webpack
Then we will create a ‘webpack.config.js’ file in our project directory containing a basic webpack configuration like the example below.
With the above configuration, webpack will look for entry file names, in our case ‘app.js’. It will read the content and process every import or require dependency found, adding it to the bundle. It keeps doing so until the dependency graph is complete. In this way, webpack will only add to the bundle what is needed, leaving the unused resources aside.
Once the bundle is complete, webpack names the bundle using output.filename and adds it in the output.path folder. In our case, [name] is replaced with entry file name, resulting ‘app.bundle.js’.
resolve.extensions contains all the extensions that need to be processed by webpack. In this case, files other than *.css or *.js will be ignored.
Webpack-dev-server is an easy to setup development server with live reload. We start by installing dev server with npm:
npm install –save-dev webpack-dev-server
After install, you can start dev server with:
npm webpack-dev-server –open
The simplest way to configure your server is by adding devServer with a set of options into ‘webpack.config.js’
The example above will compress and serve everything from “dist” directory at http://localhost:9000/.
Below you can find a list with some other useful configuration options:
–headers: adds headers to responses.
–host: used host, by default this is localhost.
–proxy: enable proxying URLs, uses http-proxy-middleware package that is highly configurable.
–hot: adds the HotModuleReplacementPlugin to switch the server to hot mode.
–lazy: no watching, compiles on request (cannot be combined with –hot).
–https: serves webpack-dev-server over HTTPS Protocol. Includes a self-signed certificate that is used when serving the requests.
–publicPath: the bundled files will be available in the browser under this path.
–watchContentBase: this tells the server to watch the files served in contentBase; in case of file change, it triggers full page reload
–watchOptions: specifies options related to watch content base
Hot Module Replacement
A development server with live reloading can be achieved using other tools too, but what is really interesting and powerful in webpack is the possibility to include Hot Module Replacement (HMR).
HMR replaces, changes, adds or removes modules while an application is running, without doing a full page reload. This is very useful because:
- the application keeps its state, which would be lost in a full reload
- we can save development time because code changes are applied immediately
- it works with CSS also, so it becomes fast to do style fixes directly in source files instead of browser debugger console
- HMR can be easily integrated with React to allow you to change components’ code without losing the states and properties
- HMR can also be used with Angular and it enables module reloading for templates, controller methods and services
HMR – how does it work?
Hot module replacement runtime executes a series of steps in order to swap modules in and out of the running application:
- The application asks HMR to check for updates
- HMR downloads the updates asynchronously and notifies the application
- The application asks HMR to apply the changes
- HMR applies the updates synchronously
Below is an example on how to enable HMR using HotModuleReplacementPlugin and setting devServer.hot option to true.
Read more on this subject by following the links below:
- webpack official documentation
- Getting started with webpack 2
- webpack – tips and tricks
Latest posts by Diana
- An Introduction to Webpack - September 2, 2017