乐趣区

项目中资源缓存机制实践静态资源和本地数据缓存

网络资源的缓存

核心方案

  1. HTML 文件 no-cache
  2. js,css 文件 时间戳 / 哈希 / 版本号,长缓存
  3. 图片资源 长缓存,更新时使用新链接地址
1. 前后端未分离,且未引入构建工具的项目

方案:每次上线时,维护一个版本记录,由后端给每个页面上提供当前的版本号,前端在引用 js 和 css 时将版本号放在资源的 query 参数中。

例如:

 原先:<script src="/static/js/utils.js"></script>
<script src="/static/js/index.js"></script>

改后:// 每次发版维护的版本号
const version = '2.2';
// 动态填写 js 引用
document.write("<script type='text/javascript'src=./utils.js?version="+ version + "><\/script>"); 

2. 使用构建工具的项目

方案:使用 webpack 和 grunt 之类的工具,每次发版时使用工具使用 hash 更新静态资源的版本

例如:

<script src="/static/js/0.2e29befc85b7d79378f4.js"></script>

本地缓存的管理

cookie、localStorage、sessionStorage、IndexDB、App cache、service worker

使用本地存储的注意点:
  1. 使用要有节制 ,同一个域名下共享 localstorage 空间,如果一个域名下的业务非常多,很可能存储量超过限制。像我们的不同业务域名时 projectA.XXX.com, projectB.XXX.com 而不是 www.XXX.com/projectA, www.XXX.com/projectB。使用子域名可以分离 storage 空间,互不干扰。
  2. 使用要有降级方案 ,不能完全依赖 storge 完成业务功能。如果 storage 读写失败,使用备用方案。
  3. 使用时要有版本管理 。storage 中储存的 key 也要有版本管理,便于迭代时避免不同版本之间数据冲突。V1.0.0_XXXX

下面写了一个用于本地存取的工具,项目中使用时可以将工具绑定到全局空间中使用。例如在 Vue 生态中,可以结合 Vux 一起使用

const namespace = 'matchu'
const version = '2.2'

function setLocal(key, value, priority) {
  try {
    // 设置的优先级不合法,为非数字时, 使用最低优先级 0
    priority = /^[0-9]\d*$/.test(priority) ? priority : 0
    localStorage.setItem(`${namespace}_V:${version}_P:${priority}`, JSON.stringify(value))
    return getLocal(key)
  } catch (err) {if (/exceeded the quota/.test(err)) {
      // 存储超出限额, 清除低优先级的数据
      clearLowPriorityLocal(priority)
    }
  }
}

function getLocal(key, value) {
  try {let result = localStorage.getItem(key)
    // 读取成功立即返回值
    // 读取失败则尝试先存储数据,再返回已存入的数据
    result = result ? JSON.parse(result) : setLocal(key, value)
    return result
  } catch (err) {
    // 读取失败
    console.log(err)
  }
}

// 删除指定 key 数据
function removeLocal(key) {
  try {localStorage.removeItem(key)
  } catch (err) {
    // 清除所有数据失败
    console.log(err)
  }
}

// 删除所有数据
function clearAllLocal() {
  try {localStorage.clear()
  } catch (err) {
    // 清除所有数据失败
    console.log(err)
  }
}

// 正则匹配删除当前命名空间且非当前版本的数据
function clearOtherVersionLocal() {
  try {const reg = new RegExp(`^${namespace}_V:(?!${version})`)
    Object.keys(localStorage).forEach(key => {if (reg.test(key)) {removeLocal(key)
      }
    })
  } catch (err) {console.log(err)
  }
}

// 正则匹配删除低于当前优先级的数据
function clearLowPriorityLocal(priority) {
  try {const reg = new RegExp(`[^${namespace}_V:${version}_P:]`)
    Object.keys(localStorage).forEach(key => {const index = key.match(reg).index
      if (index && key.slice(index) < priority) {removeLocal(key)
      }
    })
  } catch (err) {console.log(err)
  }
}
export default {
  setLocal: setLocal, // 存 storage 数据
  getLocal: getLocal, // 取 storage
  removeLocal: removeLocal, // 删除指定 key 的数据
  clearAllLocal: clearAllLocal, // 删除全部数据
  clearOtherVersionLocal: clearOtherVersionLocal, // 清除非本次版本的数据
  clearLowPriorityLocal: clearLowPriorityLocal // 清除低于当前优先级的数据
}

退出移动版