如何从 Webpack 4 中的页眉/页脚(和 css/js 注入)生成静态页面的 html 模板(lodash 模板不起作用)?
Posted
技术标签:
【中文标题】如何从 Webpack 4 中的页眉/页脚(和 css/js 注入)生成静态页面的 html 模板(lodash 模板不起作用)?【英文标题】:How to generate html template for static page from header/footer (and css/js inject) in Webpack 4 (lodash template not working)? 【发布时间】:2020-08-06 05:28:59 【问题描述】:我正在尝试使用 webpack 4 构建静态 html 页面。我正在使用 html-webpack-plugin。我想定义一些 header.html 和 footer.html 并稍后在所有 html 页面中导入它们。但我还希望我的输出 js 和 css 文件自动注入到这个页眉和页脚。像这样的:
header.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<title>Some Title</title>
<% for(var i=0; i < htmlWebpackPlugin.files.css.length; i++) %>
<link type="text/css" rel="stylesheet" href="<%= htmlWebpackPlugin.files.css[i] %>">
<% %>
</head>
<body>
footer.html:
<% for(var i=0; i < htmlWebpackPlugin.files.js.length; i++) %>
<script type="text/javascript" src="<%= htmlWebpackPlugin.files.js[i] %>"></script>
<% %>
</body>
</html>
index.hbs(或 index.html):
<%= _.template(require('./../includes/header.html'))() %>
<div class="content">
<img src="img/bg/desktop/bg-index-03.jpg" />
<div class="imgtest"></div>
</div> <!-- .content -->
<%= _.template(require('./../includes/footer.html'))() %>
但我在 dist/index.html 中遇到了一些错误。这些行仍然相同,并且不会将页眉/页脚加载到 index.html:
<%= _.template(require('./../includes/header.html'))() %>
...
<%= _.template(require('./../includes/footer.html'))() %>
由于某种原因 lodash 不起作用...
更新:
我将src/html/views/index.hbs
更改为src/html/views/index.html
(并在插件中)
new HtmlWebpackPlugin(
template: './src/html/views/index.html',
...
在此处添加include
行:
test: /\.html$/,
include: path.resolve(__dirname, 'src/html/includes'),
use: ['html-loader']
,
在页眉/页脚中,我删除了 lodash 代码 <% ... %>
,只留下干净的 html - 所以它可以工作!在 index.html 中导入页眉/页脚,但没有 css/js。
如果我在 footer.html(或 header.html)中返回 lodash
<% for(var i=0; i < htmlWebpackPlugin.files.js.length; i++) %>
<script type="text/javascript" src="<%= htmlWebpackPlugin.files.js[i] %>"></script>
<% %>
我得到错误:
ERROR in Template execution failed: ReferenceError: htmlWebpackPlugin is not defined
ERROR in ReferenceError: htmlWebpackPlugin is not defined
- lodash.templateSources[2]:10 eval
lodash.templateSources[2]:10:19
- index.html:102
D:/.../src/html/views/index.html:102:110
- index.html:104 ./node_modules/html-webpack-plugin/lib/loader.js!./src/html/views/index.html.module.exports
D:/.../src/html/views/index.html:104:3
- index.js:393
[project]/[html-webpack-plugin]/index.js:393:16
- runMicrotasks
- task_queues.js:93 processTicksAndRejections
internal/process/task_queues.js:93:5
- async Promise.all
为什么会这样?怎么了?如何使 lodash 在页眉/页脚中工作?
或者生成 html 模板的最佳方法是什么?
文件树:
dist
│ index.html
├───css
│ main.css
│ main.css.map
├───fonts
├───img
├───js
│ main.js
│ main.js.map
│ vendors~main.js
│ vendors~main.js.map
src
├───favicon
├───fonts
├───html
│ ├───includes
│ │ footer.html
│ │ header.html
│ └───views
│ index.hbs
├───img
├───js
│ index.js
├───scss
│ fonts.scss
│ icomoon.scss
│ style.scss
package.json
package-lock.json
webpack.config.js
webpack.config.js:
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const CleanWebpackPlugin = require('clean-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const TerserWebpackPlugin = require('terser-webpack-plugin');
const OptimizeCssAssetWebpackPlugin = require('optimize-css-assets-webpack-plugin');
const CopyWebpackPlugin = require('copy-webpack-plugin');
const isDev = process.env.NODE_ENV === 'development';
const isProd = !isDev;
const optimization = () =>
const config =
splitChunks:
chunks: 'all'
if (isProd)
config.minimizer = [
new OptimizeCssAssetWebpackPlugin(),
new TerserWebpackPlugin()
]
return config
const filename = ext => isDev ? `[name].$ext` : `[name].[hash].$ext`;
const cssLoaders = extra =>
const loaders = [
loader: MiniCssExtractPlugin.loader,
options:
hmr: isDev,
reloadAll: true
,
,
loader: 'css-loader',
options:
url: false
];
if (extra)
loaders.push(extra)
return loaders
const babelOptions = preset =>
const opts =
presets: [
'@babel/preset-env'
],
plugins: [
'@babel/plugin-proposal-class-properties'
]
if (preset)
opts.presets.push(preset)
return opts
module.exports =
mode: 'development',
entry: [
'@babel/polyfill',
'./src/js/index.js'
],
output:
filename: 'js/' + filename('js'),
path: path.resolve(__dirname, 'dist')
,
devServer:
port: 4250,
hot: isDev
,
devtool: isDev ? 'source-map' : '',
resolve:
//extensions: ['.js', '.json', '.png'],
alias:
'@views': path.resolve(__dirname, 'src/html/views'),
'@includes': path.resolve(__dirname, 'src/html/includes'),
'@scss': path.resolve(__dirname, 'src/scss'),
'@': path.resolve(__dirname, 'src'),
,
optimization: optimization(),
module:
rules: [
test: /\.html$/,
//include: path.resolve(__dirname, 'src/html/includes'),
use: ['html-loader']
,
test: /\.hbs$/,
loader: 'handlebars-loader'
,
test: /\.js$/,
exclude: /node_modules/,
loader:
loader: 'babel-loader',
options: babelOptions()
,
test: /\.css$/,
use: cssLoaders()
,
test: /\.s[ac]ss$/,
use: cssLoaders('sass-loader')
,
test: /\.(png|jpg|svg|gif)$/,
exclude: path.resolve(__dirname, 'src/fonts'),
use: [
loader: 'file-loader',
options:
name: '[name].[ext]',
outputPath: path.resolve(__dirname, 'dist/img')
]
,
test: /\.(ttf|otf|svg|woff|woff2|eot)$/,
exclude: path.resolve(__dirname, 'src/img'),
use: [
loader: 'file-loader',
options:
name: '[name].[ext]',
outputPath: path.resolve(__dirname, 'dist/fonts')
]
,
]
,
plugins: [
new HtmlWebpackPlugin(
template: './src/html/views/index.hbs',
minify:
collapseWhitespace: isProd
,
inject: false
),
new CleanWebpackPlugin(),
new MiniCssExtractPlugin(
filename: 'css/' + filename('css'),
),
new CopyWebpackPlugin([
from: path.resolve(__dirname, 'src/favicon'),
to: path.resolve(__dirname, 'dist')
,
from: path.resolve(__dirname, 'src/fonts'),
to: path.resolve(__dirname, 'dist/fonts')
,
from: path.resolve(__dirname, 'src/img'),
to: path.resolve(__dirname, 'dist/img')
])
]
;
package.json:
"name": "Name",
"version": "1.0.0",
"description": "",
"private": true,
"scripts":
"dev": "cross-env NODE_ENV=development webpack --mode development",
"build": "cross-env NODE_ENV=production webpack --mode production",
"watch": "cross-env NODE_ENV=development webpack --mode development --watch",
"start": "cross-env NODE_ENV=development webpack-dev-server --mode development --open"
,
"keywords": [],
"author": "",
"license": "ISC",
"devDependencies":
"@babel/core": "^7.9.0",
"@babel/plugin-proposal-class-properties": "^7.8.3",
"@babel/preset-env": "^7.9.5",
"babel-loader": "^8.1.0",
"clean-webpack-plugin": "^3.0.0",
"copy-webpack-plugin": "^5.1.1",
"cross-env": "^7.0.2",
"css-loader": "^3.5.2",
"file-loader": "^6.0.0",
"html-loader": "^1.1.0",
"html-webpack-plugin": "^4.2.0",
"mini-css-extract-plugin": "^0.9.0",
"node-sass": "^4.13.1",
"optimize-css-assets-webpack-plugin": "^5.0.3",
"raw-loader": "^4.0.1",
"resolve-url-loader": "^3.1.1",
"sass-loader": "^8.0.2",
"style-loader": "^1.1.4",
"terser-webpack-plugin": "^2.3.5",
"webpack": "^4.42.1",
"webpack-cli": "^3.3.11",
"webpack-dev-server": "^3.10.3"
,
"browserslist": "defaults",
"dependencies":
"@babel/polyfill": "^7.8.7",
"bootstrap": "^4.4.1",
"handlebars": "^4.7.6",
"handlebars-loader": "^1.7.1",
"jquery": "^3.5.0",
"popper.js": "^1.16.1"
【问题讨论】:
【参考方案1】:解决了!
我在HtmlWebpackPlugin
中将inject
更改为true
。
我还添加了函数 generateHtmlPlugins()
以自动将所有 html 文件添加到来自 ./src/html/views
的插件。
src/html/includes/header.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<title>Some Title</title>
</head>
<body>
<header>header</header>
src/html/includes/footer.html:
<footer>footer</footer>
</body>
</html>
src/html/views/index.html:
<%= _.template(require('./../includes/header-main.html'))() %>
<div class="content">
<img src="img/bg/desktop/bg-index-03.jpg" />
<div class="imgtest"></div>
</div>
<%= _.template(require('./../includes/footer.html'))() %>
dist/index.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1,shrink-to-fit=no">
<title>Some Title</title>
<link href="css/main.43f2075150009f972ed4.css" rel="stylesheet"></head>
<body>
<header>header</header>
<div class="content">
<img src="img/bg/desktop/bg-index-03.jpg" >
<div class="imgtest"></div>
</div>
<footer>footer</footer>
<script src="js/vendors~main.43f2075150009f972ed4.js"></script><script src="js/main.43f2075150009f972ed4.js"></script></body>
</html>
webpack.config.js:
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const CleanWebpackPlugin = require('clean-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const TerserWebpackPlugin = require('terser-webpack-plugin');
const OptimizeCssAssetWebpackPlugin = require('optimize-css-assets-webpack-plugin');
const CopyWebpackPlugin = require('copy-webpack-plugin');
const fs = require('fs');
const isDev = process.env.NODE_ENV === 'development';
const isProd = !isDev;
function generateHtmlPlugins(templateDir)
const templateFiles = fs.readdirSync(path.resolve(__dirname, templateDir));
return templateFiles.map(item =>
const parts = item.split('.');
const name = parts[0];
const extension = parts[1];
return new HtmlWebpackPlugin(
filename: `$name.html`,
template: path.resolve(__dirname, `$templateDir/$name.$extension`),
minify:
collapseWhitespace: isProd
,
inject: true,
)
)
const htmlPlugins = generateHtmlPlugins('./src/html/views')
const optimization = () =>
const config =
splitChunks:
chunks: 'all'
if (isProd)
config.minimizer = [
new OptimizeCssAssetWebpackPlugin(),
new TerserWebpackPlugin()
]
return config
const filename = ext => isDev ? `[name].$ext` : `[name].[hash].$ext`;
const cssLoaders = extra =>
const loaders = [
loader: MiniCssExtractPlugin.loader,
options:
hmr: isDev,
reloadAll: true
,
,
loader: 'css-loader',
options:
url: false
];
if (extra)
loaders.push(extra)
return loaders
const babelOptions = preset =>
const opts =
presets: [
'@babel/preset-env'
],
plugins: [
'@babel/plugin-proposal-class-properties'
]
if (preset)
opts.presets.push(preset)
return opts
module.exports =
mode: 'development',
entry: [
'@babel/polyfill',
'./src/js/index.js'
],
output:
filename: 'js/' + filename('js'),
path: path.resolve(__dirname, 'dist')
,
devServer:
port: 4250,
hot: isDev
,
devtool: isDev ? 'source-map' : '',
resolve:
//extensions: ['.js', '.json', '.png'],
alias:
'@views': path.resolve(__dirname, 'src/html/views'),
'@includes': path.resolve(__dirname, 'src/html/includes'),
'@scss': path.resolve(__dirname, 'src/scss'),
'@': path.resolve(__dirname, 'src'),
,
optimization: optimization(),
module:
rules: [
test: /\.html$/,
include: path.resolve(__dirname, 'src/html/includes'),
loader: 'html-loader',
options:
minimize: false,
,
test: /\.js$/,
exclude: /node_modules/,
loader:
loader: 'babel-loader',
options: babelOptions()
,
test: /\.css$/,
use: cssLoaders()
,
test: /\.s[ac]ss$/,
use: cssLoaders('sass-loader')
,
test: /\.(png|jpg|svg|gif)$/,
exclude: path.resolve(__dirname, 'src/fonts'),
use: [
loader: 'file-loader',
options:
name: '[name].[ext]',
outputPath: path.resolve(__dirname, 'dist/img')
]
,
test: /\.(ttf|otf|svg|woff|woff2|eot)$/,
exclude: path.resolve(__dirname, 'src/img'),
use: [
loader: 'file-loader',
options:
name: '[name].[ext]',
outputPath: path.resolve(__dirname, 'dist/fonts')
]
,
]
,
plugins: [
new CleanWebpackPlugin(),
new MiniCssExtractPlugin(
filename: 'css/' + filename('css'),
),
new CopyWebpackPlugin([
from: path.resolve(__dirname, 'src/favicon'),
to: path.resolve(__dirname, 'dist')
,
from: path.resolve(__dirname, 'src/fonts'),
to: path.resolve(__dirname, 'dist/fonts')
,
from: path.resolve(__dirname, 'src/img'),
to: path.resolve(__dirname, 'dist/img')
])
].concat(htmlPlugins)
;
【讨论】:
以上是关于如何从 Webpack 4 中的页眉/页脚(和 css/js 注入)生成静态页面的 html 模板(lodash 模板不起作用)?的主要内容,如果未能解决你的问题,请参考以下文章
excel表格怎么设置页眉页脚 xcel表格如何设置页眉页脚