您现在的位置是:网站首页 > 拒绝缓存(每次请求都带 '?v=Math.random()')文章详情

拒绝缓存(每次请求都带 '?v=Math.random()')

为什么需要拒绝缓存

浏览器缓存机制虽然能提升页面加载速度,但有时会导致开发者获取不到最新的资源版本。特别是在频繁更新的项目中,缓存可能让用户看到旧版代码,引发功能异常。最常见的场景包括:CSS样式更新后未生效、JS修复bug后依然报错、图片替换后显示旧图等。

缓存问题的典型表现

  1. CSS样式不更新:修改了.button的颜色,但用户浏览器依然显示旧样式
  2. JS文件缓存:修复了某个函数bug,但用户端仍在执行错误代码
  3. API数据陈旧:前端请求接口时返回304状态码而非200
// 典型问题复现
fetch('/api/data') // 可能返回缓存结果
  .then(response => console.log(response.status)) // 可能输出304

时间戳解决方案原理

通过在URL后附加随机参数(如?v=Math.random()),使浏览器将每个请求视为全新资源请求。这种方法利用了HTTP缓存机制的特性:不同URL被视为不同资源,即使它们实际指向相同内容。

// 基础实现示例
const url = `/api/data?v=${Math.random()}`
fetch(url)
  .then(response => response.json())
  .then(data => console.log(data))

具体实现方式

基础时间戳方案

function getUrlWithCacheBust(url) {
  return `${url}${url.includes('?') ? '&' : '?'}v=${Date.now()}`
}

// 使用示例
const apiUrl = getUrlWithCacheBust('/api/user')
// 输出类似:/api/user?v=1623456789123

Webpack项目配置方案

在webpack.config.js中配置output:

module.exports = {
  output: {
    filename: '[name].[contenthash].js',
    chunkFilename: '[name].[contenthash].js'
  }
}

Vue/React框架方案

// React组件内使用
function ImageWithCacheBust({ src }) {
  return <img src={`${src}?v=${new Date().getTime()}`} />
}

// Vue单文件组件
<template>
  <img :src="`${imageUrl}?v=${timestamp}`" />
</template>

<script>
export default {
  data() {
    return {
      timestamp: Date.now()
    }
  }
}
</script>

进阶应用场景

动态加载第三方库

function loadScript(url) {
  const script = document.createElement('script')
  script.src = `${url}?v=${Math.random().toString(36).substr(2, 9)}`
  document.head.appendChild(script)
}

// 使用
loadScript('https://example.com/sdk.js')

配合Service Worker使用

// service-worker.js
self.addEventListener('fetch', event => {
  const url = new URL(event.request.url)
  if (url.searchParams.has('v')) {
    // 处理带版本号的请求
    const cacheKey = url.pathname // 忽略查询参数
    event.respondWith(caches.match(cacheKey))
  }
})

潜在问题与解决方案

  1. CDN缓存失效:过度使用随机参数可能导致CDN命中率下降

    • 解决方案:改用构建时生成的hash值而非随机数
  2. 浏览器历史记录污染:每个带随机参数的URL都会被记录

    • 解决方案:对重要页面避免使用,或使用replaceState清理
  3. 缓存完全失效:失去缓存带来的性能优势

    • 平衡方案:对静态资源使用内容hash,对API保留缓存控制头
// 平衡方案示例
function smartCacheBust(url, isStatic = false) {
  if (isStatic) {
    return url // 静态资源走正常缓存
  }
  return `${url}?v=${Date.now()}`
}

性能优化建议

  1. 按资源类型区别处理

    • 频繁变更的API:使用时间戳
    • 不常变的静态资源:使用webpack的contenthash
    • 第三方资源:允许浏览器缓存
  2. HTTP缓存头配合

# 对需要缓存的资源
Cache-Control: public, max-age=31536000, immutable

# 对需要避免缓存的API
Cache-Control: no-cache
  1. 智能更新策略
// 使用ETag/Last-Modified的智能更新
fetch('/api/data', {
  headers: {
    'Cache-Control': 'no-cache',
    'If-None-Match': previousETag
  }
})

现代前端框架的最佳实践

Next.js解决方案

// next.config.js
module.exports = {
  generateBuildId: async () => {
    return process.env.GIT_COMMIT_HASH || Date.now().toString()
  }
}

Nuxt.js配置方案

// nuxt.config.js
export default {
  build: {
    filenames: {
      chunk: '[name].[contenthash].js'
    }
  }
}

监控与调试技巧

Chrome开发者工具中检查请求:

  1. 打开Network面板
  2. 查看请求的Size列:
    • (memory cache)表示从缓存读取
    • 数字值表示网络传输
// 调试代码示例
console.log('当前资源版本:', performance.getEntries()
  .filter(entry => entry.name.includes('?'))
  .map(entry => new URL(entry.name).searchParams.get('v')))

替代方案比较

方案 优点 缺点
随机参数 简单直接 CDN不友好
内容hash 精确控制 需要构建工具
版本目录 清晰明确 部署复杂
HTTP头 标准规范 需要服务端配合

服务端配合策略

Node.js Express示例:

const express = require('express')
const app = express()

// 设置无缓存头
app.get('/api/no-cache', (req, res) => {
  res.set({
    'Cache-Control': 'no-store, no-cache, must-revalidate',
    'Pragma': 'no-cache',
    'Expires': '0'
  })
  res.json({ data: Date.now() })
})

// 智能缓存验证
app.get('/api/etag', (req, res) => {
  const etag = '12345'
  res.set('ETag', etag)
  if (req.headers['if-none-match'] === etag) {
    return res.status(304).end()
  }
  res.json({ data: '新鲜内容' })
})

移动端特殊考虑

  1. WebView缓存问题

    // Android WebView设置
    webView.getSettings().setCacheMode(WebSettings.LOAD_NO_CACHE)
    
  2. 混合应用解决方案

    // Cordova/Ionic方案
    document.addEventListener('deviceready', () => {
      window.cordova.plugin.http.setHeader('Cache-Control', 'no-cache')
    })
    
  3. PWA应用策略

    // workbox配置
    workbox.routing.registerRoute(
      /\.js$/,
      new workbox.strategies.StaleWhileRevalidate({
        cacheName: 'js-cache'
      })
    )
    

我的名片

网名:~川~

岗位:console.log 调试员

坐标:重庆市-九龙坡区

邮箱:cc@qdcc.cn

沙漏人生

站点信息

  • 建站时间:2013/03/16
  • 本站运行
  • 文章数量
  • 总访问量
微信公众号
每次关注
都是向财富自由迈进的一步