HtmlRspackPlugin
Rspack only
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 插件。
WARNING
rspack.HtmlRspackPlugin 不支持完整的 EJS 语法, 仅支持 EJS 语法的一个子集,如果你对完整的 EJS 语法支持有需求,可以直接使用 html-webpack-plugin。为了和 html-webpack-plugin 的默认的插值语法对齐,
Rspack 修改了 EJS 的 escape 和 unescape 的默认语法,使其采用和 html-webpack-plugin 相同的语法。
 
支持的 EJS 语法
仅支持如下基本的插值表达式、循环和判断,这里的插值表达式只支持最基本的字符串类型,不支持任意的 JavaScript 表达式,其他的 ejs 语法目前均不支持。
<%-: Escaped output
对插值内容进行 escape:
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>
 
<%=: Unescaped output
不对插值内容进行 escape:
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 文件,该文件的 head 包含了所有 JS 产物对应的 <script> 标签。
只需像这样,将插件添加到你的 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 配置中有多个 entry points,它们的生成 <script> 标签都会被包含在这个 HTML 文件中。
如果你的构建产物中有一些 CSS 资源,它们将被包含在 HTML head 的 <link> 标签中。
选项
你可以向 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' | 输出的文件名,可以指定子目录,例如 pages/index.html | 
template | string|undefined | undefined | 模版文件路径,支持 ejs | 
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 | 产物注入位置,使用 false 则不注入,不指定时则会根据 scriptLoading 的配置自动判断 | 
publicPath | string | '' | script 和 link 标签的 publicPath | 
scriptLoading | 'blocking'|'defer'|'module'|'systemjs-module'|undefined | 'defer' | 现代浏览器支持使用 defer 来异步加载 js,设置为 module 则会添加 type="module" 同时使用 defer | 
chunks | string[]|undefined | undefined | 配置需要注入的 chunk,默认注入所有 chunk | 
excludeChunks | string[]|undefined | undefined | 配置需要跳过注入的 chunk | 
sri | 'sha256'|'sha384'|'sha512'|undefined | undefined | sri hash 算法,默认不开启 sri | 
minify | boolean | false | 是否启用压缩 | 
favicon | string|undefined | undefined | 配置 HTML 图标 | 
meta | Record<string, string|Record<string, string>> | {} | 配置需要注入 HTML 的 meta | 
hash | boolean | false | 是否在生成加载路径时添加 compilation 的哈希值作为后缀,以让缓存失效 | 
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({
      title: "My HTML Template"
      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",
    }),
  ],
};
 
模板渲染参数
可通过 templateParameters 扩展 HTML 模板渲染参数。以下变量在模板中默认可用:
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 存在差异:
- 不支持使用 
! 来添加 loader 处理模板文件 
rspackConfig 对象目前仅支持获取 mode、output.publicPath 和 output.crossOriginLoading 属性 
compilation 对象目前仅支持在使用模板生成函数时使用 
- 在模板中渲染标签列表(如 
htmlRspackPlugin.tags.headTags)或单个标签(如 htmlRspackPlugin.tags.headTags[0])时,需要使用 toHtml() 函数生成 HTML 代码 
 
过滤 Chunks
可以通过如下配置指定需要注入的 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 标签
如果设置了 meta,HtmlRspackPlugin 将注入 <meta> 标签。
请查看这份维护良好的几乎所有可用的 meta 标签的列表。
通过如下配置添加键值对以生成 <meta> 标签:
rspack.config.js
const rspack = require('@rspack/core');
module.exports = {
  plugins: [
    new HtmlRspackPlugin({
      meta: {
        // 将会生成: <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',
        // 将会生成: <meta name="theme-color" content="#4285f4">
        'theme-color': '#4285f4',
        // 将会生成:  <meta http-equiv="Content-Security-Policy" content="default-src https:">
        'Content-Security-Policy': {
          'http-equiv': 'Content-Security-Policy',
          content: 'default-src https:',
        },
      },
    }),
  ],
};
 
Base 标签
如果设置了 base,HtmlRspackPlugin 将注入 <base> 标签。
关于 <base> 标签的更多信息,请查看 文档
可以通过如下配置生成 <base> 标签:
rspack.config.js
new HtmlWebpackPlugin({
  // 将会生成: <base href="http://example.com/some/page.html">
  base: 'http://example.com/some/page.html',
});
new HtmlWebpackPlugin({
  // 将会生成: <base href="http://example.com/some/page.html" target="_blank">
  base: {
    href: 'http://example.com/some/page.html',
    target: '_blank',
  },
});
 
生成多个 HTML 文件
如果你有多个 entry points,并希望为每个 entry 生成一个 HTML 文件,那么你可以注册多个 rspack.HtmlRspackPlugin:
- 使用 
filename 来为每个 HTML 文件指定名称。 
- 使用 
chunks 来为每个 HTML 文件指定需要包含的 JS 产物。 
比如以下配置,会生成 foo.html 和 bar.html,其中 foo.html 仅会包含 foo.js 生成的 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'],
    }),
  ],
};
 
Hooks
HtmlRspackPlugin 提供了一些 hooks,可以让你在构建过程中修改标签或 HTML 产物代码。可通过 HtmlRspackPlugin.getCompilationHooks 来获取 hooks 对象:
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
从 compilation 中收集产物信息,生成加载路径后,生成标签标签前调用。
可在此处修改 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> 标签
rspack.config.js
const AddScriptPlugin = {
  apply: function (compiler) {
    compiler.hooks.compilation.tap('AddScriptPlugin', compilation => {
      HtmlRspackPlugin.getCompilationHooks(
        compilation,
      ).beforeAssetTagGeneration.tapPromise('AddScriptPlugin', async object => {
        object.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 时移除该属性 
如下代码将给所有 script 类型的标签添加 specialAttribute 属性:
rspack.config.js
const AddAttributePlugin = {
  apply: function (compiler) {
    compiler.hooks.compilation.tap('AddAttributePlugin', compilation => {
      HtmlRspackPlugin.getCompilationHooks(
        compilation,
      ).alterAssetTags.tapPromise('AddAttributePlugin', async pluginArgs => {
        pluginArgs.assetTags.scripts = pluginArgs.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 pluginArgs => {
        pluginArgs.headTags.push(
          pluginArgs.headTags.bodyTags.filter(i => i.async),
        );
        pluginArgs.bodyTags = pluginArgs.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,之后标签才会注入并添加到该文本之后,因此产物中为 ,产物中为 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 pluginArgs => {
          pluginArgs.html = pluginArgs.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,产物中为 <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 pluginArgs => {
          pluginArgs.html = pluginArgs.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;
  };
};