Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

webpack基础之chunk #16

Open
chiwent opened this issue Aug 11, 2019 · 0 comments
Open

webpack基础之chunk #16

chiwent opened this issue Aug 11, 2019 · 0 comments
Labels

Comments

@chiwent
Copy link
Owner

chiwent commented Aug 11, 2019

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的方式有三种:

  • entry配置入口
  • 异步加载模块
  • 代码分割

2.1 entry产生chunk

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

  • 传入单个字符串:
entry: './src/index.js'

这就是单页应用

  • 传入数组
entry: ['./src/js/login.js', './src/js/login.js']

这是多页应用,但是在这种配置下只会产生一个chunk,webpack会将入口文件的代码都打包到一个bundle里面。

  • 传入对象
entry: {
    index: './src/js/index.js',
    login: './src/js/login.js'
},
output: {
    path: path.join(__dirname, './dist'),
    filename: '[name].js'
}

以上配置是多页应用。在entry对象中,每个字段都会产生一个chunk,如果将output选项中的filename写死名称,那么系统会报错,因为生成了多个chunk。如上所示配置,最终生成了两个bundle。

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

1 participant