.html文件中防止script脚本缓存的三种方案

作者:窗边听雨时间:2026-10-31 10:56:19浏览:203

本文针对HTML文件中全局工具库脚本被浏览器缓存导致版本更新失效的问题,详细解析了三种解决方案。重点介绍了通过JS动态创建script节点并配合轮询机制实现同步加载的方法,分析了在URL后添加随机参数的局限性,并指出Chrome 55起document.write注入script已被限制。旨在帮助开发者在无需频繁发版审核的情况下,有效管理前端脚本缓存。

场景

假设我们有一个工具库,通过 script 标签引入并挂载到全局环境中,该库对外暴露一个包含多个方法的全局对象 gTool。其典型引入方式如下所示:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8" />
    <script type="text/javascript" src="https://xxxx/common/tool.min.js"></script> // [!code hl]
  </head>
  <body>
    <div id="app"><!--app-html--></div>
    <script type="module" src="/src/main.ts"></script>
  </body>
</html>

随后,在 main.ts 主逻辑文件中,即可直接调用 gTool.jumpLink 等方法。

gTool.jumpLink({})

问题

然而,在实际开发中常遇到如下困境:由于业务迭代频繁,我们需要不断修改 gTool 的代码并重新上传至 CDN。但用户通过 HTML 文件访问时,浏览器往往直接读取本地缓存的 gTool 链接,而不会向服务器发起新请求。这导致用户手中的 gTool 版本可能滞后,包含的 bug 无法得到及时修复。

因此,核心诉求变为:如何有效禁止浏览器对脚本进行缓存。

方法

本文尝试并对比了三种解决思路。

1. 动态创建 script 标签并插入 body

最终推荐采用此方案,既满足需求,线上运行也稳定无误。

可以通过 JavaScript 动态创建 script 节点,并将其插入 DOM 中。但需注意,动态创建的 script 脚本默认是异步加载的,不会阻塞浏览器后续 JS 代码的执行。

如下代码示例中,控制台永远会先打印 'body 执行',后打印 'tool 脚本执行'。这种异步特性导致在脚本加载完成前获取全局 gTool 对象时会报错。

<!DOCTYPE html>
<html>
  <head>
    <script>
      var htmlScriptScript = document.createElement('script')
      htmlScriptScript.type = 'text/javascript'
      htmlScriptScript.src = 'https://xxxx/common/tool.min.js?a=' + Math.random()
      htmlScriptScript.onload = function () {
        console.error('tool 脚本执行')
      }
      document.head.appendChild(htmlScriptScript)
    </script>
  </head>
  <body>
    <div id="app"><!--app-html--></div>
    <script>
      console.error('body 执行')
    </script>
    <script type="module" src="/src/main.ts"></script>
  </body>
</html>

为确保在 gTool 加载完成后才执行后续逻辑,我们可以封装一个阻塞函数。实现如下:

<!DOCTYPE html>
<html>
  <head>
    <script>
      var htmlScriptScript = document.createElement('script')
      htmlScriptScript.type = 'text/javascript'
      htmlScriptScript.src = 'https://xxxx/common/tool.min.js?a=' + Math.random()
      htmlScriptScript.onload = function () {
        window.gTool = gTool
      }
      document.head.appendChild(htmlScriptScript)
    </script>
  </head>
  <body>
    <div id="app"><!--app-html--></div>
    <script>
+      async function main() {
+        function awaitTool() {
+          return new Promise(resolve => {
+            const interval = setInterval(() => {
+              if (window.gTool) {
+                clearInterval(interval)
+                resolve(null)
+              }
+            }, 50)
+          })
+        }
+        await awaitTool();
+        // other code
+      } 

+      main()
    </script>
    <script type="module" src="/src/main.ts"></script>
  </body>
</html>

main() 该函数会每隔 50ms 检查 window 对象上是否存在 gTool。若存在,说明脚本已加载完毕,可继续执行后续代码;否则,递归调用 main(),直到 gTool 加载完成。

2. 在 URL 地址后添加随机参数

这并非理想的解决方案。

尽管该方法可行,但每次更新都需要触发发版流程。由于发版涉及审核流程且存在次数限制,这种方式并不灵活。

以下两种引入方式效果一致,区别在于第二种在每次重新打包时会自动刷新时间戳参数。

<script type="text/javascript" src="https://xxxx/common/tool.min.js?time=1124112341"></script> // [!code hl]

<!-- 或者 -->

<script type="text/javascript" src="https://xxxx/common/tool.min.js?time=<%=Math.random()%>"></script>

3. 使用 document.write()

此方法不可行。

试图利用 document.write API 来防止 .html 文件缓存 script 脚本。

根据 MDN 文档说明,自 Chrome 55 版本起,Chrome 浏览器(可能包括其他现代浏览器)不再执行通过 document.write() 注入的 <script>,此举旨在防止使用 2G 网络连接的用户无法获取 HTTP 缓存。因此,利用此方法动态注入 <script> 脚本是行不通的。

场景

<script>
  document.write("<script src='https://xxxx/common/tool.min.js?rnd=" + Math.random() + "'></s " + " cript> ")
</script>

以上即为 .html 文件中防止 script 脚本缓存的三种方法详解,更多关于 HTML 防止脚本缓存的资料,请持续关注本站其他相关文章!

作者最新文章