history,hash缓存那些事

vue-router 中的 createWebHistory,createWebHashHistory两种模式

createWebHistory

是基于 window.history 对象是HTML5提供的用于维护当前标签页浏览历史的对象,主要功能是前进后退和在不刷新页面的情况下,修改地址栏里的URL地址。history 模式就是利用了 HTML5 historyAPI,所以也叫 HTML5 模式,Vue Router 中是用 createWebHistory() 创建。地址发生变化 都会出现404 而404 会在服务器上映射到index.html中,重新请求资源!

createWebHashHistory

是用 createWebHashHistory() 创建,hash指的是地址中 # 号以及后面的字符,这个 # 就是hash符号,中文名叫哈希符或锚点,哈希符后面的值,我们称之为哈希值!我们虽然在访问的过程,在 URL 会出现#,但是此方式不会抱憾在HTTP的请求中,所以对服务器没有任何影响,因为此方式不经过服务器,所以url的hash的改变不会重新加载页面,所以如果第一次加载完html 之后在后面操作都会出现,不会是最新的资源的情况!但是这中对SEO也有影响!

缓存

为了后面更好的归类,这里我把缓存分为两类,一类是静态资源,不管是图片 js css 等等 只要是打包生成出来的统一叫静态资源,第二种就是动态的 也就是我们在请求服务器的接口动态处理dom 填补动态数据!

动态资源缓存问题

不难看出,就是请求的url被缓存啦,其实也好好处理,前端项目基本都是统一的请求器,直接在请求器上统一拦截,url上加上时间戳后缀!这样保证每次url 不是相同的,这个在以前的项目中 ,客户用的ie浏览器,get请求 都被缓存,这个貌似是IE的特殊性的坑!当时就通过时间戳的形式处理!如果没有统一的请求器的,要么一个一个改,要么写个xhr去处理!

静态资源缓存问题

无论createWebHistory,createWebHashHistory的哪一种打包生成的一些静态资源做一次文件签名即可,vite.config.js配置处理,这样处理每次打包都会变更js的文件名,默认的只会改动变更的文件,

javascript 复制代码
  build: {
    hash: true,
    manifest: true,
    rollupOptions: {
      output: {
        // 入口文件名
        entryFileNames: `assets/[name].${timeStamp}.js`,
        // 块文件名
        chunkFileNames: `assets/[name]-[hash].${timeStamp}.js`,
        // 资源文件名 css 图片等等
        assetFileNames: `assets/[name]-[hash].${timeStamp}.[ext]`,
      },
    },
  }

nginx 上禁止缓存html

nginx 复制代码
        location = /index.html {
            root /Volumes/wanglaibin/work/vite-project/dist;
            expires 0;
            add_header Cache-Control "no-cache, no-store, must-revalidate";
        }

index.html 上禁止html

html 复制代码
<meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate">
<meta http-equiv="Pragma" content="no-cache">
<meta http-equiv="Expires" content="0">

上面的配置,无关createWebHistory,createWebHashHistory,因为只要重新加载html都会请求最新的资源,只是createWebHistory相对友好,因为每次地址变更都会请求都会经过服务器,而createWebHashHistory只有在第一次加载或者F5刷新才会请求资源。

针对createWebHashHistory的缓存方式

该方式如何处理,我们都知道,每次url地址变更没有经过服务器,所以才造成一个后面所有操作都会以第一次的加载html资源做处理,后面发布变,前端在方式过程中,只要不发生重载html的操作,都会出现请求旧的资源,如果没有访问过资源,可能出现找不到资源的情况!
我们知道这个造成的原因,所以我们通过某种方式,来告知相应的版本更新,做一次资源的重新加载!所以我们只能从路由的钩子上做处理,当我们路由变更时,请求前端的某个资源文件,记录当前的hash值,每次变更跟客户端做相应的比较,如果不一致,直接通知用户,当前系统版本升级,需要重新加载!之后重载整个html的入口!目前两种方式可以处理:
第一种在 vite中的异常事件中处理,在vite加载动态导入失败时,会触发 vite:preloadError 事件,所以我们监听这个错误事件,不过这种如果以前缓存过文件,改方式也不会出发最新的操作

javascript 复制代码
window.addEventListener('vite:preloadError', (event) => {
  console.log('检测到有新版本,5秒后即将刷新...');
  // 更好的用户体验 可以做个tips
  setTimeout(() => {
    window.location.reload(true) // 例如,刷新页面
    console.log('页面已更新为最新版本...');
  }, 5000)
})

第二种在 打包的过程中,我们生成相应的json文件信息,在路由的请求的钩子上处理,每次路由变更请求对比版本变化来决定是否做一个重载url;

在vite.config.js中编写插件生成相应的json文件

js 复制代码
import { fileURLToPath, URL } from 'node:url'
import upgradePlugin from './version-upgrade-plugin'
...
const timeStamp = new Date().getTime();
// https://vitejs.dev/config/
export default defineConfig({
  define: {
    __APP_VERSION__: timeStamp,
  },
  plugins: [
    ...
    upgradePlugin({
      version: timeStamp
    })
  ],
  ...
  build: {
    hash: true,
    manifest: true,
    rollupOptions: {
      output: {
        // 入口文件名
        entryFileNames: `assets/[name].${timeStamp}.js`,
        // 块文件名
        chunkFileNames: `assets/[name]-[hash].${timeStamp}.js`,
        // 资源文件名 css 图片等等
        assetFileNames: `assets/[name]-[hash].${timeStamp}.[ext]`,
      },
    },
  }
})

version-upgrade-plugin.js

json 复制代码
export default (options) => {
  let config
  return {
    name: 'version-upgrade',

    configResolved(resolvedConfig) {
      // 存储最终解析的配置
      config = resolvedConfig
    },

    async buildStart() {
      console.log('')
      console.log('生成项目文件信息')
      const fs = await import('fs').then((module) => module.default);
      const path = await import('path').then((module) => module.default)
      // 生成版本信息文件路径
      const file = config.publicDir + path.sep + 'version.json'
      // 这里使用编译时间作为版本信息
      const content = JSON.stringify({ version: options.version })
      if (fs.existsSync(config.publicDir)) {
        writeVersion(file, content)
      } else {
        fs.mkdir(config.publicDir, (err) => {
          if (err) throw err
          writeVersion(file, content)
        })
      }
      console.log(config, file)
    }
  }
}

const writeVersion = async (versionFile, content) => {
  const fs = await import('fs').then((module) => module.default);
  // 写入文件
  fs.writeFile(versionFile, content, 'utf8', (err) => {
    if (err) throw err
  })
}

路由上处理

js 复制代码
router.afterEach(async () => {
  await isHeavyLoadPage();
});

function isHeavyLoadPage() {
  if (process.env.NODE_ENV === 'development') return
  fetch(`version.json?t=${Date.now()}`)
    .then(res => res.json())
    .then(data => {
      console.log(data)
      if (__APP_VERSION__ !== data.version) {
        alert(__APP_VERSION__, data.version)
        setTimeout(() => {
          window.location.reload(true) // 例如,刷新页面
          console.log('页面已更新为最新版本...');
        }, 5000)
      }
    })
    .catch(function (e) {
      console.log(e)
    })

}

其实我们做项目找到问题的本质,从本质上找相应的解决方案进行处理,才不会无任何头绪!也不至于没有任何方向!

相关推荐
Oak Zhang2 小时前
sharding-jdbc自定义分片算法,表对应关系存储在mysql中,缓存到redis或者本地
redis·mysql·缓存
Swift社区3 小时前
LeetCode - #139 单词拆分
算法·leetcode·职场和发展
Kent_J_Truman3 小时前
greater<>() 、less<>()及运算符 < 重载在排序和堆中的使用
算法
门牙咬脆骨3 小时前
【Redis】redis缓存击穿,缓存雪崩,缓存穿透
数据库·redis·缓存
门牙咬脆骨3 小时前
【Redis】GEO数据结构
数据库·redis·缓存
IT 青年4 小时前
数据结构 (1)基本概念和术语
数据结构·算法
Dong雨4 小时前
力扣hot100-->栈/单调栈
算法·leetcode·职场和发展
SoraLuna4 小时前
「Mac玩转仓颉内测版24」基础篇4 - 浮点类型详解
开发语言·算法·macos·cangjie
liujjjiyun4 小时前
小R的随机播放顺序
数据结构·c++·算法
¥ 多多¥5 小时前
c++中mystring运算符重载
开发语言·c++·算法