chiwent / blog

个人博客,只在issue内更新
https://chiwent.github.io/blog
8 stars 0 forks source link

webpack基础之chunk #16

Open chiwent opened 5 years ago

chiwent commented 5 years ago

webpack基础之chunk

1 什么是chunk

在初学webpack的时候,有可能会对webpack模块这一概念产生误解,究竟什么算是webpack的模块,是bundle?还是chunk?或者module?下面会给出一些大致的解析。

webpack是一个代码打包工具,所有代码在webpack的角度看来都是一个个模块。在webpack的配置文件中,有一个module的配置选项,其包含了rules字段,它就是用来处理模块的规则,通过专门类别的loader来处理其对应的类型的模块。chunk就是webpack打包过程中,这些module的集合。webpack打包时,通过引用关系逐个打包,这些module就成了一个chunk。webpack在打包完成后,又会生成一系列的bundle文件。

在webpack的配置文件中,我们可以直观地看到entry、module、plugin这样的配置选项,但是chunk没有单独的配置字段,不过它会存在于CommonsChunkPlugin、optmization.splitChunks这类插件的配置中。

在webpack官方文档中,有一篇是关于webpack一些基本概念的解释:概念术语。在这里,它对于chunk、bundle、module的描述是这样的:

chunk:
这是 webpack 特定的术语被用在内部来管理 building 过程。bundle 由 chunk 组成,其中有几种类型(例如,入口 chunk(entry chunk) 和子 chunk(child chunk))。通常 chunk 会直接对应所输出的 bundle,但是有一些配置并不会产生一对一的关系。

bundle:
由多个不同的模块生成,bundles 包含了早已经过加载和编译的最终源文件版本

module:
提供比完整程序接触面(surface area)更小的离散功能块。精心编写的模块提供了可靠的抽象和封装界限,使得应用程序中每个模块都具有条理清楚的设计和明确的目的

一般来说,在不开启source-map的情况下,chunk和bundle的关系是一对一的。但是在开始source-map后,chunk和bundle就是一对多的关系,因为生成的bundle还包括了.map文件。

举个例子,有以下的webpack配置:

{
    entry: {
        index: ['./src/index.js'],
        login: ['./src/login.js']
    },
    output: {
        path: './dist',
        filename: '[name].bundle.js'
    },
    devtool: 'source-map'
}

module指的就是./src/index.js'./src/login.js',chunk指的就是index.bundle.jslogin.bundle.js,而bundle指的是index.bundle.jslogin.bundle.js,以及对应的map文件index.bundle.js.maplogin.bundle.js.map

所以,chunk可以简单地概括为webpack在进行模块(module)依赖分析的时候,代码分割出的代码块,chunk是过程中生成的代码块(半成品),而bundle是结果生成的代码块(成品)。

2 chunk是如何产生的

产生chunk的方式有三种:

2.1 entry产生chunk

配置入口entry的时候,可以有三种方式:

2.2 异步产生chunk

异步加载的模块,也需要产生chunk:

{
    entry: {
        'index': 'src/index.js'
    },
    output: {
        filename: '[name].bundle.js',
        chunkFilename: '[name].bundle.js'
    }
}
const Page = r => require.ensure([], () => r(require('./index.vue')), 'Page');

2.3 代码分割产生chunk

module.exports = {
    entry: {
        index: path.join(__dirname, './src/js/index.js'),
        login: path.join(__dirname, './src/js/login.js')
    },
    output: {
        path: path.join(__dirname, './dist'),
        filename: '[name].bundle.js',
        chunkFilename: '[name].bundle.js'
    },
    optimization: {
        runtimeChunk: "single",
        splitChunks: {
            cacheGroups: {
                commons: {
                    chunks: "initial",
                    minChunks: 2,
                    maxInitialRequests: 5, // The default limit is too small to showcase the effect
                    minSize: 0 // This is example is too small to create commons chunks
                },
                vendor: {
                    test: /node_modules/,
                    chunks: "initial",
                    name: "vendor",
                    priority: 10,
                    enforce: true
                }
            }
        }
    }
}

在上述配置中,会生成5个chunk,包括了两个入口文件直接生成的xxx.bundle.js,另外runtimeChunk: "single"会将Webpack在浏览器端运行时需要的代码单独抽离到一个文件,commons下的配置会产生一个Chunk,vendor下的配置会产生一个Chunk。

3 webpack文件命名中的hash、chunkhash、contenthash之间有什么区别

hash可以简单地认为是整个项目生成的hash值,在每次编译时计算得到的,每次编译完成后都会生成新的hash,即使改动了任何文件,都会导致所有文件名上的的hash值改变。所以hash无法实现前端静态资源在浏览器上的长缓存,此时需要chunkhash。

chunkhash是根据不同的入口文件进行依赖解析,构建对应的chunk,从而生成的hash值。

output: {
  filename: '[name].[chunkhash].js',
  path: path.join(__dirname, 'dist')
}

每次编译完成后,由于每个chunk的hash是不同的,所以对于每个chunk来说,如果chunk代码不变,那么hash值会保持不变,从而实现该资源在浏览器长缓存。

contenthash一般是在处理css样式文件时使用的:

const ExtractTextPlugin = require("extract-text-webpack-plugin");
module.exports = {
    module: {
        loaders: [
            {
                test: /\.css$/,
                loader: ExtractTextPlugin.extract("style-loader", "css-loader")
            },
            {
                test: /\.less$/,
                loader: ExtractTextPlugin.extract("style-loader", "css-loader!less-loader")
            }
        ]
    },
    plugins: [
        new ExtractTextPlugin("[name].[contenthash].css")
    ]
}




参考:

掘金 - Webpack 理解 Chunk