常常让人摸不着头脑的是 chunk 和 bundle 这两个概念。chunk,翻译过来就是大块,也就是代码块;而 bundle 则是束,包的意思。从 webpack 给出的术语表中是这么解释的:前端
Chunk: This webpack-specific term is used internally to manage the bundling process. Bundles are composed out of chunks, of which there are several types (e.g. entry and child). Typically, chunks directly correspond with the output bundles however, there are some configurations that don't yield a one-to-one relationship.webpack
chunk 在 webpack 中用于内部打包的过程;bundle 由 chunk 组成,通常来讲,chunk 和 bundle 是对应的,可是也可能经过配置改变它们一一对应的关系。git
Bundle: Produced from a number of distinct modules, bundles contain the final versions of source files that have already undergone the loading and compilation process.github
bundle 由不一样的模块组成,包含已经进行加载和通过编译的源文件的最终输出文件。web
webpack 文档的解释很模糊,chunk 实际上是 code splitting 中的概念,当使用到 code splitting 将 bundle 拆分出多个 chunk 就能体会到 chunk 和 bundle 的区别了。浏览器
webpack 中用来管理输出的配置项主要就是output配置项,output可选的属性仍是不少的,经常使用的有如下部分:缓存
从概念解释上来讲,output.filename指定的是主 bundle 的文件名称;output.chunkFilename指定的是 chunk 的文件名称,若是为 webpack 只指定了一个入口entry,那么output.chunkFilename是没啥用的,只有代码拆分的时候指定多个 chunk,这个配置项才能体现出来,拆分出的 chunk 若是找不到output.chunkFilename就会继而使用output.filename做为 chunk 文件名。ide
web 开发中常常遇到的一个问题就是浏览器对资源的缓存,致使发布的新的 JS 文件没法生效;过去解决方式通常是在 JS 的文件名后面添加一串不重复的版本号。在工程化的前端项目里,显然没法经过手动修改文件名来完成替换。
缓存是有用的,经过代码拆分,咱们能够作到将一些不会常常改变的核心代码抽成一个 chunk 进行打包,并赋予一个长期缓存来解决浏览器重复请求网络去加载资源的问题。对于不常更改的 chunk,咱们但愿每次打包它们的名称都是固定的,而对于常常修改的 chunk,须要根据内容去每次生成一个惟一的 chunk 名称来保证更新客户端的缓存。
一般 webpack 会为每个模块分配一个惟一的模块标识符 module identifier,这个 id 是一个 Int 类型的数字,而且一般从0开始,依据生产的 chunk 依次递增。
webpack 可使用一种称为 substitution(可替换模板字符串) 的方式,经过使用内容散列(content hash)替换在output.filename或output.chunkFilename配置的模板字符串来做为输出 bundle 文件的名称,这样在文件内容修改时,会计算出新的 hash,浏览器会使用新的名称加载文件,从而使缓存无效。
具体可使用的模板字符串见—— loader-utils.interpolateName
模板字符串 | 含义 |
---|---|
[hash] | 根据模块 id 生成的 hash |
[contenthash] | 根据文件内容生成的 hash,每一个文件资源都不相同 |
[chunkhash] | 根据每一个 chunk 内容生成的 hash |
[name] | module name,若是 module 没有名称,则会使用其 id 做为名称 |
[id] | module identifier,默认是根据模块引入的顺序,从0开始的整数 |
[query] | |
[function] |
在上面的模板字符串中存在三种 hash,默认三种 hash 的长度都是20个字符长度,能够经过加 length 的方法[xxxhash::<length>]指定 hash 的长度。而且若是开发环境使用 WDS,那么[contenthash]没法是用于开发环境的。
第一种[hash],须要注意的是它是根据模块 id 生成的,因此每一个 chunk 获得的值都是同样的,在指定代码拆分之后,对其作了测试,能够看到两个 chunk 的 hash 都是同样的。
若是修改其中一个 chunk 的模块代码,全部 chunk 的 hash 值都会发生变化,因此使用[hash]是不稳定的,达不到上面咱们说的目的。
[chunkhash]是根据每一个 chunk 内容生成的 hash 值,这种状况在有些时候它是稳定的,我在修改单独入口文件的模块代码时,并未影响其它 chunk 的 hash 值。
可是当 chunk 内 CSS 和 JS 混杂的时候,例如在 React 中import一个单独的 CSS 文件,这是很常见的事,若是对output.filename使用了[chunkhash],而对导出的 CSS 也使用了[chunkhash],那么 JS 和 CSS 获得的 hash 值将是同样的,这时候 JS 和 CSS 的变化会相互影响。例以下面的配置致使的结果是 JS 主 bundle 的 hash 值和 CSS 的 hash 值始终同样。
module.exports = { output: {filename: isProduction ? 'static/js/[name].[chunkhash].js' : 'static/js/bundle.js',path: path.resolve(__dirname, 'build'), }, plugins: [ isProduction && new MiniCssExtractPlugin({filename: 'static/css/[name].[chunkhash].css', }), , ], };复制代码
而若是仅对 JS 使用[chunkhash],而 CSS 使用[contenthash],那么 CSS 发生变化,JS 的 hash 名称同样也会变。
module.exports = { output: {filename: isProduction ? 'static/js/[name].[chunkhash].js' : 'static/js/bundle.js',path: path.resolve(__dirname, 'build'), }, plugins: [ isProduction && new MiniCssExtractPlugin({filename: 'static/css/[name].[contenthash].css', }), , ], };复制代码
至于[contenthash]则是根据具体的模块内容生成的 hash 值,它能检测细微层次 module 的变化,因为 chunk 包含 module,[contenthash]是为单个 module 准备的,在使用[contenthash]之后,chunk 中的 CSS 和 JS 模块不会相互影响。
最后对于进行 code splitting 的项目,建议以下的配置,[contenthash]还能够附加像[contenthash:10]这样的形式来决定生成的 hash 字符串的长度。
module.exports = function(env) { const isDevelopment = env.NODE_ENV === 'development'; const isProduction = env.NODE_ENV === 'production'; return {mode: isProduction ? 'production' : isDevelopment && 'development',output: { filename: isProduction ? 'static/js/[name].[contenthash].js': 'static/js/bundle.js', chunkFilename: isProduction ? 'static/js/[name].[contenthash].chunk.js': 'static/js/[name].chunk.js', },plugins: [ isProduction &&new MiniCssExtractPlugin({ filename: 'static/css/[name].[contenthash].css', chunkFilename: 'static/css/[name].[contenthash].chunk.css', }), ], }; };复制代码
当使用[contenthash]替换 chunk 名称的时候,对于修改过的 chunk,每次都会生成一个具备新的 chunk 名的 chunk,而旧的 chunk 会依然保留在output.path文件夹中,这些垃圾文件会随着每次 build 愈来愈多。
clean-webpack-plugin是负责清理 build 文件夹的插件,默认状况下,这个插件会清空在output.path文件夹里的全部文件,以及每次成功重建后全部未使用的 webpack 静态资源。如今这个插件已经到了 V3.0 版本。
yarn add clean-webpack-plugin -D复制代码
// 须要注意这里要带括号const { CleanWebpackPlugin } = require('clean-webpack-plugin'); //清理build文件夹module.exports = { plugins: [isProduction && new CleanWebpackPlugin()], };复制代码