demo 目录文件
1、初始化项目
npm init
2、安装依赖包 (注意只安装到开发环境中,因为依赖不需要在生产环境运行)
安装 webpack、webpack-cli、webpack-dev-server、html-webpack-plugin、vue(SSR)
npm i webpack -D
npm i webpack-cli -D
npm i webpack-dev-server -D
npm i html-webpack-plugin -D
npm i vue -D
安装成功完成后 package.json 应该多出以下配置(版本号可不同)
"devDependencies": {
"html-webpack-plugin": "^5.6.0",
"vue": "^3.4.5",
"webpack": "^5.89.0",
"webpack-cli": "^5.1.4",
"webpack-dev-server": "^4.15.1"
}
3、新建src目录,并创建 index.html、tag.html
index.html 文件,注意这里用到了 html-webpack-plugin 模板语法、vue 模板语法
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>About</title>
</head>
<body>
<!-- 插入 html-webpack-plugin 模板, 参考文档:https://github.com/jantimon/html-webpack-plugin -->
<div><%= htmlWebpackPlugin.options.tag %></div>
<!-- vue 模板语法, 参考文档: https://cn.vuejs.org/ -->
<p>我是index.html的内容 {{filename}}</p>
</body>
</html>
tag.html 文件,注意这里用到了 vue 的模板语法
<div>我是tag.html的内容 {{filename}}</div>
4、根目录新建 webpack.config.js、HotReload.js、VueLoad.js
webpack.config.js 配置文件
const path = require('path');
const fs = require('fs');
const HtmlWebpackPlugin = require('html-webpack-plugin')
const VueLoad = require('./VueLoad.js');
const HotReload = require('./HotReload.js');
module.exports = {
mode: 'development',
entry: {},
output: {
path: path.resolve(__dirname, 'dist'),
clean: true,
},
devServer: {
static: 'dist',
hot: true,
process: false,
},
devtool: 'inline-source-map',
plugins: [
new HtmlWebpackPlugin({
title: '首页',
tag: fs.readFileSync('./src/tag.html', 'utf8'),
template: './src/index.html',
filename: 'index.html',
minify: false,
}),
new VueLoad(),
new HotReload(),
],
optimization: {
runtimeChunk: 'single',
},
};
HotReload.js 热更处理文件,由于 webpack 热更监听js注入到 entry 的 js 文件bundle实现的;entry配置为空,所以html文件更新,浏览器无法自动刷新
const HtmlWebpackPlugin = require('html-webpack-plugin');
class HotReload {
apply(compiler) {
compiler.hooks.compilation.tap('HotReload', (compilation) => {
// Static Plugin interface |compilation |HOOK NAME | register listener
HtmlWebpackPlugin.getHooks(compilation).beforeEmit.tapAsync(
'HotReload',
(data, cb) => {
// 如果执行 npm run serve 命令 WebSocket 刷新浏览器监听注入到</body>
if (process.env.npm_lifecycle_event == 'serve') {
const i = data.html.lastIndexOf('</body>')
data.html = data.html.slice(0, i) +
' <script>(function(){new WebSocket(`ws://${window.location.host}/ws`).addEventListener("message", function(event){if(event.data.indexOf("invalid")!= -1){window.location.reload()}})})()</script>\r\n' +
data.html.slice(i);
}
cb(null, data)
}
)
})
}
}
module.exports = HotReload
VueLoad.js 解析 vue 模板代码
const HtmlWebpackPlugin = require('html-webpack-plugin');
const { createSSRApp } = require('vue');
const { renderToString } = require('vue/server-renderer');
class VueLoad {
apply(compiler) {
compiler.hooks.compilation.tap('VueLoad', (compilation) => {
HtmlWebpackPlugin.getHooks(compilation).beforeEmit.tapAsync(
'VueLoad',
async (data, cb) => {
// vue解析, 出入 data 数据,在index.html、tag.html中使用
data.html = await renderToString(createSSRApp({
data: () => ({ filename: data.plugin.options.filename }),
template: data.html
}))
cb(null, data)
}
)
})
}
}
module.exports = VueLoad
5、相关参考文档 webpack、html-webpack-plugin、vue
到这里就配置完成了,npm run serve 运行一下试试吧!
6、css、img、js 一起拷贝打包目录
npm i copy-webpack-plugin -D
在webpack.config.js配置引入使用
const path = require('path');
const fs = require('fs');
const HtmlWebpackPlugin = require('html-webpack-plugin')
const VueLoad = require('./VueLoad.js');
const HotReload = require('./HotReload.js');
const CopyPlugin = require('copy-webpack-plugin');
plugins: [
new HtmlWebpackPlugin({
title: '首页',
tag: fs.readFileSync('./src/tag.html', 'utf8'),
template: './src/index.html',
filename: 'index.html',
minify: false,
}),
new VueLoad(),
new HotReload(),
new CopyPlugin({
patterns: [
{ from: './src/css', to: 'css' },
{ from: './src/images', to: 'images' },
{ from: './src/js', to: 'js' },
],
}),
]
7、prettier 格式化打包后代码
安装 prettier
npm i prettier -D
根目录下创建 prettier.config.js
module.exports = {
useTabs: false,
singleQuote: true,
semi: false,
printWidth: 200,
htmlWhitespaceSensitivity: 'ignore', /* <span>、<a>、<b>等行内元素换行处理按钮块执行 */
};
htmlWhitespaceSensitivity 配置后 “行内元素” 默认为 display: block ,特殊代码处理如下:
<!-- input -->
<!-- display: inline -->
<span class="dolorum atque aspernatur">Est molestiae sunt facilis qui rem.</span>
<!-- output -->
<span class="dolorum atque aspernatur"
>Est molestiae sunt facilis qui rem.</span
>
<!-- input -->
<!-- display: block -->
<span class="dolorum atque aspernatur">Est molestiae sunt facilis qui rem.</span>
<!-- output -->
<!-- display: block -->
<span class="dolorum atque aspernatur">
Est molestiae sunt facilis qui rem.
</span>
<!-- 忽略代码块 -->
<!-- input -->
<!-- prettier-ignore-start -->
<span class="dolorum atque aspernatur">Est molestiae sunt facilis qui rem.</span>
<!-- prettier-ignore-end -->
<!-- output -->
<span class="dolorum atque aspernatur">Est molestiae sunt facilis qui rem.</span>
根目录下创建 Format.js
const HtmlWebpackPlugin = require('html-webpack-plugin');
const prettier = require('prettier');
const config = require('./prettier.config');
/**
* 代码格式化
*/
class Format {
apply(compiler) {
compiler.hooks.compilation.tap('Format', (compilation) => {
HtmlWebpackPlugin.getHooks(compilation).beforeEmit.tapAsync('Format', async (data, cb) => {
// prettier格式化
data.html = await prettier.format(data.html, { ...config, parser: 'html' });
cb(null, data);
});
});
}
}
module.exports = Format;
webpack.config.js 配置
const path = require('path');
const fs = require('fs');
const HtmlWebpackPlugin = require('html-webpack-plugin')
const VueLoad = require('./VueLoad.js');
const HotReload = require('./HotReload.js');
const CopyPlugin = require('copy-webpack-plugin');
const Format = require('./Format.js');
plugins: [
new HtmlWebpackPlugin({
title: '首页',
tag: fs.readFileSync('./src/tag.html', 'utf8'),
template: './src/index.html',
filename: 'index.html',
minify: false,
}),
new VueLoad(),
new HotReload(),
new Format(),
new CopyPlugin({
patterns: [
{ from: './src/css', to: 'css' },
{ from: './src/images', to: 'images' },
{ from: './src/js', to: 'js' },
],
}),
]
npm run build 运行一下试试,看看dist目录html文件被格式化后的效果吧!
如果格式化失败,.gitignore、.prettierignore、.eslintignore 是否忽略了 dist 目录,请先移除忽略在试试(此处不知道 prettier 为什么不执行 git 忽略项)prettier忽略规则;其他文件需要格式化请参考 prettier 文档
注意:<%= htmlWebpackPlugin.options.tag %> 因为不属于html规范,格式化出现缩进问题,可以加入prettier忽略代码块,如下:
<!-- prettier-ignore-start -->
<%= htmlWebpackPlugin.options.tag %>
<!-- prettier-ignore-end -->
8、打包后vue中对v-if产生的 <!----> 空注释,及 prettier-ignore 注释
可在 VueLoad.js 中加入一下正则去除
class VueLoad {
apply(compiler) {
compiler.hooks.compilation.tap('VueLoad', (compilation) => {
HtmlWebpackPlugin.getHooks(compilation).beforeEmit.tapAsync(
'VueLoad',
async (data, cb) => {
// vue解析, 出入 data 数据,在index.html、tag.html中使用
data.html = await renderToString(createSSRApp({
data: () => ({ filename: data.plugin.options.filename }),
template: data.html
}))
// 去掉多余注释信息
data.html = html.replace(/<!-- prettier-ignore-start -->|<!-- prettier-ignore-end -->|<!---->/g, '')
cb(null, data)
}
)
})
}
}
npm run build 运行一下再试试
9、遍历所有html模板和页面
创建模板存放目录 src/template,把 tag.html 放入template目录中
根目录下创建 BuildHtml.js
const fs = require('fs');
const glob = require('glob');
const HtmlWebpackPlugin = require('html-webpack-plugin');
// 获取所有模板
function getTemplate() {
const files = glob.sync('src/template/**.html');
const templates = {};
files.forEach(async (filePath) => {
const arr = filePath.split('/');
const templateName = arr[arr.length - 1];
const name = templateName.split('.')[0];
templates[name] = fs.readFileSync(filePath, 'utf8');
});
return templates;
}
// 构建所有页面
function BuildHtml() {
const files = glob.sync('src/**.html');
const htmlWebpack = [];
const templates = getTemplate();
files.forEach((filePath, index) => {
const arr = filePath.split('/');
const fileName = arr[arr.length - 1];
let main = fs.readFileSync(filePath, 'utf8');
htmlWebpack.push(
new HtmlWebpackPlugin({
inject: false,
tags: templates,
filename: fileName,
template: filePath,
minify: false,
})
)
})
return htmlWebpack;
}
module.exports = BuildHtml;
webpack.config.js 配置
const VueLoad = require('./VueLoad.js');
const HotReload = require('./HotReload.js');
const CopyPlugin = require('copy-webpack-plugin');
const Format = require('./Format.js');
const BuildHtml = require('./BuildHtml.js');
plugins: [
...BuildHtml(),
new VueLoad(),
new HotReload(),
new Format(),
new CopyPlugin({
patterns: [
{ from: './src/css', to: 'css' },
{ from: './src/images', to: 'images' },
{ from: './src/js', to: 'js' },
],
}),
]
使用模板
<div><%= htmlWebpackPlugin.options.tags.tag %></div>