HtmlRspackPlugin
Rspack 唯一
rspack.HtmlRspackPlugin
是用 Rust 实现的高性能 HTML 插件。您可以使用它为 Rspack 项目生成 HTML 文件。
new rspack.HtmlRspackPlugin(options);
比较
在使用 rspack.HtmlRspackPlugin
之前,请注意 rspack.HtmlRspackPlugin
与社区 html-webpack-plugin 之间存在一些差异。
性能
由于 rspack.HtmlRspackPlugin
是用 Rust 实现的,因此它的构建性能明显优于 html-webpack-plugin,尤其是在构建大量 HTML 文件的场景下。
功能
rspack.HtmlRspackPlugin
的功能是 html-webpack-plugin
的一个子集。为了保证插件的性能,我们没有实现 html-webpack-plugin 提供的所有功能。
如果它的选项不能满足您的需求,您也可以直接使用社区 html-webpack-plugin。
警告
rspack.HtmlRspackPlugin
不支持完整的 ejs
语法;它只支持 ejs
语法的子集。如果您需要完整的 ejs
语法支持,您可以直接使用 html-webpack-plugin
。为了使 html-webpack-plugin
的默认模板语法保持一致,Rspack 将默认的 EJS 转义和反转义更改为与 html-webpack-plugin
的默认语法相同。
支持的 EJS 语法
只支持以下基本插值表达式和一些控制语句。这里,插值表达式只支持最基本字符串类型,不支持任意 JavaScript 表达式。其他 EJS 语法目前不支持。
<%-: 转义输出
转义插值内的内容
ejs
<p>Hello, <%- name %>.</p>
<p>Hello, <%- 'the Most Honorable ' + name %>.</p>
html
<p>Hello, Rspack<y>.</p>
<p>Hello, the Most Honorable Rspack<y>.</p>
<%=: 非转义输出
不转义插值内的内容
ejs
<p>Hello, <%- myHtml %>.</p>
<p>Hello, <%= myHtml %>.</p>
<p>Hello, <%- myMaliciousHtml %>.</p>
<p>Hello, <%= myMaliciousHtml %>.</p>
locals
{
"myHtml": "<strong>Rspack</strong>",
"myMaliciousHtml": "</p><script>document.write()</script><p>"
}
html
<p>Hello, <strong>Rspack</strong>.</p>
<p>Hello, <strong>Rspack</strong>.</p>
<p>Hello, </p><script>document.write()</script><p>.</p>
<p>Hello,</p>
<script>
document.write();
</script>
<p>.</p>
控制语句
使用 for in
语句实现列表遍历,使用 if
语句实现条件判断
ejs
<% for tag in htmlRspackPlugin.tags.headTags { %>
<% if tag.tagName=="script" { %>
<%= toHtml(tag) %>
<% } %>
<% } %>
用法
该插件将为您生成一个 HTML 文件,该文件使用 <script>
标签在头部包含所有 JS 输出。
只需像这样将插件添加到您的 Rspack 配置中
rspack.config.js
const rspack = require('@rspack/core');
module.exports = {
plugins: [new rspack.HtmlRspackPlugin()],
};
这将生成一个包含以下内容的文件 dist/index.html
<!doctype html>
<html>
<head>
<meta charset="utf-8" />
<title>rspack</title>
<script src="main.js" defer></script>
</head>
<body></body>
</html>
如果您的 Rspack 配置中有多个入口点,它们都将使用 <script>
标签包含在生成的 HTML 中。
如果您在构建输出中有一些 CSS 资产,它们将使用 <link>
标签包含在 HTML 头部。
选项
您可以将一些配置选项传递给 rspack.HtmlRspackPlugin
。允许的选项如下
type HtmlRspackPluginOptions = {
title?: string;
filename?: string | ((entry: string) => string);
template?: string;
templateContent?: string | ((params: Record<string, any>) => string | Promise<string>);
templateParameters?: Record<string, string> | (oldParams: params: Record<string, any>) => Record<string, any> | Promise<Record<string, any>>;
inject?: 'head' | 'body' | boolean;
publicPath?: string;
base?: string | {
href?: string;
target?: '_self' | '_blank' | '_parent' | '_top'
};
scriptLoading?: 'blocking' | 'defer' | 'module' | 'systemjs-module';
chunks?: string[];
excludeChunks?: string[];
sri?: 'sha256' | 'sha384' | 'sha512';
minify?: boolean;
favicon?: string;
meta?: Record<string, string | Record<string, string>>;
hash?: boolean;
};
名称 | 类型 | 默认值 | 描述 |
---|
title | string|undefined | undefined | 用于生成的 HTML 文档的标题。 |
filename | string|undefined|((entry: string) => string) | 'index.html' | 要写入 HTML 的文件。默认为 index.html 。您也可以在这里指定一个子目录(例如:pages/index.html)。 |
template | string|undefined | undefined | 模板文件路径 |
templateContent | string|undefined|((params: Record<string, any>) => string | Promise<string>) | undefined | 模板文件内容,优先级高于 template。使用函数时,传入模板参数,使用返回的字符串作为模板内容。 |
templateParameters | Record<string, string>|(oldParams: params: Record<string, any>) => Record<string, any> | Promise<Record<string, any>> | {} | 允许覆盖模板中使用的参数。使用函数时,传入原始模板参数,使用返回的对象作为最终的模板参数。 |
inject | 'head' | 'body' | boolean | undefined | undefined | 在 template 中的脚本和链接标签注入位置。使用 false 不注入。如果没有指定,它将根据 scriptLoading 自动确定。 |
publicPath | string | '' | 用于脚本和链接标签的 publicPath。 |
scriptLoading | 'blocking'|'defer'|'module'|'systemjs-module'|undefined | 'defer' | 现代浏览器支持非阻塞 JavaScript 加载 ('defer') 以提高页面启动性能。设置为 'module' 将添加属性 type='module'。这也意味着 'defer',因为模块会自动延迟。 |
chunks | string[]|undefined | undefined | 允许您仅添加一些块。 |
excludeChunks | string[]|undefined | undefined | 允许您跳过一些块。 |
sri | 'sha256'|'sha384'|'sha512'|undefined | undefined | sri 哈希算法,默认情况下禁用。 |
minify | boolean | false | 控制是否缩小输出。 |
favicon | string|undefined | undefined | 将给定的 favicon 路径添加到输出 HTML。 |
meta | Record<string, string|Record<string, string>> | {} | 允许注入元标签。 |
hash | boolean | false | 如果为 true,则将唯一的 rspack 编译哈希追加到所有包含的脚本和 CSS 文件中。这对于清除缓存很有用 |
base | string|object|undefined | undefined | 注入一个 base 标签 |
示例
自定义 HTML 模板
如果默认生成的 HTML 无法满足您的需求,您可以使用自己的模板。
使用模板文件
最简单的方法是使用 template 选项并传递一个自定义 HTML 文件。rspack.HtmlRspackPlugin
将自动将所有必要的 JS、CSS 和 favicon 文件注入到 HTML 中。
通过 template
指定 HTML 模板文件
index.html
<!doctype html>
<html>
<head>
<meta charset="utf-8" />
<title><%= htmlRspackPlugin.options.title %></title>
</head>
<body></body>
</html>
rspack.config.js
const rspack = require('@rspack/core');
module.exports = {
plugins: [
new rspack.HtmlRspackPlugin({
template: 'index.html',
}),
],
};
使用模板字符串
通过 templateContent
指定 HTML 模板内容
rspack.config.js
const rspack = require('@rspack/core');
module.exports = {
plugins: [
new rspack.HtmlRspackPlugin({
title: "My HTML Template"
templateContent: `
<!DOCTYPE html>
<html>
<head>
<title><%= htmlRspackPlugin.options.title %></title>
</head>
<body></body>
</html>
`,
}),
],
};
使用模板函数
使用函数来生成 HTML 模板内容
rspack.config.js
const rspack = require('@rspack/core');
module.exports = {
plugins: [
new rspack.HtmlRspackPlugin({
title: "My HTML Template"
templateContent: ({ htmlRspackPlugin }) => `
<!DOCTYPE html>
<html>
<head>
<title>${htmlRspackPlugin.options.title}</title>
</head>
<body></body>
</html>
`,
}),
],
};
- 或者在
template
中传递以 .js
或 .cjs
结尾的文件路径
template.js
module.exports = ({ htmlRspackPlugin }) => `
<!DOCTYPE html>
<html>
<head>
<title>${htmlRspackPlugin.options.title}</title>
</head>
<body></body>
</html>
`;
rspack.config.js
const rspack = require('@rspack/core');
module.exports = {
plugins: [
new rspack.HtmlRspackPlugin({
title: "My HTML Template"
template: "template.js",
}),
],
};
模板参数
HTML 模板渲染参数可以通过 templateParameters
扩展。以下变量默认情况下可用
htmlRspackPlugin
: 插件的数据
htmlRspackPlugin.options
: 插件的配置对象
htmlRspackPlugin.tags
: 为在模板中注入准备好的标签信息
htmlRspackPlugin.tags.headTags
: 在 <head>
中注入的 <base>
、<meta>
、<link>
、<script>
标签列表
htmlRspackPlugin.tags.bodyTags
: 在 <body>
中注入的 <script>
标签列表
htmlRspackPlugin.files
: 此编译中生成的资产文件
htmlRspackPlugin.files.js
: 此编译中生成的 JS 资产路径列表
htmlRspackPlugin.files.css
: 本次编译生成的 CSS 资产路径列表
htmlRspackPlugin.files.favicon
: 如果配置了 favicon
,则此处为计算出的最终 favicon 资产路径
htmlRspackPlugin.files.publicPath
: 资产文件的 publicPath
rspackConfig
: 本次编译中使用的 Rspack 配置对象
compilation
: 本次编译的 Compilation 对象
警告
如果使用 htmlRspackPlugin.tags
在模板渲染期间插入标签,请将 inject
配置为 false
,否则标签将被注入两次。
区别
与 HtmlWebpackPlugin 有些区别
- 不支持使用
!
添加加载器来处理模板文件
rspackConfig
对象目前仅支持 mode
、output.publicPath
和 output.crossOriginLoading
compilation
对象目前仅在 使用模板函数 时支持
- 在模板中渲染标签列表(例如
htmlRspackPlugin.tags.headTags
)或单个标签(例如 htmlRspackPlugin.tags.headTags[0]
)时,需要使用 toHtml()
函数生成 HTML 代码
过滤 Chunk
可以通过以下配置指定需要注入的 Chunk
rspack.config.js
const rspack = require('@rspack/core');
module.exports = {
plugins: [
new HtmlRspackPlugin({
chunks: ['app'],
}),
],
};
也可以通过以下配置排除特定 Chunk
rspack.config.js
const rspack = require('@rspack/core');
module.exports = {
plugins: [
new HtmlRspackPlugin({
excludeChunks: ['app'],
}),
],
};
元标签
如果设置了 meta
,HtmlRspackPlugin 将注入 <meta>
标签。
请查看这个维护良好的几乎所有可用 元标签 列表。
通过以下配置添加键值对以生成 <meta>
标签
rspack.config.js
const rspack = require('@rspack/core');
module.exports = {
plugins: [
new HtmlRspackPlugin({
meta: {
// Will generate: <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
viewport: 'width=device-width, initial-scale=1, shrink-to-fit=no',
// Will generate: <meta name="theme-color" content="#4285f4">
'theme-color': '#4285f4',
// Will generate: <meta http-equiv="Content-Security-Policy" content="default-src https:">
'Content-Security-Policy': {
'http-equiv': 'Content-Security-Policy',
content: 'default-src https:',
},
},
}),
],
};
基本标签
如果设置了 base
,HtmlRspackPlugin 将注入 <base>
标签。
有关 <base>
标签的更多信息,请查看 文档
可以通过以下配置生成 <base>
标签
rspack.config.js
new HtmlWebpackPlugin({
// Will generate: <base href="http://example.com/some/page.html">
base: 'http://example.com/some/page.html',
});
new HtmlWebpackPlugin({
// Will generate: <base href="http://example.com/some/page.html" target="_blank">
base: {
href: 'http://example.com/some/page.html',
target: '_blank',
},
});
生成多个 HTML 文件
如果有多个入口点并且想要为每个入口点生成一个 HTML 文件,可以注册多个 rspack.HtmlRspackPlugin
- 使用
filename
指定每个 HTML 文件的名称。
- 使用
chunks
指定每个 HTML 文件中要包含的 JS 包。
例如,以下配置将生成 foo.html 和 bar.html,其中 foo.html 只包含 foo.js 生成的 JS 包。
rspack.config.js
const rspack = require('@rspack/core');
module.exports = {
entry: {
foo: './foo.js',
bar: './bar.js',
},
plugins: [
new rspack.HtmlRspackPlugin({
filename: 'foo.html',
chunks: ['foo'],
}),
new rspack.HtmlRspackPlugin({
filename: 'bar.html',
chunks: ['bar'],
}),
],
};
钩子
HtmlRspackPlugin 提供了一些钩子,允许您修改标签或生成的 HTML 代码。可以通过 HtmlRspackPlugin.getCompilationHooks
获取钩子对象
rspack.config.js
const HtmlModifyPlugin = {
apply: function (compiler) {
compiler.hooks.compilation.tap('HtmlModifyPlugin', compilation => {
const hooks = HtmlRspackPlugin.getCompilationHooks(compilation);
// hooks.beforeAssetTagGeneration.tapPromise()
// hooks.alterAssetTags.tapPromise()
// hooks.alterAssetTagGroups.tapPromise()
// hooks.afterTemplateExecution.tapPromise()
// hooks.beforeEmit.tapPromise()
// hooks.afterEmit.tapPromise()
});
},
};
module.exports = {
//...
plugins: [new HtmlRspackPlugin(), HtmlModifyPlugin],
};
beforeAssetTagGeneration
此钩子将在从编译中收集资产并生成加载路径后调用,但在生成标签之前调用。
可以在这里修改 assets
以添加自定义的 JS 和 CSS 资产文件。
- 类型:
AsyncSeriesWaterfallHook<[BeforeAssetTagGenerationData]>
- 参数:
type BeforeAssetTagGenerationData = {
assets: {
publicPath: string;
js: Array<string>;
css: Array<string>;
favicon?: string;
};
outputName: string;
plugin: {
options: HtmlRspackPluginOptions;
};
};
警告
只能修改 assets.js
、assets.css
和 assets.favicon
。对其他项目的修改将不会生效。
以下代码添加了一个额外的 extra-script.js
并生成一个 <script defer src="extra-script.js"></script>
标签,在最终的 html 内容中。
rspack.config.js
const AddScriptPlugin = {
apply: function (compiler) {
compiler.hooks.compilation.tap('AddScriptPlugin', compilation => {
HtmlRspackPlugin.getCompilationHooks(
compilation,
).beforeAssetTagGeneration.tapPromise('AddScriptPlugin', async data => {
data.assets.js.push('extra-script.js');
});
});
},
};
module.exports = {
//...
plugins: [new HtmlRspackPlugin(), AddScriptPlugin],
};
alterAssetTags
此钩子将在根据资产文件生成资产标签后调用,但在确定标签的插入位置之前调用。
可以在这里调整标签。
警告
只能修改 assetTags
。对其他项目的修改将不会生效。
- 当将属性值设置为
true
时,将添加一个无值属性,并将生成 <script defer specialattribute src="main.js"></script>
。
- 当将属性值设置为
string
时,将添加一个有值属性,并将生成 <script defer specialattribute="some value" src="main.js"></script>
。
- 当将属性值设置为
false
时,将删除该属性。
以下代码将 specialAttribute
属性添加到所有 script
类型标签
rspack.config.js
const AddAttributePlugin = {
apply: function (compiler) {
compiler.hooks.compilation.tap('AddAttributePlugin', compilation => {
HtmlRspackPlugin.getCompilationHooks(
compilation,
).alterAssetTags.tapPromise('AddAttributePlugin', async data => {
data.assetTags.scripts = data.assetTags.scripts.map(tag => {
if (tag.tagName === 'script') {
tag.attributes.specialAttribute = true;
}
return tag;
});
});
});
},
};
module.exports = {
//...
plugins: [new HtmlRspackPlugin(), AddAttributePlugin],
};
alterAssetTagGroups
此钩子将在生成 head
和 body
的标签组后调用,但在模板被函数或模板引擎渲染之前调用。
可以在这里调整标签的插入位置。
- 类型:
AsyncSeriesWaterfallHook<[AlterAssetTagGroupsData]>
- 参数:
type AlterAssetTagGroupsData = {
headTags: Array<HtmlTag>;
bodyTags: Array<HtmlTag>;
outputName: string;
plugin: {
options: HtmlRspackPluginOptions;
};
};
警告
只能修改 headTags
和 bodyTags
。对其他项目的修改将不会生效。
以下代码将 async
script
标签从 body
移动到 head
rspack.config.js
const MoveTagsPlugin = {
apply: function (compiler) {
compiler.hooks.compilation.tap('MoveTagsPlugin', compilation => {
HtmlWebpackPlugin.getCompilationHooks(
compilation,
).alterAssetTagGroups.tapPromise('MoveTagsPlugin', async data => {
data.headTags.push(data.headTags.bodyTags.filter(i => i.async));
data.bodyTags = data.bodyTags.filter(i => !i.async);
});
});
},
};
module.exports = {
//...
plugins: [
new HtmlRspackPlugin({
inject: 'body',
}),
AllHeadTagsPlugin,
],
};
afterTemplateExecution
此钩子将在模板渲染完成后调用,但在标签被注入之前调用。
可以在这里修改 HTML 内容和要注入的标签。
-
当使用函数 templateContent
或以 .js/.cjs
结尾的 template
,并使用此函数渲染模板时,这里的 html
是函数返回的结果。
-
在其他情况下,HTML 模板将在内部通过模板引擎编译,这里的 html
是编译后的结果。
-
类型: AsyncSeriesWaterfallHook<[AfterTemplateExecutionData]>
-
参数:
type AfterTemplateExecutionData = {
html: string;
headTags: Array<HtmlTag>;
bodyTags: Array<HtmlTag>;
outputName: string;
plugin: {
options: HtmlRspackPluginOptions;
};
};
:::warning 警告 只能修改 html
、headTags
和 bodyTags
。对其他项目的修改将不会生效。 ::
以下代码在 body 的末尾添加 Injected by plugin
。然后标签将在此文本之后注入。因此,在最终的 HTML 内容中将是 <Injected by plugin<script defer src="main.js"></script></body>
rspack.config.js
const InjectContentPlugin = {
apply: function (compiler) {
compiler.hooks.compilation.tap('InjectContentPlugin', compilation => {
HtmlWebpackPlugin.getCompilationHooks(
compilation,
).afterTemplateExecution.tapPromise('InjectContentPlugin', async data => {
data.html = data.html.replace('</body>', 'Injected by plugin</body>');
});
});
},
};
module.exports = {
//...
plugins: [
new HtmlRspackPlugin({
inject: 'body',
}),
InjectContentPlugin,
],
};
beforeEmit
此钩子将在生成 HTML 资产文件之前调用,是修改 HTML 内容的最后机会。
- 类型:
SyncHook<[BeforeEmitData]>
- 参数:
type BeforeEmitData = {
html: string;
outputName: string;
plugin: {
options: HtmlRspackPluginOptions;
};
};
以下代码在 body 的末尾添加 Injected by plugin
。它将在最终的 HTML 内容中是 <script defer src="main.js"></script>Injected by plugin</body>
rspack.config.js
const InjectContentPlugin = {
apply: function (compiler) {
compiler.hooks.compilation.tap('InjectContentPlugin', compilation => {
HtmlWebpackPlugin.getCompilationHooks(compilation).beforeEmit.tapPromise(
'InjectContentPlugin',
async data => {
data.html = data.html.replace('</body>', 'Injected by plugin</body>');
},
);
});
},
};
module.exports = {
//...
plugins: [
new HtmlRspackPlugin({
inject: 'body',
}),
InjectContentPlugin,
],
};
afterEmit
此钩子将在生成 HTML 资产文件后调用,仅用于通知。
- 类型:
SyncHook<[AfterEmitData]>
- 参数:
type AfterEmitData = {
outputName: string;
plugin: {
options: HtmlRspackPluginOptions;
};
};