项目上线解决浏览器缓存问题
1.什么是浏览器缓存
简单来说,浏览器缓存就是把一个已经请求过的Web资源(如html页面,图片,js,数据等)拷贝一份副本储存在浏览器中。缓存会根据进来的请求保存输出内容的副本。当下一个请求来到的时候,如果是相同的URL,缓存会根据缓存机制决定是直接使用副本响应访问请求,还是向源服务器再次发送请求。比较常见的就是浏览器会缓存访问过网站的网页,当再次访问这个URL地址的时候,如果网页没有更新,就不会再次下载网页,而是直接使用本地缓存的网页。只有当网站明确标识资源已经更新,浏览器才会再次下载网页。
2.浏览器和网站服务器是如何标识网站页面是否更新的机制
第一次请求:
再次请求资源的时候:
3.nginx配置缓存策略
- 使用缓存前,去源服务器校验有效性,nginx设置方法
location / {
add_header Cache-Control no-cache;
}
- 如果不想html被缓存,nginx设置方法
location / { add_header Cache-Control no-store,max-age:0; }
#no-store只能阻止新的资源不会被缓存,不能阻止已经缓存的资源仍被使用,添加max-age:0,可以清除旧缓存;这个设置意味着每次请求都会去源服务器获取资源,状态码就是200;
- 对不变的js、css等静态资源建议设置:
location / { add_header Cache-Control public, max-age:最大值, immutable,max-age; //max-age可以设置超大的值,以达到永不过期的目的; }
4.解决缓存问题的思路
1、webpack 就内置了 hash 计算方法,对生成的文件可以在输出的文件中添加 hash 字段。
2、webpack 内置的 hash 有三种:
hash:每次构建会生成一个hash。和整个项目有关,只要项目有文件更改,就会改变hash。chunkhash:和 webpack 打包生成的chunk相关。每一个entry,都会有不用的hash。contenthash:和单个文件的内容有关。指定文件的内容发生改变,就会改变hash。
3.# 以vue-cli项目为例,vue.config.js 打包配置
const port = process.env.port || 8081 // 端口
const Timestamp = new Date().getTime();
const MiniCssExtractPlugin = require("mini-css-extract-plugin")
module.exports = {
// 部署生产环境和开发环境下的URL。
// 默认情况下,Vue CLI 会假设你的应用是被部署在一个域名的根路径上
//development:未压缩代码;production:压缩代码
publicPath: process.env.NODE_ENV === "production" ? "/" : "/",
// 在npm run build 或 yarn build 时 ,生成文件的目录名称(要和baseUrl的生产环境路径一致)(默认dist)
outputDir: 'dist',
// 用于放置生成的静态资源 (js、css、img、fonts) 的;(项目打包之后,静态资源会放在这个文件夹下)
assetsDir: 'static',
//指定生成的 index.html 的输出路径 (相对于 outputDir)。也可以是一个绝对路径
indexPath: 'index.html',
//默认情况下,生成的静态资源在它们的文件名中包含了 hash 以便更好的控制缓存。
//然而,这也要求 index 的 HTML 是被 Vue CLI 自动生成的。
//如果你无法使用 Vue CLI 生成的 index HTML,你可以通过将这个选项设为 false 来关闭文件名哈希。
filenameHashing: false,
// 是否开启eslint保存检测,有效值:ture | false | 'error'
lintOnSave: process.env.NODE_ENV === 'development',
// 如果你不需要生产环境的 source map,可以将其设置为 false 以加速生产环境构建。
productionSourceMap: false,
devServer: {
host: '0.0.0.0',
port: port,
open: true,
proxy: {
[process.env.VUE_APP_BASE_API]: {
target: `http://localhost:8081`,
changeOrigin: true,
pathRewrite: {
['^' + process.env.VUE_APP_BASE_API]: ''
}
}
},
disableHostCheck: true
},
chainWebpack(config) {
// 从生成的资源覆写 filename 或 chunkFilename 时,assetsDir 会被忽略。
config.output.filename(`static/js/[hash].[name].${Timestamp}.js`).end();
config.output.chunkFilename('static/js/[hash].[name].' + Timestamp + '.js').end()
// css output config
let miniCssExtractPlugin = new MiniCssExtractPlugin({
filename: `static/css/[hash].[name].${Timestamp}.css`,
chunkFilename: `static/css/[hash].[name].${Timestamp}.css`
})
config.plugin('extract-css').use(miniCssExtractPlugin).end();
// 给img配置Timestamp
config.module.rule('images').use('url-loader').tap(options => {
options.name = `static/img/[hash].[name].${Timestamp}.[ext]`
options.fallback = {
loader: 'file-loader',
options: {
name: `static/img/[hash].[name].${Timestamp}.[ext]`
}
}
return options
})
}
}
案例2
const path = require('path');
const HotHashWebpackPlugin = require('hot-hash-webpack-plugin');
const WebpackBar = require('webpackbar');
const resolve = (dir) => path.join(__dirname, '.', dir);
module.exports = {productionSourceMap: false,
publicPath: './',
outputDir: 'dist',
assetsDir: 'assets',
devServer:
{
port: 9999,
host: '0.0.0.0',
https: false,
open: true
},
chainWebpack: (config) => {
const types = ['vue-modules', 'vue', 'normal-modules', 'normal'];
types.forEach(type => {
let rule = config.module.rule('less').oneOf(type)
rule.use('style-resource').loader('style-resources-loader').options({
patterns: [path.resolve(__dirname, './lessVariates.less')]
});
});
config.resolve.alias.set('@', resolve('src'))
.set('api', resolve('src/apis'))
.set('common', resolve('src/common'))
config.module.rule('images').use('url-loader')
.tap(options => ({
name: './assets/images/[name].[ext]',
quality: 85,limit: 0,esModule: false,
}));
config.module.rule('svg').test(/.svg$/)
.include.add(resolve('src/svg'))
.end().use('svg-sprite-loader').loader('svg-sprite-loader');
config.plugin('define').tap(args => [{
...args,
"window.isDefine": JSON.stringify(true)
}]);
// 生产环境配置
if (process.env.NODE_ENV === 'production') {
config.output.filename('./js/[name].[chunkhash:8].js');
config.output.chunkFilename('./js/[name].[chunkhash:8].js');
config.plugin('extract-css').tap(args => [{
filename: 'css/[name].[contenthash:8].css',
chunkFilename: 'css/[name].[contenthash:8].css'}]);
config.plugin('hotHash').use(HotHashWebpackPlugin, [{ version : '1.0.0'}]);
config.plugin('webpackBar').use(WebpackBar);
config.optimization.minimize(true).minimizer('terser').tap(args => {
let { terserOptions } = args[0];
terserOptions.compress.drop_console = true;
terserOptions.compress.drop_debugger = true;
return args});config.optimization.splitChunks({
cacheGroups: {
common: {
name: 'common',
chunks: 'all',
minSize: 1,
minChunks: 2,
priority: 1
},
vendor: {
name: 'chunk-libs',
chunks: 'all',
test: /[\/]node_modules[\/]/,
priority: 10
}
}
});
}
}
};