vue项目上线解决浏览器缓存问题

2,156 阅读3分钟

项目上线解决浏览器缓存问题

1.什么是浏览器缓存

简单来说,浏览器缓存就是把一个已经请求过的Web资源(如html页面,图片,js,数据等)拷贝一份副本储存在浏览器中。缓存会根据进来的请求保存输出内容的副本。当下一个请求来到的时候,如果是相同的URL,缓存会根据缓存机制决定是直接使用副本响应访问请求,还是向源服务器再次发送请求。比较常见的就是浏览器会缓存访问过网站的网页,当再次访问这个URL地址的时候,如果网页没有更新,就不会再次下载网页,而是直接使用本地缓存的网页。只有当网站明确标识资源已经更新,浏览器才会再次下载网页。

2.浏览器和网站服务器是如何标识网站页面是否更新的机制

第一次请求:

第一次请求.png

再次请求资源的时候:

image.png

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
                            }
                        }
                    });
                }
            }
};