3、核心配置之Entry:单入口与多入口配置、动态入口、entry的context选项、依赖图与chunk概念

好,咱们今天来聊聊 Webpack 的入口配置。说真的,Entry 是整个打包流程的起点,你告诉 Webpack 从哪个文件开始干活,它就顺着依赖关系一路找下去。我见过不少新手,上来就复制粘贴配置,结果项目一复杂就崩了。嗯,咱们今天把这几个核心概念彻底讲透。

3.1 单入口配置:最基础的玩法

单入口,说白了就是一个项目只有一个 HTML 页面,所有 JS 都从 index.js 开始。我个人习惯用这种配置做小型项目或者后台管理系统。

// webpack.config.js
module.exports = {
  entry: './src/index.js'
};

这种写法最简洁。Webpack 会自动把 index.js 以及它引用的所有模块打包成一个 bundle.js。我在项目中遇到过,有人把入口写成 entry: ['./src/index.js', './src/utils.js'],以为这样能打两个包。其实不是,这样写是把多个文件合并成一个入口,最终还是一个 bundle。

小提示:单入口配置时,输出文件名通常用 [name].js,默认的 name 就是 main。如果你想要自定义,可以写成 entry: { main: './src/index.js' }

3.2 多入口配置:多页面应用的标配

多入口就更有意思了。你想想看,一个电商网站,首页、商品详情页、购物车,每个页面都有自己的 JS 逻辑。如果全塞到一个 bundle 里,首屏加载会慢得离谱。这时候就需要多入口。

module.exports = {
  entry: {
    home: './src/home.js',
    product: './src/product.js',
    cart: './src/cart.js'
  }
};

每个入口都会生成一个独立的 chunk。我记得有一次做重构,老项目有 20 多个页面,我全部改成了多入口配置。配合 HtmlWebpackPlugin,每个页面只加载自己的 JS,性能提升非常明显。

注意:多入口配置时,output 的 filename 必须用占位符,比如 [name].bundle.js,否则会报错。因为 Webpack 不知道多个入口该用哪个名字输出。

3.3 动态入口:灵活应对复杂场景

动态入口,这个是我觉得最酷的功能。有时候入口文件不是写死的,而是根据环境变量或者文件系统动态生成的。比如,你有一个插件系统,每个插件都有自己的入口。

// 动态入口示例
const fs = require('fs');
const path = require('path');

function getEntries() {
  const entries = {};
  const pages = fs.readdirSync('./src/pages');
  pages.forEach(page => {
    const entryPath = path.join('./src/pages', page, 'index.js');
    if (fs.existsSync(entryPath)) {
      entries[page] = entryPath;
    }
  });
  return entries;
}

module.exports = {
  entry: getEntries()
};

我曾经在一个 CMS 项目里用过这个技巧。每次新增一个页面模块,只需要在 pages 目录下新建文件夹,Webpack 自动识别并生成对应的入口。省去了手动修改配置的麻烦。

核心要点:动态入口的本质是「用代码生成配置」。你可以用任何 Node.js 能力——读文件、调 API、解析命令行参数——来动态决定入口。

3.4 entry 的 context 选项:路径的根目录

context 这个选项,说实话很多人容易忽略。它决定了 Webpack 解析入口文件时的根目录。默认是 process.cwd(),也就是你运行 webpack 命令的目录。

module.exports = {
  context: path.resolve(__dirname, 'src'),
  entry: './index.js'  // 实际指向 src/index.js
};

为什么要用 context?我遇到过最坑的情况:团队里有人用 Windows,有人用 Mac,路径分隔符不一样。设置 context 后,所有入口路径都基于这个根目录,跨平台兼容性会好很多。

避坑指南:我曾经因为没设置 context,导致 CI 环境打包失败。因为 CI 的工作目录和本地开发不一样。后来统一设置 context: __dirname,问题就解决了。

3.5 依赖图与 chunk 概念:Webpack 的核心思维

依赖图,这是 Webpack 最核心的概念。你想想看,Webpack 从入口开始,遇到 importrequire 就继续往下找,最终形成一张巨大的依赖关系网。这张网就是依赖图。

而 chunk,就是依赖图被切割后的产物。每个入口对应一个 chunk,但 chunk 不一定是入口。比如,你用 SplitChunksPlugin 抽离公共代码,那些公共模块也会形成独立的 chunk。

// 依赖图示例
// index.js
import { format } from './utils';
import './style.css';

// utils.js
import { add } from 'lodash';
export function format() { ... }

// 依赖图:
// index.js → utils.js → lodash
//          → style.css

我刚开始学 Webpack 时,一直搞不清 chunk 和 bundle 的区别。后来才明白:chunk 是 Webpack 内部的概念,是代码分割后的逻辑单元;bundle 是最终输出的文件。一个 chunk 可能对应一个 bundle,也可能被拆成多个(比如异步加载)。

记住这张表:
概念 说明 例子
Entry 打包的起点 ./src/index.js
Module 每个被引用的文件 utils.js, lodash
Chunk 代码分割后的代码块 main.chunk.js, vendors.chunk.js
Bundle 最终输出的文件 main.bundle.js

为什么会这样设计?说白了,就是为了性能。如果所有代码打成一个包,首屏加载会慢。通过 chunk 拆分,你可以实现按需加载、并行加载、缓存复用。我建议你在做大型项目时,一定要理解 chunk 的概念,否则优化无从下手。

警告:不要试图手动控制 chunk 的生成。Webpack 的 chunk 算法非常复杂,你只需要配置好入口和 splitChunks,剩下的交给 Webpack。我曾经手写 chunk 分割逻辑,结果维护成本极高,最后全删了改用默认配置。

嗯,关于 Entry 的核心配置,今天就聊这么多。记住一句话:入口决定了起点,依赖图决定了路径,chunk 决定了输出。下一章咱们聊聊 Output,看看打包出来的文件怎么命名、怎么存放。