PHP 和 JAVA 解决浏览器缓存 JS 和 CSS 文件

作者:elvbyte 发布时间: 2026-04-07 阅读量:4

一、背景

修改 JS 或 CSS 文件,发布到服务器后,访问经常会失效,需要清除一下浏览器缓存,才可以正常访问。

二、问题原因

浏览器会默认缓存 JS 和 CSS 文件,如果文件名未发生改变,则会直接调用缓存的文件,所以就会出现更新了 JS 或 CSS 文件后,访问不会生效。

三、解决方案

1、PHP

(1)版本号/时间戳防缓存(推荐)

在引用 CSS、JS 时给资源加上一个变化的参数,让浏览器认为是新的文件。

<!-- 用文件修改时间做版本号 -->
<link rel="stylesheet" href="/assets/css/style.css?v=<?php echo filemtime('assets/css/style.css'); ?>">
<script src="/assets/js/app.js?v=<?php echo filemtime('assets/js/app.js'); ?>"></script>

原理:文件更新后,filemtime() 会返回最新的修改时间,相当于 URL 不一样,浏览器就会去重新加载。

(2)发布时手动更换版本号

如果不想每次都读取 filemtime,可以写死一个版本号,每次上线时手动改:

<link rel="stylesheet" href="/assets/css/style.css?v=20251103">
<script src="/assets/js/app.js?v=20251103"></script>

(3)服务端设置 HTTP 头控制缓存

如果是后台管理系统,不需要长时间缓存,可以在 PHP 输出头部禁用或缩短缓存时间,例如:

<?php
header("Cache-Control: no-cache, must-revalidate"); // HTTP 1.1
header("Expires: Sat, 26 Jul 1997 05:00:00 GMT");   // 让它过期
?>

或者在 Nginx/Apache 配置中针对 .css .js 资源设置较短的 max-age。

2、JAVA

(1)使用版本号/时间戳(在模板里实现)

如果你用的是 Thymeleaf 或 FreeMarker 之类的模板引擎,可以在引用静态资源时加版本标记: Thymeleaf 示例:

<link th:href="@{/css/style.css(v=${T(java.lang.System).currentTimeMillis()})}">
<script th:src="@{/js/app.js(v=${T(java.lang.System).currentTimeMillis()})}"></script>

这样每次页面渲染,都会带上一个当前时间参数,浏览器自然不会用旧缓存。

(2)用文件实际修改时间做版本号(更精准)

可以写一个工具方法,在模板里调用文件的修改时间: 工具类:

import java.nio.file.*;
import java.io.IOException;
​
public class VersionUtil {
    public static long fileVersion(String path) {
        try {
            return Files.getLastModifiedTime(Paths.get(path)).toMillis();
        } catch (IOException e) {
            return System.currentTimeMillis();
        }
    }
}

Thymeleaf 调用:

<link th:href="@{/css/style.css(v=${T(com.example.util.VersionUtil).fileVersion('src/main/resources/static/css/style.css')})}">

这样只有文件真正修改才会更新版本号。

(3)使用 Spring Boot 资源版本化(推荐生产环境用)

Spring MVC 提供了 ResourceUrlEncodingFilter,可以根据构建过程自动给静态文件加 hash 值,这样文件名都不同,缓存自然失效。 配置例子:

import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import org.springframework.web.servlet.resource.VersionResourceResolver;
​
@Configuration
public class WebConfig implements WebMvcConfigurer {
​
    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler("/**")
                .addResourceLocations("classpath:/static/")
                .resourceChain(true)
                .addResolver(new VersionResourceResolver()
                        .addContentVersionStrategy("/**"));
    }
}

这样 /css/style.css 在生成时会变成 /css/style.css?v=文件内容hash,只要文件改变 hash 就会变。

(4)构建工具解决(推荐前端项目用)

如果前端用 Webpack / Vite 之类的构建工具,可以在打包时直接让生成的文件带 hash:

style.3a8b7.css
app.81f2c.js

然后 Thymeleaf 或 HTML 模板直接引用打包后的文件名。这样缓存问题完全解决。

3、JS

由于 JS 无法直接获取本地文件,所以没法根据文件修改日期自动添加版本号,因此我们采用 JS 追加版本号的方式。

(1)新建 config.js 文件

文件内容如下:

window.APP_VERSION = "v1.0.0";
(function () {
    const ver = window.APP_VERSION || '1.0.0';
    console.log(`应用版本号: ${ver}`);
    // 处理 CSS 链接
    document.querySelectorAll('link[rel="stylesheet"]').forEach(link => {
        const href = link.getAttribute('href');
        // 跳过外部CDN(避免污染)
        if (!href.startsWith('http')) {
            link.setAttribute('href', `${href}?v=${ver}`);
        }
    });
​
    // 处理 JS 脚本
    document.querySelectorAll('script[src]').forEach(script => {
        const src = script.getAttribute('src');
        if (!src.startsWith('http') && !src.includes('APP_VERSION_LOADER')) {
            // 防止给自己加版本号导致重复加载
            script.setAttribute('src', `${src}?v=${ver}`);
        }
    });
})();

(2)将 config.js 引用在 html 页面最后。

<!--配置-->
<script type="text/javascript" src="../static/custom/js/config.js"></script>

运行网页后,即可自动给所有 css 和 js 文件引用增加版本号。