JavaScript modules evolved from immediate invocation patterns to standardized ES6 imports. Early approaches used closures to encapsulate variables and prevent global namespace pollution:
const configModule = (() => {
const apiEndpoint = 'https://api.example.com';
const retryCount = 3;
return { apiEndpoint, retryCount };
})();
While effective for isolation, sharing functionality across files required explicit attachment to global objects. Modern module standards like ES6 Modules and CommonJS resolved this through explicit dependency graphs and import/export syntax.
ES6 Module Syntax
Named exports allow multiple values per file:
// constants.js
export const MAX_RETRY = 3;
export const TIMEOUT = 5000;
export function calculateTotal(price, quantity) {
return price * quantity;
}
export class DataProcessor {
transform(data) {
return data.map(item => item.toUpperCase());
}
}
Default exports suit single-purpose modules, allowing importers to assign custom names:
// logger.js
export default function logMessage(level, text) {
console.log(`[${level.toUpperCase()}] ${text}`);
}
// consumer.js
import logError from './logger.js';
logError('critical', 'Connection failed');
Webpack Architecture
Webpack operates as a static module bundler, constructing a dependency graph from entry points and emitting optimized assets for browser consumption. Unlike Grunt or Gulp—which automate sequential tasks on files—webpack focuses on module resolution, dependency management, and asset transformation through a unified processing pipeline.
Task runners excel at file concatenation and minification for non-modular projects, whereas webpack manages complex dependency trees, enabling code splitting, lazy loading, and sophisticated asset optimization strategies.
Installation and Initialization
Ensure Node.js version 12 or higher is installed. Initialize the project and install webpack locally to maintain version consistency across environments:
npm init -y
npm install webpack webpack-cli --save-dev
Local installation is preferred over global installation, ensuring CI/CD pipelines and team members use identical webpack versions through npm scripts.
Basic Configuration
Create webpack.config.js to define entry points and output destinations:
const path = require('path');
module.exports = {
entry: './src/index.js',
output: {
filename: 'main.[contenthash:8].js',
path: path.resolve(__dirname, 'dist'),
clean: true
},
mode: 'development'
};
The contenthash suffix enables efficient browser caching strategies, while clean: true removes stale build artifacts before each compilation.
Processing Stylesheets
CSS requires specific loaders to interpret imports and inject styles into the DOM:
npm install --save-dev style-loader css-loader
Configuration chain for CSS processing:
module.exports = {
module: {
rules: [
{
test: /\.css$/i,
use: ['style-loader', 'css-loader']
}
]
}
};
For Sass preprocessing, install the additional compiler and loader:
npm install --save-dev sass-loader sass
Extend the rules array to handle SCSS files:
{
test: /\.scss$/i,
use: ['style-loader', 'css-loader', 'sass-loader']
}
Loaders execute right-to-left: sass-loader compiles SCSS to CSS, css-loader interprets @import and url() statements, and style-loader injects the result into the document head.
Asset Optimization
Handle images and fonts using webpack's built-in asset modules, which automatically select between inline data URIs and separate files based on size thresholds:
module.exports = {
module: {
rules: [
{
test: /\.(png|svg|jpg|jpeg|gif)$/i,
type: 'asset',
parser: {
dataUrlCondition: {
maxSize: 8 * 1024 // Inline assets under 8KB
}
},
generator: {
filename: 'images/[name].[hash:8][ext]'
}
}
]
}
};
Transpilation Pipeline
Convert modern JavaScript for legacy browser support using Babel:
npm install --save-dev babel-loader @babel/core @babel/preset-env
Configuration to process JavaScript files:
{
test: /\.m?js$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader',
options: {
presets: ['@babel/preset-env']
}
}
}
Vue.js Integration
Install the framework and specific loaders for Single File Components:
npm install vue
npm install --save-dev vue-loader vue-template-compiler
Configure resolution aliases to ensure the correct Vue build is used:
const { VueLoaderPlugin } = require('vue-loader');
module.exports = {
resolve: {
alias: {
'vue$': 'vue/dist/vue.esm.js'
},
extensions: ['.js', '.vue', '.json']
},
plugins: [
new VueLoaderPlugin()
],
module: {
rules: [
{
test: /\.vue$/,
loader: 'vue-loader'
}
]
}
};
Single File Components encapsulate template, logic, and scoped styles:
<template>
<div class="container">
<h1>{{ greeting }}</h1>
<button @click="increment">Count: {{ counter }}</button>
</div>
</template>
<script>
export default {
data() {
return {
greeting: 'Welcome to Vue with Webpack',
counter: 0
};
},
methods: {
increment() {
this.counter += 1;
}
}
};
</script>
<style scoped>
.container {
padding: 20px;
background: #f5f5f5;
}
</style>
Plugin Ecosystem
Plugins extend webpack's core functionality beyond module loading. Inject banner comments for licensing:
const webpack = require('webpack');
module.exports = {
plugins: [
new webpack.BannerPlugin({
banner: 'Licensed under MIT License'
})
]
};
Generate HTML files automatically to serve the bundles:
npm install --save-dev html-webpack-plugin
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
plugins: [
new HtmlWebpackPlugin({
template: './public/index.html',
title: 'Production Application'
})
]
};
Minify JavaScript for production deployments:
const TerserPlugin = require('terser-webpack-plugin');
module.exports = {
optimization: {
minimize: true,
minimizer: [new TerserPlugin()]
}
};
Development Server
Enable rapid iteration with hot module replacement:
npm install --save-dev webpack-dev-server
Configure the development environment:
module.exports = {
devServer: {
static: {
directory: path.join(__dirname, 'dist')
},
compress: true,
port: 8080,
hot: true,
open: true
}
};
Update package.json scripts to utilize environment-specific configurations:
{
"scripts": {
"start": "webpack serve --config webpack.dev.js",
"build": "webpack --config webpack.prod.js"
}
}
Configuration Splitting
Maintain separate configurations for different environments using webpack-merge to eliminate conditional logic and ensure optimal settings for each target:
npm install --save-dev webpack-merge
Common configuration (webpack.common.js):
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
entry: './src/index.js',
plugins: [
new HtmlWebpackPlugin({ template: './src/index.html' })
],
output: {
filename: '[name].bundle.js',
path: path.resolve(__dirname, 'dist')
}
};
Development configuration (webpack.dev.js):
const { merge } = require('webpack-merge');
const common = require('./webpack.common.js');
module.exports = merge(common, {
mode: 'development',
devtool: 'inline-source-map',
devServer: {
static: './dist'
}
});
Production configuration (webpack.prod.js):
const { merge } = require('webpack-merge');
const common = require('./webpack.common.js');
const TerserPlugin = require('terser-webpack-plugin');
module.exports = merge(common, {
mode: 'production',
optimization: {
minimizer: [new TerserPlugin()]
}
});