Hi everyone, this blog is my attempt to document how webpack can help us. We use Webpack to bundle our own products, as well as using it with some of our framework examples. Although there are alternatives to Webpack, it is still very popular and with version 2.2 recently released I believe it will remain so for quite a while yet.
Webpack is a module bundler. It takes disparate dependencies, creates modules for them and bundles the entire network up into manageable output files. This is especially useful for Single Page Applications (SPAs), which is the defacto standard for Web Applications today.
Let’s assume we have an application that can perform two simple mathematical tasks — sum and multiply. We decide to split these functions into separate files for easier maintenance:
<html> <head> <script src="src/sum.js"></script> <script src="src/multiply.js"></script> <script src="src/index.js"></script> </head> </html>
var totalMultiply = multiply(5, 3); var totalSum = sum(5, 3); console.log('Product of 5 and 3 = ' + totalMultiply); console.log('Sum of 5 and 3 = ' + totalSum);
var multiply = function (a, b) { var total = 0; for (var i = 0; i < b; i++) { total = sum(a, total); } return total; };
var sum = function (a, b) { return a + b; };
The output of this would be:
Product of 5 and 3 = 15
Sum of 5 and 3 = 8
From the above code you can see that both multiply.js and index.js depend on sum.js.If we get the order wrong in index.html our application won’t work. If index.js is included before either of the other dependencies or if sum.js is included after multiply.js we will get errors. Finally, by using global variables, we risk other code overwriting our variables, causing hard to find bugs. Webpack can convert these dependencies into modules?—?they will have a much tighter scope (which is safer). Additionally, by converting our dependencies into Modules, Webpack can manage our dependencies for us?—?Webpack will pull in the dependant Modules at the right time, in the correct scope (we’ll see this in more detail later).
Making Dependencies Available, And Linking Them:
For our initial setup, we’ll use the CommonJS module syntax. There are other options (AMD, ES2015) but for now we’ll use CommonJS and later move to ES2015.CommonJS uses module.exports to export - or make available - functions or variables to other code. It uses require to then pull in these exported values.
<html>: <head>: <script src="./dist/bundle.js">:</script>: </head>: </html>:
var multiply = require('./multiply'); var sum = require('./sum'); var totalMultiply = multiply(5, 3); var totalSum = sum(5, 3); console.log('Product of 5 and 3 = ' + totalMultiply); console.log('Sum of 5 and 3 = ' + totalSum);
var sum = require('./sum'); var multiply = function (a, b) { var total = 0; for (var i = 0; i < b; i++) { total = sum(a, total); } return total; }; module.exports = multiply;
var sum = function (a, b) { return a + b; }; module.exports = sum;
Notice that we’ve made both sum and multiply available to other code and we've pulled in these exported functions in both multiple.js and index.js.Notice too that our index.html now only needs to pull in a single file — bundle.js.This is great! We now no longer have to worry about dependency order. We can expose what we want and keep other code effectively private. We also reduce web calls from 3 (sum.js, multiply.js and index.js) to a single call — this will help speed loading times.
Webpack Initial Configuration:
For the above to work, we need to do some initial Webpack configuration as shown below:
var path = require('path'); module.exports = { entry: './src/index.js', output: { path: path.resolve(__dirname, './dist/), filename: 'bundle.js } }
At a minimum, we need to tell Webpack what our application entry point is and what the resulting output should be.
entry: This is the main entry point of our application. This is where our initial loading and application logic will be. Webpack uses this as a starting point for its dependency tree walking. It will build up a dependency graph and create modules as necessary.
output.path: An absolute path for the resulting bundle. To make this cross platform and easy to use, we use a built-in Node.js function (path). This will help us to dynamically create an absolute path, relative to where we are.
output.filename: The filename of the resulting bundle. This can be anything, but by convention it's called 'bundle.js'
Note: __dirname is a Node.js utility variable - it is the directory name of the current file.
Looking at bundle.js
Looking at the resulting bundle.js can be very instructional (prettified and commented for easier navigation):
// the webpack bootstrap (function (modules) { // The module cache var installedModules = {}; // The require function function __webpack_require__(moduleId) { // Check if module is in cache // Create a new module (and put it into the cache) // Execute the module function // Flag the module as loaded // Return the exports of the module } // expose the modules object (__webpack_modules__) // expose the module cache // Load entry module and return exports return __webpack_require__(0); }) /************************************************************************/ ([ // index.js - our application logic /* 0 */ function (module, exports, __webpack_require__) { var multiply = __webpack_require__(1); var sum = __webpack_require__(2); var totalMultiply = multiply(5, 3); var totalSum = sum(5, 3); console.log('Product of 5 and 3 = ' + totalMultiply); console.log('Sum of 5 and 3 = ' + totalSum); }, // multiply.js /* 1 */ function (module, exports, __webpack_require__) { var sum = __webpack_require__(2); var multiply = function (a, b) { var total = 0; for (var i = 0; i < b; i++) { total = sum(a, total); } return total; }; module.exports = multiply; }, // sum.js /* 2 */ function (module, exports) { var sum = function (a, b) { return a + b; }; module.exports = sum; } ]);
From this you can see that Webpack wraps each of our files into a module and passes them into the Webpack bootstrap as an array of Modules. For each module, it adds them to the Webpack, executes them and makes them available to other modules. It executes __webpack_require__(0) which looking at the array of modules is our index.js. The result is the output we started with, but with far easier dependency management and less web traffic! Brilliant!