本节描述了 webpack 的内部机制,对插件开发者很有用。
打包是一个函数,它接收一些文件并输出另一些文件。
但在输入和输出之间,它还有模块(modules)、入口点(entry points)、代码块(chunks)、代码块组(chunk groups)以及许多其他中间部分。
项目中使用的每个文件都是一个模块(Module)
./index.js
import app from './app.js';
./app.js
export default 'the app';
通过相互引用,模块会形成一个图(ModuleGraph)。
在打包过程中,模块被组合成代码块。代码块再组合成代码块组,并通过模块相互连接形成一个图(ChunkGraph)。当你描述一个入口点时,在底层机制上,你创建了一个包含一个代码块的代码块组。
./webpack.config.js
module.exports = {
entry: './index.js',
};
创建了一个名为 main 的代码块组(main 是入口点的默认名称)。这个代码块组包含 ./index.js 模块。当解析器处理 ./index.js 内部的导入时,新的模块会被添加到这个代码块中。
另一个例子
./webpack.config.js
module.exports = {
entry: {
home: './home.js',
about: './about.js',
},
};
创建了两个名为 home 和 about 的代码块组。每个代码块组都含有一个代码块和一个模块 —— home 对应 ./home.js,about 对应 ./about.js。
一个代码块组中可能有多个代码块。例如 SplitChunksPlugin 会将一个代码块拆分成一个或多个代码块。
代码块有两种形式
initial 是入口点的主代码块。此代码块包含你为入口点指定的所有模块及其依赖。non-initial 是一个可能被延迟加载的代码块。它在使用动态导入(dynamic import)或SplitChunksPlugin时可能会出现。每个代码块都有一个对应的资源(asset)。资源就是输出文件 —— 打包的结果。
webpack.config.js
module.exports = {
entry: './src/index.jsx',
};
./src/index.jsx
import React from 'react';
import ReactDOM from 'react-dom';
import('./app.jsx').then((App) => {
ReactDOM.render(<App />, root);
});
创建了名为 main 的 initial 代码块。它包含
./src/index.jsxreactreact-dom及其所有依赖,除了 ./app.jsx。
由于 ./app.jsx 模块是动态导入的,因此为其创建了 non-initial 代码块。
输出
/dist/main.js - 一个 initial 代码块/dist/394.js - non-initial 代码块默认情况下,non-initial 代码块没有名称,因此会使用唯一 ID 而不是名称。在使用动态导入时,我们可以通过使用“魔法注释”("magic" comment)来显式指定代码块名称
import(
/* webpackChunkName: "app" */
'./app.jsx'
).then((App) => {
ReactDOM.render(<App />, root);
});
输出
/dist/main.js - 一个 initial 代码块/dist/app.js - non-initial 代码块输出文件的名称受配置中两个字段的影响
output.filename - 用于 initial 代码块文件output.chunkFilename - 用于 non-initial 代码块文件output.filename。这些字段中可以使用一些占位符。最常用的是
[id] - 代码块 ID(例如 [id].js -> 485.js)[name] - 代码块名称(例如 [name].js -> app.js)。如果代码块没有名称,则会使用其 ID。[contenthash] - 输出文件内容的 md4 哈希值(例如 [contenthash].js -> 4ea6ff1de66c537eb9b2.js)