For AI agents: the complete documentation index is available at /zh/llms.txt, the full documentation bundle is available at /zh/llms-full.txt, and this page is available as Markdown at /zh/plugins/html-rspack-plugin.md.
close

HtmlRspackPlugin

Rspack only

rspack.HtmlRspackPlugin 会为 Rspack 构建生成 HTML 文件,并注入各入口所需的 JavaScript 和 CSS 产物。它还可以设置文档标题、添加 favicon,以及生成 <base><meta> 标签。

  • 示例:查看常见用法。
  • 选项:查看类型、默认值和示例。
  • 模板语法:查看支持的插值和控制语句。
  • Hooks:修改产物 URL、标签和 HTML。

关于内置插件与 JavaScript 版 html-rspack-plugin 的选择,请参考 HTML 指南

示例

默认产物

默认情况下,插件会生成 index.html,并注入所有入口所需的产物。

rspack.config.mjs
import { rspack } from '@rspack/core';

export default {
  entry: './src/index.js',
  plugins: [new rspack.HtmlRspackPlugin()],
};

使用内置模板时,生成的 dist/index.html 与以下内容等价:

<!doctype html>
<html>
  <head>
    <meta charset="utf-8" />
    <title>rspack</title>
    <script defer src="main.js"></script>
  </head>
  <body></body>
</html>

默认情况下,插件会将带有 defer 属性的脚本注入 <head>。如果入口还生成 CSS,插件也会在 <head> 中插入对应的 <link> 标签。配置多个入口时,HTML 会包含所有入口的产物。

生成多个 HTML 文件

需要为每个入口分别生成 HTML 时,可以注册多个 rspack.HtmlRspackPlugin 实例:

  • 使用 filename 指定每个 HTML 文件的名称。
  • 使用 chunks 选择每个 HTML 文件要包含的入口产物。

以下配置会生成 foo.htmlbar.html。每个文件只包含对应入口所需的产物,包括运行时代码和共享产物。

rspack.config.mjs
export default {
  entry: {
    foo: './foo.js',
    bar: './bar.js',
  },
  plugins: [
    new rspack.HtmlRspackPlugin({
      filename: 'foo.html',
      chunks: ['foo'],
    }),
    new rspack.HtmlRspackPlugin({
      filename: 'bar.html',
      chunks: ['bar'],
    }),
  ],
};

模块脚本

启用 output.module 且未设置 scriptLoading 时,插件会生成 <script type="module">,而不是 <script defer>

rspack.config.mjs
export default {
  output: {
    module: true,
  },
  plugins: [new rspack.HtmlRspackPlugin()],
};
<script src="main.js" type="module"></script>

生产模式压缩

在生产模式(mode: 'production')下,未设置 minify 时,插件会自动压缩生成的 HTML。其他模式下,只有显式启用 minify 才会压缩 HTML。

rspack.config.mjs
export default {
  mode: 'production',
  plugins: [new rspack.HtmlRspackPlugin()],
};

使用模板文件

如果 Rspack context 中存在 src/index.ejs,插件会自动使用它作为模板;如果不存在,则使用内置模板。

需要自定义 HTML 结构时,也可以使用 template 指定一个 HTML 文件。插件会将所需的 JavaScript、CSS 和 favicon 标签注入其中。

index.html
<!doctype html>
<html>
  <head>
    <meta charset="utf-8" />
    <title><%= htmlRspackPlugin.options.title %></title>
  </head>
  <body></body>
</html>
rspack.config.mjs
export default {
  plugins: [
    new rspack.HtmlRspackPlugin({
      title: 'My HTML Template',
      template: 'index.html',
    }),
  ],
};

使用模板字符串

也可以通过 templateContent 直接提供 HTML 模板:

rspack.config.mjs
export default {
  plugins: [
    new rspack.HtmlRspackPlugin({
      title: 'My HTML Template',
      templateContent: `
        <!DOCTYPE html>
        <html>
          <head>
            <title><%= htmlRspackPlugin.options.title %></title>
          </head>
          <body></body>
        </html>
      `,
    }),
  ],
};

使用模板生成函数

如果模板内容需要动态生成,可以使用函数。插件支持以下两种写法:

  • 直接将函数传给 templateContent
rspack.config.mjs
export default {
  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.mjs
export default {
  plugins: [
    new rspack.HtmlRspackPlugin({
      title: 'My HTML Template',
      template: 'template.js',
    }),
  ],
};

模板渲染参数

通过 templateParameters 可以自定义渲染 HTML 模板时传入的参数。模板默认会收到以下可序列化参数:

  • htmlRspackPlugin: 插件提供的数据
    • htmlRspackPlugin.options: 规范化后的插件选项
    • htmlRspackPlugin.tags: 已生成、等待插入的标签
      • htmlRspackPlugin.tags.headTags: 用于在 <head> 中注入的 <base><meta><title><link><script> 标签列表
      • htmlRspackPlugin.tags.bodyTags: 用于在 <body> 中注入的 <script> 标签列表
    • htmlRspackPlugin.files: 当前 HTML 文件选中的产物 URL
      • htmlRspackPlugin.files.js: 选中的 JavaScript 产物 URL
      • htmlRspackPlugin.files.css: 选中的 CSS 产物 URL
      • htmlRspackPlugin.files.favicon: 配置 favicon 后生成的 favicon URL
      • htmlRspackPlugin.files.publicPath: 产物 URL 实际使用的 publicPath
  • rspackConfig: 部分 Rspack 构建设置
    • rspackConfig.mode: 当前构建模式
    • rspackConfig.output.publicPath: 当前 HTML 文件使用的有效 publicPath,包括 publicPath 覆盖值
    • rspackConfig.output.crossOriginLoading: 配置的跨域加载值

使用 JavaScript 函数渲染模板时,还可以访问 Rspack 的 compilation 对象。但如果 templateParametersfalse 或函数,则不会传入该对象。

在内置模板中,可以通过 EJS 风格的插值读取这些参数:

rspack.config.mjs
export default {
  mode: 'development',
  plugins: [
    new rspack.HtmlRspackPlugin({
      title: 'My application',
      templateContent: `
        <!doctype html>
        <html>
          <head></head>
          <body>
            <h1><%- htmlRspackPlugin.options.title %></h1>
            <p>Mode: <%- rspackConfig.mode %></p>
          </body>
        </html>
      `,
    }),
  ],
};

在 JavaScript 模板函数中,参数会作为普通 JavaScript 对象传入:

rspack.config.mjs
export default {
  plugins: [
    new rspack.HtmlRspackPlugin({
      templateContent: ({ htmlRspackPlugin }) => `
        <!doctype html>
        <html>
          <head></head>
          <body>
            <p>Scripts: ${htmlRspackPlugin.files.js.join(', ')}</p>
          </body>
        </html>
      `,
    }),
  ],
};

使用内置模板引擎时,需要调用 toHtml() 将单个标签或标签列表转换为 HTML。使用 JavaScript 模板函数时,标签和标签列表提供了 toString(),可以直接插入模板字符串。

警告

如果在模板中手动插入 htmlRspackPlugin.tags,请将 inject 设为 false,否则插件会重复插入这些标签。

差异

与 HtmlWebpackPlugin 相比:

  • 模板路径不支持 loader!./template.html 这类 loader 语法
  • compilation 对象仅在使用模板生成函数时可用,并受上述 templateParameters 条件限制

选项

以下选项均传给 new rspack.HtmlRspackPlugin()。所有示例都沿用第一个示例中的 rspack 导入。

title

  • 类型: string
  • 默认值: undefined

用于设置生成 HTML 的 <title>。启用自动注入后,插件会替换模板中已有的 <title>;如果模板中没有,则在 <head> 中添加一个。

未设置该选项时,自定义模板会保留原有标题,内置模板则使用 rspack。将 inject 设为 false 后,插件不会自动应用 title,但模板仍可通过 htmlRspackPlugin.options.title 读取该值。

new rspack.HtmlRspackPlugin({
  title: 'My application',
});

生成的 HTML 片段:

<title>My application</title>

filename

  • 类型:

    type HtmlFilenameFunction = (entry: string) => string;
    type HtmlFilename = string | HtmlFilenameFunction;
  • 默认值: 'index.html'

指定 HTML 产物相对于 output.path 的路径和文件名。未设置时,插件会在输出目录中生成 index.html

  • 字符串: 在指定路径生成 HTML 文件。字符串可以包含子目录,也可以使用 [name][contenthash] 等文件名占位符。使用 [name] 时,插件会为每个静态配置的入口生成一个 HTML 文件。

    new rspack.HtmlRspackPlugin({
      filename: 'pages/index.html',
    });
  • 函数: 插件会为每个静态配置的入口调用一次函数,并将入口名称作为参数。函数返回值会作为对应 HTML 文件的名称。

    export default {
      entry: {
        app: './src/app.js',
        admin: './src/admin.js',
      },
      plugins: [
        new rspack.HtmlRspackPlugin({
          filename: (entry) => `pages/${entry}.html`,
        }),
      ],
    };

[name] 占位符和函数写法都不支持函数形式的 Rspack entry。此外,filename 只决定 HTML 文件名,不会决定各文件注入哪些入口产物。每个生成的文件仍会注入由 chunksexcludeChunks 选出的同一组产物。如果不同页面需要不同的产物,请分别注册多个插件实例。

template

  • 类型: string
  • 默认值: undefined

指定模板文件。相对路径基于 Rspack context 解析。如果同时设置了 templateContent,插件会优先使用 templateContent

未设置该选项时,插件会先查找 context 中的 src/index.ejs;如果文件不存在,则使用内置 HTML 文档。

  • HTML 文件: 插件会读取文件内容,使用内置模板语法渲染,再注入生成的标签。

    index.html
    <!doctype html>
    <html>
      <head>
        <title><%= htmlRspackPlugin.options.title %></title>
      </head>
      <body></body>
    </html>
    new rspack.HtmlRspackPlugin({
      title: 'My application',
      template: './index.html',
    });
  • JavaScript 模块:.js.cjs 结尾的文件会作为 CommonJS 模块加载。该模块需要导出一个函数,接收模板参数,并直接返回 HTML 字符串或 Promise;Promise 的解析值也必须是 HTML 字符串。

    template.cjs
    module.exports = ({ htmlRspackPlugin }) => `
      <!doctype html>
      <html>
        <head><title>${htmlRspackPlugin.options.title}</title></head>
        <body></body>
      </html>
    `;
    new rspack.HtmlRspackPlugin({
      title: 'My application',
      template: './template.cjs',
    });

templateContent

  • 类型:

    type TemplateRenderFunction = (
      params: Record<string, any>,
    ) => string | Promise<string>;
    
    type TemplateContent = string | TemplateRenderFunction;
  • 默认值: undefined

直接提供模板内容,无需读取文件。它的优先级高于 template 和默认模板查找逻辑。

  • 字符串: 插件会使用内置模板语法渲染字符串,再注入生成的标签。

    new rspack.HtmlRspackPlugin({
      templateContent: `
        <!doctype html>
        <html>
          <head><title>My application</title></head>
          <body></body>
        </html>
      `,
    });
  • 函数: 插件会将最终的模板参数传给函数。函数返回的字符串会直接作为模板渲染结果,不再经过 EJS 处理;该函数也可以是异步函数。

    new rspack.HtmlRspackPlugin({
      title: 'My application',
      templateContent: ({ htmlRspackPlugin }) => `
        <!doctype html>
        <html>
          <head><title>${htmlRspackPlugin.options.title}</title></head>
          <body></body>
        </html>
      `,
    });

未设置时,如果配置了 template,插件会优先使用它;否则会查找 src/index.ejs,如果仍未找到,则使用内置文档。

templateParameters

  • 类型:

    type TemplateParamFunction = (
      params: Record<string, any>,
    ) => Record<string, any> | Promise<Record<string, any>>;
    
    type TemplateParameters =
      Record<string, string> | boolean | TemplateParamFunction;
  • 默认值: undefined

配置传给 HTML 模板或模板函数的参数。内置参数详见模板渲染参数

  • 对象: 将对象中的字符串属性与内置参数合并;同名属性会覆盖内置值。

    new rspack.HtmlRspackPlugin({
      templateContent: '<main><%= environment %></main>',
      templateParameters: {
        environment: 'production',
      },
    });
  • 布尔值: true 保留全部内置参数,效果与省略该选项相同;false 则向模板传入空对象。

    new rspack.HtmlRspackPlugin({
      templateContent: () => '<main>Static page</main>',
      templateParameters: false,
    });
  • 函数: 插件会将可序列化的内置参数传给函数,并把函数返回的对象作为完整的最终参数。如果模板仍需使用原有参数,请在返回值中保留它们。该函数也可以是异步函数。

    new rspack.HtmlRspackPlugin({
      templateContent: ({ buildName }) => `<main>${buildName}</main>`,
      templateParameters: (params) => ({
        ...params,
        buildName: 'documentation',
      }),
    });

只有在使用 JavaScript 模板函数,并且未设置 templateParameters、将其设为 true 或传入对象时,模板参数才会包含 compilation。字符串模板不会收到该参数;使用 templateParameters 函数或设置 templateParameters: false 时,模板函数也不会收到该参数。

inject

  • 类型: boolean | 'head' | 'body'
  • 默认值: true

控制插件是否自动将生成的标签插入 HTML。启用自动注入后,样式表、标题、<base><meta> 和 favicon 标签都会插入 <head>'head''body' 只会改变 <script> 标签的位置。

  • true scriptLoading'blocking' 时,将脚本插入 <body>;其他情况下插入 <head>。这是 inject 的默认行为。

    new rspack.HtmlRspackPlugin({
      inject: true,
    });
  • 'head''body' 无论 scriptLoading 如何,都将脚本插入指定元素。样式表和元信息标签仍位于 <head>

    new rspack.HtmlRspackPlugin({
      inject: 'body',
    });
  • false 关闭所有生成标签的自动插入,包括脚本、样式表、标题、<base><meta> 和 favicon 标签,但不会删除模板中已有的标签。

    new rspack.HtmlRspackPlugin({
      inject: false,
    });

设置为 false 后,仍可通过 htmlRspackPlugin.tags 获取生成的标签。使用内置模板引擎时,可通过 toHtml() 插入这些标签;使用 JavaScript 模板函数时,可直接将其插入模板字符串。已配置的 favicon 仍会作为产物输出。

publicPath

  • 类型: string
  • 默认值: undefined

设置生成 HTML 中 JavaScript、CSS 和 favicon URL 的前缀。必要时,插件会在末尾补充 /。该选项的优先级高于 output.publicPath

未设置时,插件会使用 output.publicPath。当 output.publicPath'auto' 时,插件会根据每个 HTML 文件的位置计算相对路径,因此子目录中的 HTML 可以通过 ../main.js 等路径引用产物。

new rspack.HtmlRspackPlugin({
  publicPath: '/assets/',
});

base

  • 类型:

    type HtmlBase =
      | string
      | {
          href?: string;
          target?: '_self' | '_blank' | '_parent' | '_top';
        };
  • 默认值: undefined

<head> 中生成一个 <base> 标签。未设置时不生成该标签;设置 inject: false 后,插件不会自动插入它。

  • 字符串: 将字符串用作 href 属性。

    new rspack.HtmlRspackPlugin({
      base: 'https://example.com/app/',
    });

    生成的 HTML 片段:

    <base href="https://example.com/app/" />
  • 对象: 设置可选的 hreftarget 属性。如果两个属性都未提供,则不会生成标签。

    new rspack.HtmlRspackPlugin({
      base: {
        href: 'https://example.com/app/',
        target: '_blank',
      },
    });

    生成的 HTML 片段:

    <base href="https://example.com/app/" target="_blank" />

scriptLoading

  • 类型: 'blocking' | 'defer' | 'module' | 'systemjs-module'
  • 默认值: 启用 output.module 时为 'module',否则为 'defer'

设置生成的 <script> 标签采用哪种加载方式,并决定使用默认 inject 时的插入位置。显式设置 inject: 'head'inject: 'body' 会覆盖该位置。此选项不会改变 JavaScript 产物的模块格式。

  • 'blocking' 不添加加载属性。使用默认 inject 时,脚本会插入 <body>

    new rspack.HtmlRspackPlugin({
      scriptLoading: 'blocking',
    });

    生成的 HTML 片段:

    <script src="main.js"></script>
  • 'defer' 添加布尔属性 defer。使用默认 inject 时,脚本会插入 <head>

    new rspack.HtmlRspackPlugin({
      scriptLoading: 'defer',
    });

    生成的 HTML 片段:

    <script defer src="main.js"></script>
  • 'module' 添加 type="module"。浏览器会延迟执行模块脚本,默认 inject 会将其插入 <head>

    new rspack.HtmlRspackPlugin({
      scriptLoading: 'module',
    });

    生成的 HTML 片段:

    <script src="main.js" type="module"></script>
  • 'systemjs-module' 添加 type="systemjs-module"。默认 inject 会将此类脚本插入 <head>

    new rspack.HtmlRspackPlugin({
      scriptLoading: 'systemjs-module',
    });

    生成的 HTML 片段:

    <script src="main.js" type="systemjs-module"></script>

chunks

  • 类型: string[]
  • 默认值: undefined

筛选要注入 HTML 的入口产物。数组中的每一项都会与入口名称进行精确匹配,不会匹配任意 chunk ID、产物文件名或模块路径。不存在的入口名称会被忽略。

未设置时,插件会先选择所有入口,再应用 excludeChunks。选中一个入口后,该入口所需的运行时代码和共享产物也会包含在 HTML 中。

使用默认的 chunksSortMode: 'auto' 时,插件会先用 chunks 筛选入口,再用 excludeChunks 移除匹配项。使用 'manual' 时,如果提供了 chunks,该数组就是最终的有序入口列表;如果未提供,excludeChunks 仍会从编译过程记录的入口顺序中排除匹配项。

export default {
  entry: {
    app: './src/app.js',
    admin: './src/admin.js',
  },
  plugins: [
    new rspack.HtmlRspackPlugin({
      chunks: ['app'],
    }),
  ],
};

excludeChunks

  • 类型: string[]
  • 默认值: undefined

从生成的 HTML 中排除指定入口的产物。数组中的每一项都会与入口名称进行精确匹配,不会匹配产物文件名、模块路径或非入口 chunk。不存在的入口名称不会产生影响。

使用默认的 chunksSortMode: 'auto' 时,插件会在 chunks 之后应用排除规则,因此同时出现在两个数组中的入口最终会被排除。使用 'manual' 时,如果提供了 chunks,该数组就是最终的有序列表,不再应用 excludeChunks;如果未提供 chunks,排除规则仍会应用于编译过程记录的入口顺序。未设置 excludeChunks 时,所有已选入口都会保留。

export default {
  entry: {
    app: './src/app.js',
    admin: './src/admin.js',
  },
  plugins: [
    new rspack.HtmlRspackPlugin({
      excludeChunks: ['admin'],
    }),
  ],
};

chunksSortMode

  • 类型: 'auto' | 'manual'
  • 默认值: 'auto'

控制所选入口的产物以何种顺序生成标签。

  • 'auto' 先应用 chunksexcludeChunks,再按编译过程记录的入口顺序处理所选入口。

    new rspack.HtmlRspackPlugin({
      chunksSortMode: 'auto',
    });
  • 'manual' 按照 chunks 中的顺序处理入口,并忽略不存在的入口名称。未提供 chunks 时,先应用 excludeChunks,再按编译过程记录的入口顺序处理;提供 chunks 时,则不再应用 excludeChunks

    new rspack.HtmlRspackPlugin({
      chunks: ['admin', 'app'],
      chunksSortMode: 'manual',
    });

minify

  • 类型: boolean
  • 默认值: 生产模式下为 true,其他模式下为 false

控制是否在模板渲染和标签注入后压缩生成的 HTML。显式配置的值优先于根据构建模式得到的默认值。

new rspack.HtmlRspackPlugin({
  minify: false,
});

favicon

  • 类型: string
  • 默认值: undefined

指定 favicon 文件。相对路径基于 Rspack context 解析。插件会按原文件名将它输出到产物根目录,并生成一个 <link rel="icon"> 标签;标签 URL 由 publicPath 决定。

未设置时,插件不会生成 favicon 产物和标签。设置 inject: false 后,favicon 仍会作为产物输出,并可通过 htmlRspackPlugin.files.favicon 获取,但对应的 <link> 标签不会自动插入 HTML。

new rspack.HtmlRspackPlugin({
  favicon: './src/favicon.ico',
});

生成的 HTML 片段:

<link href="favicon.ico" rel="icon" />

meta

  • 类型:

    type HtmlMeta = Record<string, string | Record<string, string>>;
  • 默认值: {}

<head> 中生成额外的 <meta> 标签。每个顶层键默认会作为 name 属性。内置模板中的 <meta charset="utf-8"> 不受该选项影响。传入空对象或未设置该选项时,不会生成额外的 <meta> 标签;设置 inject: false 后,插件不会自动插入它们。

  • 字符串值: 使用顶层键作为 name,字符串作为 content

    new rspack.HtmlRspackPlugin({
      meta: {
        viewport: 'width=device-width,initial-scale=1',
      },
    });

    生成的 HTML 片段:

    <meta content="width=device-width,initial-scale=1" name="viewport" />
  • 对象值: 将对象中的每个属性添加为标签属性。对象中的 name 会覆盖由顶层键得到的名称。

    new rspack.HtmlRspackPlugin({
      meta: {
        viewport: {
          name: 'viewport',
          content: 'width=device-width,initial-scale=1',
          'data-origin': 'rspack',
        },
      },
    });

    生成的 HTML 片段:

    <meta
      content="width=device-width,initial-scale=1"
      data-origin="rspack"
      name="viewport"
    />

hash

  • 类型: boolean
  • 默认值: undefined

设置为 true 时,插件会将 Rspack 编译哈希作为查询字符串追加到 JavaScript、CSS 和 favicon URL。该选项只会改变 HTML 中的引用,不会改变产物文件名。未设置或设为 false 时,插件不会修改这些 URL。

new rspack.HtmlRspackPlugin({
  hash: true,
});

模板语法

内置模板引擎支持 EJS 风格的插值和基本控制流,但不会执行任意 JavaScript。以下示例展示了常用写法。

转义输出 <%-

转义插值内容:

ejs
<p>Hello, <%- name %>.</p>
<p>Hello, <%- 'the Most Honorable ' + name %>.</p>
locals
{
  "name": "Rspack<y>"
}
html
<p>Hello, Rspack&lt;y&gt;.</p>
<p>Hello, the Most Honorable Rspack&lt;y&gt;.</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, &lt;strong&gt;Rspack&lt;/strong&gt;.</p>
<p>Hello, <strong>Rspack</strong>.</p>

<p>Hello, &lt;/p&gt;&lt;script&gt;document.write()&lt;/script&gt;&lt;p&gt;.</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) %>
  <% } %>
<% } %>

Hooks

HtmlRspackPlugin 提供了多个 hooks,可用于修改生成的标签和 HTML。通过 rspack.HtmlRspackPlugin.getCompilationHooks 可以获取这些 hooks:

传给插件构造函数的原始选项可通过 data.plugin.options 访问。额外的自定义字段也会保留在这里,供 hook 使用,但这些字段本身不会影响 HTML 的生成。

rspack.config.mjs
const HtmlModifyPlugin = {
  apply(compiler) {
    compiler.hooks.compilation.tap('HtmlModifyPlugin', (compilation) => {
      const hooks = rspack.HtmlRspackPlugin.getCompilationHooks(compilation);
      // hooks.beforeAssetTagGeneration.tapPromise()
      // hooks.alterAssetTags.tapPromise()
      // hooks.alterAssetTagGroups.tapPromise()
      // hooks.afterTemplateExecution.tapPromise()
      // hooks.beforeEmit.tapPromise()
      // hooks.afterEmit.tapPromise()
    });
  },
};

export default {
  plugins: [new rspack.HtmlRspackPlugin(), HtmlModifyPlugin],
};

beforeAssetTagGeneration

该 hook 会在插件从 compilation 中收集完产物 URL 后、生成标签前调用。

通过修改 assets.jsassets.cssassets.favicon,可以添加或替换用于生成标签的 URL。新增值会被直接使用:插件不会为其添加 publicPath,也不会输出对应文件。

  • 类型: AsyncSeriesWaterfallHook<[BeforeAssetTagGenerationData]>
  • 参数:
    type BeforeAssetTagGenerationData = {
      assets: {
        publicPath: string;
        js: Array<string>;
        css: Array<string>;
        favicon?: string;
        jsIntegrity?: Array<string | undefined | null>;
        cssIntegrity?: Array<string | undefined | null>;
      };
      outputName: string;
      plugin: {
        options: HtmlRspackPluginOptions;
      };
    };
警告

只有对 assets.jsassets.cssassets.favicon 的修改会影响插件自动生成的标签。其他字段不会影响自动生成标签,但模板仍可通过 htmlRspackPlugin.files 读取这些字段。

以下示例添加了 URL extra-script.js,最终 HTML 中会生成对应的 <script defer src="extra-script.js"></script> 标签。

rspack.config.mjs
const AddScriptPlugin = {
  apply(compiler) {
    compiler.hooks.compilation.tap('AddScriptPlugin', (compilation) => {
      const hooks = rspack.HtmlRspackPlugin.getCompilationHooks(compilation);
      hooks.beforeAssetTagGeneration.tapPromise(
        'AddScriptPlugin',
        async (data) => {
          data.assets.js.push('extra-script.js');
        },
      );
    });
  },
};

export default {
  plugins: [new rspack.HtmlRspackPlugin(), AddScriptPlugin],
};

alterAssetTags

该 hook 会在根据产物 URL 生成标签后、将标签分配到 <head><body> 前调用。通过修改 assetTags,可以添加、删除或更新标签。

  • 类型: AsyncSeriesWaterfallHook<[AlterAssetTagsData]>

  • 参数:

    type HtmlTag = {
      tagName: string;
      attributes: Record<string, string | boolean | undefined | null>;
      voidTag: boolean;
      innerHTML?: string;
      asset?: string;
    };
    
    type AlterAssetTagsData = {
      assetTags: {
        scripts: Array<HtmlTag>;
        styles: Array<HtmlTag>;
        meta: Array<HtmlTag>;
      };
      publicPath: string;
      outputName: string;
      plugin: {
        options: HtmlRspackPluginOptions;
      };
    };
警告

只有对 assetTags 的修改会影响生成的 HTML;其他字段的变化会被忽略。

属性名会统一转换为小写,属性值则按以下规则处理:

  • true 添加无值属性,例如 <script defer specialattribute src="main.js"></script>
  • 字符串: 添加带有该值的属性,例如 <script defer specialattribute="some value" src="main.js"></script>
  • falseundefinednull 移除该属性。

以下示例会为所有 <script> 标签添加 specialAttribute 属性:

rspack.config.mjs
const AddAttributePlugin = {
  apply(compiler) {
    compiler.hooks.compilation.tap('AddAttributePlugin', (compilation) => {
      const hooks = rspack.HtmlRspackPlugin.getCompilationHooks(compilation);
      hooks.alterAssetTags.tapPromise('AddAttributePlugin', async (data) => {
        data.assetTags.scripts = data.assetTags.scripts.map((tag) => {
          if (tag.tagName === 'script') {
            tag.attributes.specialAttribute = true;
          }
          return tag;
        });
      });
    });
  },
};

export default {
  plugins: [new rspack.HtmlRspackPlugin(), AddAttributePlugin],
};

alterAssetTagGroups

该 hook 会在标签分配到 <head><body> 后、模板渲染前调用。通过修改 headTagsbodyTags,可以移动或更新分组后的标签。

  • 类型: AsyncSeriesWaterfallHook<[AlterAssetTagGroupsData]>
  • 参数:
    type AlterAssetTagGroupsData = {
      headTags: Array<HtmlTag>;
      bodyTags: Array<HtmlTag>;
      publicPath: string;
      outputName: string;
      plugin: {
        options: HtmlRspackPluginOptions;
      };
    };
警告

只有对 headTagsbodyTags 的修改会影响生成的 HTML;其他字段的变化会被忽略。

以下示例会将所有 <script> 标签从 <body> 移到 <head>

rspack.config.mjs
const MoveTagsPlugin = {
  apply(compiler) {
    compiler.hooks.compilation.tap('MoveTagsPlugin', (compilation) => {
      const hooks = rspack.HtmlRspackPlugin.getCompilationHooks(compilation);
      hooks.alterAssetTagGroups.tapPromise('MoveTagsPlugin', async (data) => {
        const scripts = data.bodyTags.filter((tag) => tag.tagName === 'script');
        data.headTags.push(...scripts);
        data.bodyTags = data.bodyTags.filter((tag) => tag.tagName !== 'script');
      });
    });
  },
};

export default {
  plugins: [
    new rspack.HtmlRspackPlugin({
      inject: 'body',
    }),
    MoveTagsPlugin,
  ],
};

afterTemplateExecution

该 hook 会在模板渲染完成后、自动注入标签前调用。通过修改 htmlheadTagsbodyTags,可以调整渲染结果或待注入的标签。

如果 templateContent 是函数,或者 template 指向 .js/.cjs 文件,html 就是模板函数返回的字符串。如果使用字符串模板或标记文件,html 则是内置模板引擎的渲染结果。

  • 类型: AsyncSeriesWaterfallHook<[AfterTemplateExecutionData]>
  • 参数:
    type AfterTemplateExecutionData = {
      html: string;
      headTags: Array<HtmlTag>;
      bodyTags: Array<HtmlTag>;
      outputName: string;
      plugin: {
        options: HtmlRspackPluginOptions;
      };
    };
警告

只有对 htmlheadTagsbodyTags 的修改会影响生成的 HTML;其他字段的变化会被忽略。

以下示例会在 <body> 结尾添加 Injected by plugin。随后注入的标签会出现在这段文本之后,最终得到 Injected by plugin<script defer src="main.js"></script></body>

rspack.config.mjs
const InjectContentPlugin = {
  apply(compiler) {
    compiler.hooks.compilation.tap('InjectContentPlugin', (compilation) => {
      const hooks = rspack.HtmlRspackPlugin.getCompilationHooks(compilation);
      hooks.afterTemplateExecution.tapPromise(
        'InjectContentPlugin',
        async (data) => {
          data.html = data.html.replace('</body>', 'Injected by plugin</body>');
        },
      );
    });
  },
};

export default {
  plugins: [
    new rspack.HtmlRspackPlugin({
      inject: 'body',
    }),
    InjectContentPlugin,
  ],
};

beforeEmit

该 hook 会在 HTML 产物输出前调用,也是修改产物内容的最后机会。

  • 类型: AsyncSeriesWaterfallHook<[BeforeEmitData]>
  • 参数:
    type BeforeEmitData = {
      html: string;
      outputName: string;
      plugin: {
        options: HtmlRspackPluginOptions;
      };
    };
警告

只有对 html 的修改会影响最终产物;其他字段的变化会被忽略。

以下示例会在 <body> 结尾添加 Injected by plugin,最终顺序为 <script defer src="main.js"></script>Injected by plugin</body>

rspack.config.mjs
const InjectContentPlugin = {
  apply(compiler) {
    compiler.hooks.compilation.tap('InjectContentPlugin', (compilation) => {
      const hooks = rspack.HtmlRspackPlugin.getCompilationHooks(compilation);
      hooks.beforeEmit.tapPromise('InjectContentPlugin', async (data) => {
        data.html = data.html.replace('</body>', 'Injected by plugin</body>');
      });
    });
  },
};

export default {
  plugins: [
    new rspack.HtmlRspackPlugin({
      inject: 'body',
    }),
    InjectContentPlugin,
  ],
};

afterEmit

该 hook 会在 HTML 产物输出后调用,仅用于通知产物已生成。

  • 类型: AsyncSeriesWaterfallHook<[AfterEmitData]>
  • 参数:
    type AfterEmitData = {
      outputName: string;
      plugin: {
        options: HtmlRspackPluginOptions;
      };
    };