What is webpack?
Webpack is a powerful module bundler for JavaScript applications. It processes applications by internally building a dependency graph which maps every module your project needs and generates one or more bundles. It is highly extensible via loaders and plugins, and it's designed to manage, transform, and bundle frontend assets like JavaScript, CSS, and images.
What are webpack's main functionalities?
Module Bundling
Webpack bundles all the JavaScript files and other assets like CSS and images into a single output file. The code sample shows a basic webpack configuration defining an entry point and the output bundle.
module.exports = {
entry: './path/to/my/entry/file.js',
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'my-first-webpack.bundle.js'
}
};
Loaders
Loaders allow webpack to process different types of files and convert them into modules that can be included in your bundle. The code sample demonstrates how to use loaders to handle .txt and .css files.
module.exports = {
module: {
rules: [
{ test: /\.txt$/, use: 'raw-loader' },
{ test: /\.css$/, use: ['style-loader', 'css-loader'] }
]
}
};
Plugins
Plugins can be leveraged to perform a wider range of tasks like bundle optimization, asset management, and environment variable injection. The code sample shows how to use the HtmlWebpackPlugin to generate an index.html file with the bundled assets injected.
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
plugins: [new HtmlWebpackPlugin({ template: './src/index.html' })]
};
Development Server
Webpack provides a development server that can be used to serve your application during development. It supports live reloading. The code sample configures the webpack development server to serve files from the 'dist' directory.
module.exports = {
devServer: {
contentBase: './dist',
open: true
}
};
Code Splitting
Code splitting allows you to split your code into various bundles which can then be loaded on demand or in parallel. The code sample shows how to split the application and vendor code into separate bundles.
module.exports = {
entry: {
app: './src/app.js',
vendor: './src/vendor.js'
},
output: {
filename: '[name].bundle.js',
path: __dirname + '/dist'
}
};
Other packages similar to webpack
rollup
Rollup is a module bundler for JavaScript which uses a flat bundle approach that's more efficient for libraries and applications with a complex module structure. It's known for its tree-shaking capabilities, which eliminate unused code.
parcel
Parcel is a web application bundler that offers a zero-configuration setup. It's known for its fast bundle times and out-of-the-box support for many file types without the need for additional plugins or loaders.
browserify
Browserify lets you require('modules') in the browser by bundling up all of your dependencies. It's been around longer than webpack and has a simpler approach, but it lacks some of the more advanced features and optimizations that webpack offers.
fuse-box
FuseBox is a bundler/module loader that combines the power of webpack, JSPM, and SystemJS. It introduces a streamlined workflow and has a powerful API. It's known for its speed and simplicity.
modules-webpack
As developer you want to reuse existing code.
As with node.js and web all file are already in the same language, but it is extra work to use your code with the node.js module system and the browser.
The goal of webpack
is to bundle CommonJs modules into javascript files which can be loaded by <script>
-tags.
Simply concating all required files has a disadvantage: many code to download (and execute) on page load.
Therefore webpack
uses the require.ensure
function (CommonJs/Modules/Async/A) to split your code automatically into multiple bundles which are loaded on demand.
This happens mostly transparent to the developer with a single function call. Dependencies are resolved for you.
The result is a smaller inital code download which results in faster page load.
TL;DR
- bundle CommonJs modules for browser
- reuse server-side code (node.js) on client-side
- create multiple files which are loaded on demand (faster page load in big webapps)
- dependencies managed for you, on compile time
Goals
- minimize code size
- minimize code size on inital download
- download code only on demand
- hide development details, like module names and folder structure
- require minimal configuration
- load polyfills for node-specific things if used
- offer replacements for node buildin libaries
Example
var b = require("./b");
b.stuff("It works");
exports.stuff = function(text) {
console.log(text);
}
are compiled to
()
({
0: function(module, exports, require) {
var b = require(1);
b.stuff("It works");
},
1: function(module, exports, require) {
exports.stuff = function(text) {
console.log(text);
}
}
})
Code Splitting
Example
var a = require("a");
var b = require("b");
require.ensure(["c"], function(require) {
require("b").xyz();
var d = require("d");
});
File 1: web.js
- code of that file
- code of module a and dependencies
- code of module b and dependencies
File 2: 1.web.js
- code of module c and dependencies (but code is not used)
- code of module d and dependencies
See details for exact output.
Reusing node.js code
webpack
was built to support most of the code that was coded for node.js environment.
For example this works out of the box:
require("./templates/" + templateName);
require(condition ? "moduleA" : condition2 ? "moduleB" : "./localStuff");
function xyz(require) { require("text"); } xyz(function(a) { console.log(a) });
var r = require; r("./file");
with warningfunction xyz(require) { require("./file"); } xyz(require);
with warningtry { require("missingModule"); } catch(e) { console.log("missing") }
with warningvar require = function(a) { console.log(a) }; require("text");
if(condition) require("optionalModule")
with warning if missing
Browser replacements
Somethings it happens that browsers require other code than node.js do.
webpack
allow module developers to specify replacements which are used in the compile process of webpack
.
Modules in web_modules
replace modules in node_modules
.
filename.web.js
replaces filename.js
when required without file extention.
TODO specify replacements in options
require.context
If the require
d module is not known while compile time we get into a problem.
A solution is the method require.context
which takes a directory as parameter
and returns a function which behaves like the require
function issued from a file
in this directory (but only if used for files in that directory).
Example
We have a directory full of templates, which are compiled javascript files.
A template should be loaded by template name.
var requireTemplate = require.context("./templates");
function getTemplate(templateName) {
return requireTemplate("./" + templateName);
}
In addition to that webpack
uses the require.context
function automatically
if you use variables or other non-literal things in the require
function.
That means the following code behaves like the above:
function getTemplate(templateName) {
return require("./templates/" + templateName);
}
See details for complete example.
When try to store the require
function in another variable or try to pass it as parameter,
webpack
convert it to a require.context(".")
to be combatible.
There is a warning emitted in this case.
Warning: The complete code in the directory are included. So use it carefully.
Limitations
require
-function
As dependencies are resolved before running:
require.ensure
should not be overwritten or called indirectrequire.context
should not be overwritten or called indirect- the argument to
require.context
should be a literal or addition of multiple literals - An indirect call of
require
should access a file in current directory: This throws an exception: var r = require; r("../file");
The following cases could result in too much code in result file if used wrong:
- indirect call of
require
: var r = require; r("./file");
require.context
. It includes the whole directory.- expressions in require arguments:
require(variable)
, webpack is smart enough for this require(condition ? "a" : "b")
- the function passed to
require.ensure
is not inlined in the call.
node.js specific modules
As node.js specific modules like fs
will not work in browser they are not included and cause an error.
You should replace them by own modules if you want to use them.
For some modules are replacements included in webpack
.
Some credit goes to the browserify contributors, as I took some replacements from them.
web_modules
fs
path
...
TODO provide some replacements (half way done...)
Usage
Shell
webpack
offers a command line interface:
after npm install webpack -g
you can use the webpack
command
if invoked without arguments it prints a usage:
Usage: webpack <options> <input> <output>
Options:
--single Disable Code Splitting [boolean] [default: false]
--min Minimize it with uglifyjs [boolean] [default: false]
--filenames Output Filenames Into File [boolean] [default: false]
--options Options JSON File [string]
--script-src-prefix Path Prefix For JavaScript Loading [string]
--libary Stores the exports into this variable [string]
--colors Output Stats with colors [boolean] [default: false]
--json Output Stats as JSON [boolean] [default: false]
--alias Set a alias name for a module. ex. http=http-browserify [string]
Programmatically Usage
webpack(context, moduleName, [options], callback)
webpack(absoluteModulePath, [options], callback)
options
you can save this options object in a JSON file and use it with the shell command.
outputJsonpFunction
JSONP function used to load chunks
scriptSrcPrefix
Path from where chunks are loaded
outputDirectory
write files to this directory (absolute path)
output
write first chunk to this file
outputPostfix
write chunks to files named chunkId plus outputPostfix
libary
exports of input file are stored in this variable
minimize
minimize outputs with uglify-js
includeFilenames
add absolute filenames of input files as comments
callback
function(err, source / stats)
source
if options.output
is not set
else stats
as json see example
Comparison
Feature
|
sokra/ modules- webpack
|
medikoo/ modules- webmake
|
substack/ node- browserify
|
---|
single bundle
|
yes
|
yes
|
yes
|
multiple bundles, Code Splitting
|
yes
|
no
|
no
|
indirect require
var r = require; r("./file");
|
in directory
|
include by config option
|
no
|
concat in require
require("./fi" + "le")
|
yes
|
yes
|
no
|
variables in require (local)
require("./templates/"+template)
|
yes, complete directory included
|
include by config option
|
no
|
variables in require (global)
require(moduleName)
|
no
|
include by config option
|
no
|
node buildin libs
require("http");
|
some
|
no
|
many
|
process polyfill
|
yes, on demand
|
no
|
yes, ever
|
module polyfill
|
yes, on demand
|
no
|
no
|
require.resolve
|
no
|
no
|
yes
|
global to window mapping
|
yes
|
no
|
no
|
requirable files
|
filesystem
|
directory scope
|
filesystem
|
different modules with same name
|
yes
|
no
|
yes
|
eliminate duplicate code
|
yes
|
no
|
yes
|
require JSON
|
no
|
no
|
no
|
plugins
|
no
|
no
|
yes
|
compile coffee script
|
no
|
no
|
yes
|
watch mode
|
no
|
no
|
yes
|
debug mode
|
no
|
no
|
yes
|
libaries
|
on global obj
|
no
|
requirable
|
browser replacements
|
web_modules and .web.js
|
no
|
by alias config option
|
compiles with (optional) modules missing
|
yes, emit warnings
|
no
|
no
|
Tests
You can run the unit tests with npm test
.
You can run the browser tests:
cd test/browsertests
node build
and open test.html
in browser. There must be several OKs in the file and no FAIL.
TODO more tests
Contribution
You are welcome to contribute by writing issues or pull requests.
You are also welcome to correct any spelling mistakes or any language issues, because my english is not so good...
License
MIT (http://www.opensource.org/licenses/mit-license.php)