WebPack用于构建JavaScript捆绑包,但不支持HTML或CSS文件开箱即用。
要将JavaScript和CSS与HTML捆绑在一起,我们可以使用功能强大的html-bundler-webpack-plugin。
安装
第一步是安装插件:
npm install html-bundler-webpack-plugin --save-dev
安装样式的其他软件包:
npm install css-loader sass-loader sass --save-dev
一个简单的静态网站
我们创建了一些静态文件,以说明插件如何处理HTML中的引用到脚本,样式和图像源文件:
src/js/main.js
src/scss/styles.scss
src/views/index.html
src/images/favicon.ico
src/images/picture.png (size > 2 KB)
src/images/icon.png (size < 2 KB)
在src/view/
目录中创建index.html
文件:
<!doctype html>
<html lang="en">
<head>
<title>Home</title>
<link href="../images/favicon.ico" rel="shortcut icon">
<link href="../scss/styles.scss" rel="stylesheet">
<script src="../js/main.js" defer="defer"></script>
</head>
<body>
<h1>Homepage</h1>
<img src="../images/picture.png" alt="picture">
<img src="../images/icon.png" alt="icon">
</body>
</html>
要将我们的样式和脚本文件绑定到HTML,我们使用源文件的相对路径直接在HTML中指定它们。
具有复杂的文件结构,某些相对路径可能就像../../../images
。为了避免这种不可读的路径,建议使用WebPack别名。
该插件允许我们同时使用相对路径和WebPack别名。
我们将使用别名,因此我们将在WebPack resolve.alias
选项中定义它们:
{
'@scripts': path.join(__dirname, 'src/js'),
'@styles': path.join(__dirname, 'src/scss'),
'@images': path.join(__dirname, 'src/images'),
}
然后将index.html
中的所有相对路径更改为别名:
<!doctype html>
<html lang="en">
<head>
<title>Hello World</title>
<link href="@images/favicon.ico" rel="shortcut icon">
<link href="@styles/styles.scss" rel="stylesheet">
<script src="@scripts/main.js" defer="defer"></script>
</head>
<body>
<h1>Hello World!</h1>
<img src="@images/picture.png" alt="picture">
<img src="@images/icon.png" alt="icon">
</body>
</html>
所以HTML看起来更好,更干净。
处理HTML模板
WebPack不知道如何处理HTML模板,但是html-bundler-webpack-plugin可以处理模板并将生成的HTML放入输出目录中。
配置webpack.config.js
中的插件以处理index.html
:
const path = require('path');
const HtmlBundlerPlugin = require('html-bundler-webpack-plugin');
module.exports = {
output: {
path: path.resolve(__dirname, 'dist'),
clean: true,
},
plugins: [
new HtmlBundlerPlugin({
entry: {
// define templates here
index: 'src/views/index.html',
},
}),
],
// ...
};
我们可以在插件的entry
选项中定义HTML模板。
entry
键是没有.html
扩展名的输出文件名,该值是模板文件。因此我们可以在entry
选项中定义许多模板。
自动处理许多HTML模板
有一个HTML文件,但是在每次添加或删除文件时更新WebPack配置会很乏味。我们没有手动添加其他文件,让我们修改配置以自动包含它找到的所有HTML文件。
module.exports = {
// ...
plugins: [
new HtmlBundlerPlugin({
// relative or absolute path to templates
entry: 'src/views/',
}),
],
// ...
};
如果entry
选项是路径,则插件会自动找到所有模板,并将相同的目录结构保留在输出目录中。因此,我们可以在输入目录中创建许多模板,而无需重新启动以serve
模式运行的WebPack。
处理样式
要编译HTML中指定的SASS文件到CSS,请添加模块规则:
module.exports = {
// ...
module: {
rules: [
{
test: /\.(scss)$/,
use: ['css-loader', 'sass-loader'],
},
],
},
// ...
};
插件从编译的SASS文件中提取CSS,并将作为单独文件保存到输出目录中。
处理图像
插件解析了HTML中指定的图像引用,并允许WebPack将这些图像复制到输出目录。我们可以使用规则中的generator.filename
来定义图像的哈希输出文件名:
module.exports = {
// ...
module: {
rules: [
{
test: /\.(ico|png|jp?g|svg)$/,
type: 'asset/resource',
generator: {
filename: 'img/[name].[hash:8][ext]',
},
},
],
},
// ...
};
要优化小图像的加载,我们可以使用规则中的type: 'asset'
和parser.dataUrlCondition.maxSize
直接在HTML中插入图像:
module.exports = {
// ...
module: {
rules: [
{
test: /\.(ico|png|jp?g|svg)$/,
type: 'asset',
generator: {
// save images to file
filename: 'img/[name].[hash:8][ext]',
},
parser: {
dataUrlCondition: {
// inline images < 2 KB
maxSize: 2 * 1024
}
},
},
],
},
// ...
};
插件会自动内联图像较小的maxSize
。
JS和CSS的输出文件名
默认情况下,处理后的JS和CSS文件将在未解决的文件名下保存在输出目录中。但是建议使用哈希输出文件名。
我们可以在插件选项中定义JS和CSS的输出文件名:
module.exports = {
// ...
plugins: [
new HtmlBundlerPlugin({
entry: {
index: 'src/views/index.html',
},
js: {
// output filename for JS
filename: 'js/[name].[contenthash:8].js',
},
css: {
// output filename for CSS
filename: 'css/[name].[contenthash:8].css',
},
}),
],
// ...
};
可选,您可以使用js.inline和css.inline插件选项中的HTML中的JS和CSS。
现场重新加载
要在更改后启用浏览器的重新加载,请将devServer
选项添加到WebPack配置:
module.exports = {
// enable HMR with live reload
devServer: {
static: path.join(__dirname, 'dist'),
watchFiles: {
paths: ['src/**/*.*'],
options: {
usePolling: true,
},
},
},
};
最终的Webpack配置
const path = require('path');
const HtmlBundlerPlugin = require('html-bundler-webpack-plugin');
module.exports = {
mode: 'development',
output: {
path: path.resolve(__dirname, 'dist'),
clean: true,
},
resolve: {
alias: {
'@scripts': path.join(__dirname, 'src/js'),
'@styles': path.join(__dirname, 'src/scss'),
'@images': path.join(__dirname, 'src/images'),
},
},
plugins: [
new HtmlBundlerPlugin({
// path to templates
entry: 'src/views/',
js: {
// output filename for JS
filename: 'js/[name].[contenthash:8].js',
},
css: {
// output filename for CSS
filename: 'css/[name].[contenthash:8].css',
},
}),
],
module: {
rules: [
{
test: /\.(scss)$/,
use: ['css-loader', 'sass-loader'],
},
{
test: /\.(ico|png|jp?g|svg)/,
type: 'asset',
generator: {
filename: 'img/[name].[hash:8][ext]', // save to file images >= 2 KB
},
parser: {
dataUrlCondition: {
maxSize: 2 * 1024, // inline images < 2 KB
},
},
},
],
},
// enable HMR with live reload
devServer: {
static: path.resolve(__dirname, 'dist'),
watchFiles: {
paths: ['src/**/*.*'],
options: {
usePolling: true,
},
},
},
};
结论
生成的HTML包含处理后的文件的输出文件名和与小图的内衬。所有处理过的文件均在dist/
输出目录中。
<!doctype html>
<html lang="en">
<head>
<title>Home</title>
<link href="img/favicon.34fd67a8.ico" rel="shortcut icon">
<link href="css/styles.f042b772.css" rel="stylesheet">
<script src="js/main.b35246f4.js" defer="defer"></script>
</head>
<body>
<h1>Homepage</h1>
<img src="img/picture.7b396424.png" alt="picture">
<!-- src contains "data:image/png;base64,iVBORwSU..." -->
<img src="..." alt="icon">
</body>
</html>
仅使用一个html-bundler-webpack-plugin,您就可以很容易地设置WebPack来生成静态HTML文件。