soulcm / blog

2017开通
6 stars 0 forks source link

webpack构建之hash缓存的利用 #6

Open soulcm opened 7 years ago

soulcm commented 7 years ago

webpack构建之hash缓存的利用

众所周知,优化一个页面的方法之一就是利用浏览器的缓存机制,在文件名不改变的情况下,使客户端不用频繁向服务端请求和重复下载相同的资源,节约了流量的开销。

现在前端的主要构建工具就是使用webpack,现在就说说使用webpack时遇到的hash的坑。

在生产环境发布时,为了利用缓存,都会加一个标识,webpack打包时也可加入这个功能,对于一个基本的应用,最开始的用法估计这样,见如下代码

module.exports = {
    entry: {
        index: path.resolve(__dirname, '../src/js/index.js'),
        index2: path.resolve(__dirname, '../src/js/index2.js'),
        vendor: ['react', 'react-dom'] //第三方模块
    },

    output: {
        path: path.resolve(__dirname, '../dist'),
        filename: '[name].[hash:8].js',
        publicPath: '/'
    },

    module: {
        rules: [{
            test: /\.jsx?$/,
            use: ['babel-loader'],
            exclude: /node_modules/
        }
        //省略其他loader
        ]
    },

    plugins: [
         new CommonsChunkPlugin({
            name: 'vendor'
        })
        //省略其他plugin
    ] 
}

打包结果如下:

hash1

这里的hash是每次编译时所计算出来的一个值,因此当任何一个文件修改,hash都将会改变,而且所打包出来的文件名也将不一样了,这样对于需频繁发布的项目不是很友好,会造成每次有新版本,用户浏览器都将重新下载所有的文件,为了避免这种情况,webpack还提供了chunkhash(:这里提取出的css文件hash值不一样,是因为使用的是extract-text-webpack-plugin插件,它提供了自己的一个contenhash,也是对于css文件建议的一种用法,保证了css有自己独立的hash,不会受到js文件的干扰)

将上述代码中的hash换成chunkhash后打包结果如下:

hash2

看到每个chunk有自己单独的hash值,此时只修改某一个模块里的文件,将不会影响到其他的模块打包出的hash值了,这样也就能充分利用hash缓存了。


你以为到此处就结束了吗,too young too simple!

你可以试试改动一个文件,打包后vendor的hash值每次都是在变化的,第三方模块是最不长改动的模块,更应该被缓存住,可为什么vendor的chunkhash总是变化,是因为webpack runtime由于entry对应的Id变化而发生了变化,chunkhash的计算又依赖于runtime,因此vendor的chunkhash也发生了变化。

为了解决这个问题,我们首先得保证chunkId的稳定,参考webpack2的文档caching,可以使用HashedModuleIdsPlugin的插件(webpack1也可以使用,但需要将HashedModuleIdsPlugin.js自行引入),然后就是创建一个额外的chunk来提取runtime,就是文档中说的manifest,部分代码如下

plugins: [
    new CommonsChunkPlugin({
    names: ['vendor', 'manifest'],
        minChunks: Infinity
    }),
    new webapck.HashedModuleIdsPlugin()
]

打包后将多出一个很小的manifest.js的文件,但保证了vendor的hash没有改变,对于manifest的内容我们需要优先引入,在此可以借助inline-manifest-webpack-plugin将manifest内容内联进html文件中,以免多发一次js的请求,使用方式可直接参考文档。

至此对于hash的变化才算真正结束,才能达到利用hash的变化真正的控制住缓存。

PS. 建议不要用webpack-md5-hash,会有坑的,见webpack-md5-hash-issue

参考资料:

webpack的cache

extract-text-webpack-plugin

webpack-md5-hash

http缓存

inline-manifest-webpack-plugin

webpack-md5-hash-issue