如果你是一名前端开发者,那么你一定对 webpack 不陌生。webpack 是一款强大的前端打包工具,可以帮助我们打包和管理各种前端资源,使得项目更加可维护和可靠。在使用 webpack 进行打包时,你会经常听到三个名词:hash、chunkhash 和 contenthash。这三个名词都涉及到打包后文件名的命名规则,但是它们各自的意义和使用场景却存在着差别。
hash
首先我们来看一下 hash。在 webpack 的打包过程中,如果我们想要生成一个唯一的文件名,那么我们可以使用 hash。每当文件内容发生改变时,hash 的值也会发生改变。使用 hash 作为文件名的好处是,能够保证打包后的文件名唯一,从而防止浏览器缓存的问题。
举个例子,假设我们有一个入口文件 main.js,它包含了一个 div 元素,并给它设置了一个背景图像。我们的 webpack.config.js 文件如下所示:
// javascriptcn.com 代码示例 const path = require('path'); const HtmlWebpackPlugin = require('html-webpack-plugin'); module.exports = { entry: './src/main.js', output: { filename: 'app.[hash].js', path: path.resolve(__dirname, 'dist') }, plugins: [ new HtmlWebpackPlugin({ title: 'Webpack hash Demo', template: './src/index.html' }) ] };
在上述配置中,我们在 output.filename 中使用了 [hash],表示生成唯一的文件名。当我们运行 webpack 后,生成的文件名为 app.f9c38f7fe73ea907d036.js。这里的 f9c38f7fe73ea907d036 就是本次打包的 hash 值。
在正式应用中,我们可以使用 MiniCssExtractPlugin 来提取 CSS 文件。使用 css-loader 和 style-loader 的时候也可使用以下的参数来生成 hash 名称的文件:
// javascriptcn.com 代码示例 { test: /\.css$/i, use: [ MiniCssExtractPlugin.loader, { loader: 'css-loader', options: { modules: true, } } ] },
上述代码中,使用了 MiniCssExtractPlugin.loader
从样式中提取 CSS,并使用了 options: { modules: true }
来启用 CSS Modules。最终生成的 CSS 文件名将会是一个包含 hash 值的唯一文件名。
需要注意的是,每一次打包后,所有打包后的文件,均会生成新的 hash 值。当我们不修改任何文件,再次打包时,也会生成不同的 hash 值。这是因为我们每次打包后的文件名都是唯一的,所以即使文件不变,我们生成的 hash 值也会发生变化。这样会导致在浏览器中缓存的文件因名字不同而被重新下载。
总结一下,使用 hash 可以通过修改文件名保证文件的唯一性,从而避免浏览器缓存的问题。但也正因为如此,如果我们只是改动了一个文件,那么所有文件的文件名都会改变。这种情况对于大型应用来说,会造成很多不必要的重复下载。
chunkhash
接下来我们来看 chunkhash。我们知道,当我们使用 webpack 进行打包时,打包后的文件可能会被分成多个块(chunk),例如入口代码块、异步加载代码块等。为了防止每次打包时所有文件的 hash 值都改变,我们可以使用 chunkhash。
chunkhash 的原理是,对于每一个 chunk,它都有自己的一个唯一 hash。当该 chunk 的内容发生变化时,它的 hash 值会改变,而其他 chunk 的 hash 值则不变。使用 chunkhash 作为文件名的好处是,只有发生改变的 chunk 的文件名会发生改变,其他 chunk 的文件名都不会改变。这样我们就可以通过模块热替换等技术实现部分打包和增量更新。
举个例子,假设我们项目中有两个入口文件:main.js 和 vendor.js。我们的 webpack.config.js 文件如下所示:
// javascriptcn.com 代码示例 const path = require('path'); const HtmlWebpackPlugin = require('html-webpack-plugin'); module.exports = { entry: { main: './src/main.js', vendor: './src/vendor.js' }, output: { filename: '[name].[chunkhash].js', path: path.resolve(__dirname, 'dist') }, plugins: [ new HtmlWebpackPlugin({ title: 'Webpack chunkhash Demo', template: './src/index.html' }) ] };
在上述配置中,我们在 output.filename 中使用了 [chunkhash],表示生成唯一的文件名。当我们运行 webpack 后,我们会得到两个输出文件:
- main.js:6632c9c6e725c6d0d01c.chunk.js
- vendor.js:89a774c74be936f1aee9.chunk.js
可以看到,main.js 和 vendor.js 分别对应了不同的 chunk,因此它们对应的 chunkhash 值也是不同的。这样当我们只修改了其中一个文件时,只有对应的 chunkhash 值会改变,而其他文件的文件名都不会发生改变。
需要注意的是,这种方法并不能完全解决潜在的缓存问题。如果我们修改了一些公共代码,例如添加一些新的函数,那么所有文件的文件名都会发生改变,这时候我们也需要重新下载这些文件。
总结一下,使用 chunkhash 可以保证只有发生改变的 chunk 的文件名会发生改变,其他文件的文件名都不会发生改变。这种方法适用于大型应用,可以实现模块热替换和增量更新。
contenthash
接下来我们来看 contenthash。使用 contenthash 的目的是保证文件名和文件内容有关系。当文件内容发生改变时,它的 contenthash 值也会发生改变。这样,即使使用了 hash 和 chunkhash 保证了唯一性和增量更新,我们也可以通过保证文件名和文件内容的关系避免文件被错误缓存的问题。
举个例子,假设我们有一个 CSS 文件 styles.css,它的内容为:
body { background-color: #F5F5F5; }
我们的 webpack.config.js 文件如下所示:
// javascriptcn.com 代码示例 const path = require('path'); const MiniCssExtractPlugin = require('mini-css-extract-plugin'); const HtmlWebpackPlugin = require('html-webpack-plugin'); module.exports = { entry: './src/main.js', output: { filename: 'app.js', path: path.resolve(__dirname, 'dist') }, module: { rules: [ { test: /\.css$/, use: [ MiniCssExtractPlugin.loader, { loader: 'css-loader', options: { modules: true, } } ] } ] }, plugins: [ new MiniCssExtractPlugin({ filename: 'styles.[contenthash].css' }), new HtmlWebpackPlugin({ title: 'Webpack contenthash Demo', template: './src/index.html' }) ] };
在上述配置中,我们在 MiniCssExtractPlugin 中使用了 [contenthash],表示生成唯一的文件名。当我们运行 webpack 后,生成的 CSS 文件名为 styles.5e5e5ce874d3f3b83d9e.css。这里的 5e5e5ce874d3f3b83d9e 就是根据文件内容生成的 contenthash 值。
需要注意的是,contenthash 的使用场景并不是很多。通常我们只需要使用 hash 和 chunkhash 就可以满足大部分需求。只有当我们对文件名和文件内容的对应关系有着特殊的要求,或者需要通过保证文件名和文件内容的关系来避免浏览器缓存的问题时,才需要使用 contenthash。
总结
通过本文的介绍,我们了解了 webpack 中的 hash、chunkhash 和 contenthash。这三者的使用场景和区别都十分重要,对于大型应用来说,选择合适的命名规则能够带来更佳的性能表现。我们可以根据具体需求来选择合适的命名规则,从而提高项目的可维护性和可靠性。
最后我们来看一下完整的 webpack.config.js 文件,它包含了所有三种命名规则的使用:
// javascriptcn.com 代码示例 const path = require('path'); const MiniCssExtractPlugin = require('mini-css-extract-plugin'); const HtmlWebpackPlugin = require('html-webpack-plugin'); module.exports = { entry: { main: './src/main.js', vendor: './src/vendor.js' }, output: { filename: '[name].[chunkhash].js', path: path.resolve(__dirname, 'dist') }, module: { rules: [ { test: /\.css$/, use: [ MiniCssExtractPlugin.loader, { loader: 'css-loader', options: { modules: true, } } ] } ] }, plugins: [ new MiniCssExtractPlugin({ filename: 'styles.[contenthash].css' }), new HtmlWebpackPlugin({ title: 'Webpack hash-chunkhash-contenthash Demo', template: './src/index.html' }) ] };
现在我们已经掌握了 webpack 中的三种命名规则,希望这篇文章能够帮助你更好地使用 webpack 进行打包和管理前端资源。
来源:JavaScript中文网 ,转载请注明来源 本文地址:https://www.javascriptcn.com/post/65453c707d4982a6ebefdaa5