Index.html Extensions 机制

index.html 中的 <!--NOP_EXTENSIONS_INJECT--> 占位符会在服务端被替换为一段 HTML 片段,用于向前端页面注入扩展脚本或样式。

默认行为

  • 缺省不启用。配置项 nop.web.index-extensions-path 默认为空,此时不加载任何扩展片段。
  • 必须明确配置 nop.web.index-extensions-path 才启用扩展注入。
  • 启用后,Provider 会检查 index.html 是否包含 <!--NOP_EXTENSIONS_INJECT--> 占位符。不包含则原样返回。
  • 占位符出现在 nop-web-site 模块的 META-INF/resources/index.html<body> 末尾。
  • Spring (ZipContentEncodingFilter) 和 Quarkus (ZipContentEncodingFilterRegistrar) 对 //index.html 路径都会触发处理。

片段文件

路径与格式

通过配置项 nop.web.index-extensions-path 指定片段文件的 VFS 路径。支持两种后缀:

后缀 加载方式
.xpl 通过 XLang.loadTpl() 加载 XplModel,利用 ResourceComponentManager 编译缓存,执行后生成 HTML 文本
.html 直接读取文件内容作为 HTML 文本

启用示例

nop.web.index-extensions-path: /nop/web/index/extensions.xpl

或使用纯 HTML:

nop.web.index-extensions-path: /nop/web/index/extensions.html

Delta 定制

片段文件存放在 VFS 中,支持 Delta 覆盖:

  • 基础层:_vfs/nop/web/index/extensions.xpl
  • Delta 层:在 delta 目录下放同名文件即可覆盖

XPL 模板示例

<c:unit xpl:outputMode="text">
  <script src="/extensions/my-extension.js"></script>
  <link rel="stylesheet" href="/extensions/my-extension.css" />
</c:unit>

也可以包含动态逻辑:

<c:unit xpl:outputMode="text">
  <c:if test="${config.getBoolean('nop.web.enable-analytics')}">
    <script src="/analytics.js"></script>
  </c:if>
</c:unit>

架构

核心类

模块 职责
IndexHtmlProvider nop-web 加载 index.html、检查占位符、根据配置加载扩展片段(xpl/html)、替换并返回
ZipContentEncodingFilter nop-spring-web-starter //index.html 拦截,调用 IndexHtmlProvider
ZipContentEncodingFilterRegistrar nop-quarkus-web 同上,Quarkus 版本

调用流程

GET / 或 /index.html
  → Filter 拦截
    → IndexHtmlProvider.getIndexHtml()
      → 从 classpath 加载 index.html
      → 检查是否包含 <!--NOP_EXTENSIONS_INJECT--> 占位符
        → 不包含:原样返回
        → 包含且配置了 extensions-path:
          → .xpl 后缀:通过 ResourceComponentManager 加载 XplModel 并执行,生成 HTML
          → .html 后缀:直接读取文件内容
          → 替换占位符,返回最终 HTML
    → Filter 写入 response

配置项

配置键 类型 缺省值 说明
nop.web.index-extensions-path String 空(不启用) 扩展片段文件的 VFS 路径,支持 .xpl.html 后缀

配置项定义在 WebConfigs.CFG_WEB_INDEX_EXTENSIONS_PATH 中。

IoC 注册

IndexHtmlProvider 作为 Nop IoC bean 注册在 nop-web 模块的 web-defaults.beans.xml 中。

执行时序

替换后的 HTML 片段位于 <body> 末尾、</body> 之前。插入的 <script> 标签的执行时机取决于其类型:

插入的脚本类型 相对于 main.js (type="module") 的执行时机
<script> (普通) 先于 main.js 执行
<script type="module"> 后于 main.js 执行

如果需要扩展脚本在 main.js 之前执行,使用普通 <script> 标签。

使用场景

  • 注入第三方分析脚本(如 Google Analytics)
  • 注入全局 CSS 样式覆盖
  • 注入系统监控或诊断脚本
  • 通过 Delta 机制在不同部署环境注入不同脚本