This guide walks through manually configuring a Vue.js application using Webpack 5—bypassing vue-cli to gain deeper insight into the underlying toolchain. The setup targets modern development workflows, emphasizing modularity, performance, and maintainability.
Core Webpack Configuration
Webpack 5 introduces built-in asset modules and drops legacy loaders like file-loader and url-loader. Its five foundational concepts remain unchanged:
- Entry: Starting point(s) for dependency graph traversal.
- Output: Where bundled assets are emitted (requires absolute paths).
- Loader: Transforms files before adding them to the dependency graph.
- Plugin: Extends webpack’s capabilities beyond module transformation.
- Mode: Determines built-in optimizations (
developmentorproduction).
A minimal configuration skeleton:
const path = require('path');
module.exports = {
entry: './src/main.js',
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'assets/js/bundle.[contenthash:8].js',
clean: true
},
module: {
rules: []
},
plugins: [],
mode: 'development'
};
Handling Styles
CSS Integration
Use css-loader to interpret @import and url(), and style-loader to inject styles into the DOM during development:
npm install --save-dev css-loader style-loader
{
test: /\.css$/i,
use: ['style-loader', 'css-loader']
}
Extracting CSS into Separate Files
In production, extract CSS into standalone files using MiniCssExtractPlugin:
npm install --save-dev mini-css-extract-plugin
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
// In rules:
{
test: /\.css$/i,
use: [MiniCssExtractPlugin.loader, 'css-loader']
}
// In plugins:
new MiniCssExtractPlugin({
filename: 'assets/css/styles.[contenthash:8].css'
})
PostCSS & Browser Compatibility
Add vendor prefixing and modern feature fallbacks via PostCSS:
npm install --save-dev postcss-loader postcss postcss-preset-env
{
test: /\.css$/i,
use: [
MiniCssExtractPlugin.loader,
'css-loader',
{
loader: 'postcss-loader',
options: {
postcssOptions: {
plugins: ['postcss-preset-env']
}
}
}
]
}
Define target browsers in package.json:
"browserslist": ["last 2 versions", "> 1%", "not dead"]
CSS Minification
Enable compression in production:
npm install --save-dev css-minimizer-webpack-plugin
const CssMinimizerPlugin = require('css-minimizer-webpack-plugin');
module.exports = {
// ...
optimization: {
minimizer: [
new CssMinimizerPlugin()
]
}
};
Preprocessor Support
For .less, .scss, or .sass files:
npm install --save-dev less-loader sass-loader sass stylus-loader
{
test: /\.less$/i,
use: [MiniCssExtractPlugin.loader, 'css-loader', 'less-loader']
},
{
test: /\.s[ac]ss$/i,
use: [MiniCssExtractPlugin.loader, 'css-loader', 'sass-loader']
}
Asset Management
Images & Fonts
Leverage Webpack 5’s native asset module types:
// Inline small images as Base64
{
test: /\.(png|jpe?g|gif|webp)$/i,
type: 'asset',
parser: {
dataUrlCondition: { maxSize: 10 * 1024 }
},
generator: {
filename: 'assets/images/[name].[contenthash:6][ext]'
}
},
// Copy larger assets and fonts as-is
{
test: /\.(woff2?|ttf|eot|svg)$/i,
type: 'asset/resource',
generator: {
filename: 'assets/fonts/[name].[contenthash:8][ext]'
}
}
JavaScript Tooling
Type Safety & Linting
Integrate ESLint for code quality enforcement:
npm install --save-dev eslint-webpack-plugin eslint
const ESLintPlugin = require('eslint-webpack-plugin');
module.exports = {
// ...
plugins: [
new ESLintPlugin({
context: path.resolve(__dirname, 'src'),
cache: true,
cacheLocation: path.resolve(__dirname, 'node_modules/.cache/eslint')
})
]
};
Sample .eslintrc.cjs:
module.exports = {
root: true,
env: { browser: true, es2021: true, node: true },
extends: ['eslint:recommended', 'plugin:vue/vue3-essential'],
parserOptions: {
ecmaVersion: 'latest',
sourceType: 'module',
parser: '@babel/eslint-parser'
},
rules: {
'vue/multi-word-component-names': 'off',
'no-console': process.env.NODE_ENV === 'production' ? 'warn' : 'off'
}
};
Babel Transpilation
Support modern JavaScript syntax across environments:
npm install --save-dev babel-loader @babel/core @babel/preset-env @babel/plugin-transform-runtime
{
test: /\.js$/i,
include: path.resolve(__dirname, 'src'),
use: {
loader: 'babel-loader',
options: {
presets: ['@babel/preset-env'],
plugins: ['@babel/plugin-transform-runtime']
}
}
}
HTML Integration
Automatically generate and inject bundle references:
npm install --save-dev html-webpack-plugin
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
// ...
plugins: [
new HtmlWebpackPlugin({
template: './public/index.html',
minify: {
collapseWhitespace: true,
removeComments: true
}
})
]
};
Development Experience
Hot Module Replacement (HMR)
Enable live updates without full page reloads:
devServer: {
host: 'localhost',
port: 8080,
open: true,
hot: true,
compress: true
}
Source Maps
Map generated code back to original sources:
// Development
devtool: 'cheap-module-source-map'
// Production
devtool: 'source-map'
Build Performance Optimizations
- Threaded Loaders: Use
thread-loaderwithos.cpus().lengthworkers. - Caching: Enable
cacheDirectoryin Babel andcachein ESLint. - OneOf Rules: Prevent redundant rule matching with
oneOf.
Vue-Specific Setup
Vue Loader & Runtime
Install core Vue tooling:
npm install vue
npm install --save-dev vue-loader vue-template-compiler @vue/compiler-sfc
const { VueLoaderPlugin } = require('vue-loader');
module.exports = {
// ...
module: {
rules: [
{
test: /\.vue$/i,
loader: 'vue-loader'
}
]
},
plugins: [new VueLoaderPlugin()],
resolve: {
extensions: ['.vue', '.js', '.ts'],
alias: {
'@': path.resolve(__dirname, 'src')
}
}
};
Style Scoping & Preprocessors
Replace style-loader with vue-style-loader for scoped styles:
npm install --save-dev vue-style-loader
{
test: /\.vue$/i,
use: ['vue-style-loader', 'css-loader', 'postcss-loader']
}
Environment Definitions
Expose Vue-spceific flags at compile time:
npm install --save-dev webpack DefinePlugin
const webpack = require('webpack');
module.exports = {
plugins: [
new webpack.DefinePlugin({
__VUE_OPTIONS_API__: JSON.stringify(true),
__VUE_PROD_DEVTOOLS__: JSON.stringify(false)
})
]
};
Production Enhancements
- Tree Shaking: Enabled by default for ES modules; ensure all dependencies export properly.
- Code Splitting: Use dynamic
import()for route-based or component-level chunks. - PWA Support: Integrate
workbox-webpack-pluginfor offline capability.
For full implementation details and working examples, refer to the companion repository.