很多前端新手一看到 Webpack 就头疼我也一样。当初我第一次打开同事项目的 webpack.config.js看到一堆 entry、module、plugins、optimization第一反应就是这玩意是用来看的不是用来懂的。直到我自己从 0 到 1 手搭了一个 Webpack 脚手架才明白那些配置其实就那几个固定动作告诉它入口在哪中间怎么加工最后输出到哪。这篇内容不讲花哨技巧就是老老实实把从零搭脚手架的完整过程讲一遍顺便把新手最常见的坑都摆到台面上。适合刚准备学习前端工程化、看官方文档又不知所云的朋友照着操作一遍你会在一个小时内拥有一个可运行的开发环境也终于敢说自己入门了 Webpack。1. 先弄清楚三个问题Webpack在解决什么1.1 从一个简单的HTML页面说起在没有构建工具的时候前端页面通常是这样的一个 index.html里面用 script 标签按顺序引入好几个 js 文件。文件少的时候勉强能维护文件一多问题就出来了全局变量互相污染加载顺序稍微调错就报错线上缓存也不容易控制。我印象最深的一次是在一个老项目里只改了一个工具函数结果浏览器报“某个变量 is not defined”排查半天才发现另一个文件用到这个变量时它所在的 script 还没加载完。Webpack 想解决的就是这件事把零散的模块文件当成有依赖关系的整体通过一个入口文件开始分析把 ES Module、CommonJS 这些模块语法翻译成浏览器真正能运行的代码最后合并输出成少量静态文件。它本质上是一个“打包器”核心流程可以简化成三步从入口出发顺着 import/require 把依赖关系摸清楚然后经过各种 loader 的加工最后按规则输出到指定目录。理解了这个核心流程再回看配置就不会慌。webpack.config.js 里的 entry 是入口output 是出口module.rules 是加工规则plugins 负责打包过程里的额外动作。它们之间是流水线关系先有入口再按规则处理最后输出。没有太多玄学只是配置项比较多需要一步一步来。1.2 新手容易绕晕的模块、loader、plugin我见过不少同学上来就背概念结果越背越乱。这里用大白话给你捋一遍模块你可以把每一个 JS、CSS、图片、字体文件都当作一个模块。Webpack 会通过 import、require、url() 这些写法自动建立它们之间的依赖关系。loader它是一个文件处理器。因为 Webpack 默认只能看懂 JS遇到 CSS、图片、Vue 单文件组件就需要对应的 loader 先把它们转换成 Webpack 能处理的模块。loader 的另一个作用是编译比如用 babel-loader 把 ES6 语法转成兼容老浏览器的 ES5。plugin它不直接处理某个文件而是介入打包流程本身。自动生成 HTML、压缩代码、把 CSS 抽成独立文件、分析打包体积这些是 plugin 的活。我通常用一个生活化类比如果把模块当作食材loader 就是切菜、腌制的环节plugin 则是负责布置后厨、控制出菜节奏的厨师长。切菜是逐个处理食材厨师长则协调整个做菜过程。理解了这一点你就知道什么时候改 loader什么时候加 plugin不会混为一谈。1.3 版本选型直接上Webpack 5不少老教程还在讲 Webpack 4但新项目完全没必要从 4 开始了。Webpack 5 发布到现在已经非常成熟很多新特性比如内置的缓存、模块联邦、更友好的 tree shaking都能省掉大量额外配置。如果安装时看到版本号还是 4.x多半是用了过老的脚手架模板或者依赖锁定了旧版本建议以 5.x 为准。Webpack 5 相比 4 有几个明显变化容易触发“坑”devServer 的配置项从 contentBase 改成了 staticNode.js 的 polyfill 不再自动注入在浏览器里使用某些 Node 核心模块时需要手动安装对应包文件系统缓存机制也比 4 灵活很多。这些差异后面避坑章节再展开现在先记住一个结论入门直接装 Webpack 5不要拿老教程的标准去套新版本。版本选择还涉及 Node.js 环境。Webpack 5 要求 Node.js 至少 10.13.0但建议直接使用 LTS 版本比如 16 或 18 以上。很多 loader 和工具链会慢慢放弃低版本 Node用太旧的系统环境容易遇到安装失败或构建异常。2. 从 0 到 1搭出一个能用的最小脚手架2.1 环境准备Node.js版本检查动手之前先看看本机 Node.js 版本。打开终端输入node -v npm -v如果输出类似 v18.17.1 和 9.6.7说明基本环境没问题。如果 node 命令找不到去官网下载 LTS 安装包一路默认安装即可。安装完 Node.js 会自带 npm一般不需要单独装 npm。为什么要先检查版本因为 Webpack 和很多 loader 会在安装阶段检查环境Node 版本不合适时npm 可能给你装上一堆“不兼容”的警告甚至直接报错。比如旧项目里常见的digital envelope routines::unsupported本质上不是 Node 太新也不是太旧而是新版 Node 的 OpenSSL 3 和 Webpack 4 默认哈希算法对不上。解决方式是升级到 Webpack 5或者临时加--openssl-legacy-provider环境变量。与其靠临时参数续命不如一开始就把环境版本对齐。2.2 初始化项目并安装依赖在一个空目录里执行npm init -y这条命令会生成一个 package.json不需要手动一步步填写。然后安装 Webpack 本体和命令行工具npm install --save-dev webpack webpack-cli注意是--save-dev因为 Webpack 只在开发构建时使用不需要打进生产代码里。webpack 是核心库webpack-cli 则是让你能在终端里敲出 webpack 命令的工具。两个都要装只装一个会出现webpack: command not found或CLI 运行失败的提示。安装完成之后package.json 里会有类似这样的依赖{ devDependencies: { webpack: ^5.88.0, webpack-cli: ^5.1.0 } }先不急着追加其他包先创建一个 src 目录里面放一个入口文件 src/index.js简单写一行代码用于验证console.log(hello webpack);这样一个最小的项目骨架就搭起来了。接下来就是配置 Webpack让它认识这个入口文件。2.3 最简配置文件 entry/output在项目根目录新建 webpack.config.js。Webpack 的配置文件其实就是一个普通的 Node.js 模块它通过 module.exports 导出一个配置对象。最核心的两个字段是 entry 和 outputconst path require(path); module.exports { entry: ./src/index.js, output: { path: path.resolve(__dirname, dist), filename: bundle.js, }, };entry 告诉 Webpack 从哪个文件开始分析依赖。output.path 是打包产物的存放目录必须是绝对路径所以这里用了 Node.js 内置的 path 模块和 __dirname 来拼接。filename 是输出文件的名字可以写死也可以后面用[contenthash]之类的方式动态命名。很多新手会在这里犯一个错误直接把 output.path 写成./dist。这样写的后果是打包产物会被输出到项目根目录的上一级或者直接报错因为 Webpack 要求 output.path 必须是绝对路径。这个坑非常隐蔽我过去也踩过所以请一定记住用path.resolve(__dirname, dist)。配置写完后在终端执行npx webpack默认情况下Webpack 会去读取 webpack.config.js。如果一切正常dist 目录下会生成一个 bundle.js并且终端里会显示打包完成和产物体积。你可以用 Node 执行一下 bundle.js 验证或者在浏览器里加载它控制台会打印出 hello webpack。2.4 用npm scripts统一构建命令项目里如果每次都要敲 npx webpack不仅麻烦而且团队成员对命令的理解成本高。更规范的做法是把它写进 package.json 的 scripts 里scripts: { build: webpack --modeproduction }之后只需要 npm run build 就能完成构建。production 模式会让 Webpack 自动启用代码压缩等生产优化dev 模式则不压缩、构建更快开发时更友好。如果不在命令行传 mode也可以在配置文件的 mode 字段里指定但一般放在 scripts 里更直观。一个脚手架好不好用很大程度由 scripts 写得好不好决定。我建议从一开始就把常用命令统一成三个build 生产构建、dev 开发服务器、后续的打包分析命令。讲到 devServer 和拆分配置时再把它们补全。先养成一个习惯所有构建操作都通过 npm scripts 暴露不把终端里的临时命令当作项目的一部分。3. 开发体验优化devServer、loader、source map一条龙3.1 一键启动开发服务器webpack-dev-server每次修改代码都手动打包、手动刷新浏览器效率太低所以要引入开发服务器。安装依赖npm install --save-dev webpack-dev-server然后把配置补上devServer: { static: ./dist, port: 9000, hot: true, open: true, }同时给 package.json 增加一条 scriptscripts: { dev: webpack serve --modedevelopment }Webpack 5 对应的 webpack-dev-server 4.x 版本里静态资源目录用 static 配置而不是 Webpack 4 时代的 contentBase。如果你看到网上老教程写 contentBase: ./dist在 devServer 4 上会直接报错或者被忽略这就是版本差异导致的坑。启动后终端会显示本地服务地址浏览器也会自动打开。此时你修改 src/index.js 里的内容页面会通过热更新自动刷新不需要手动构建。热更新不等于全量刷新它会在不丢失页面状态的情况下替换模块这对于做表单调试、保留滚动位置的开发体验非常重要。3.2 babel-loader让浏览器看懂你的高级语法现在的项目普遍使用 ES6 语法比如箭头函数、解构、async/await甚至可选链。但老旧浏览器不一定支持这些特性。Babel 的作用就是把高级语法翻译成目标环境能运行的版本。安装三件套npm install --save-dev babel-loader babel/core babel/preset-env然后在 webpack.config.js 里加一条 rulesmodule: { rules: [ { test: /\.js$/, exclude: /node_modules/, use: { loader: babel-loader, options: { presets: [babel/preset-env], }, }, }, ], }test 是一个正则表达式匹配哪些文件需要这个 loaderexclude 排除 node_modules避免把第三方库也做一遍 Babel 编译拖慢构建use 里指定具体用的 loader 和参数。这里要注意presets 是 Babel 的预设babel/preset-env会按照你设定的浏览器目标自动决定转译规则而不是无脑把每一个语法都转成 ES5。关于浏览器目标推荐写一个 .browserslistrc 文件或在 package.json 里配置 browserslistdefaults这样 preset-env 才会根据你的目标环境智能地注入 polyfill 和语法转换。如果完全不配置它会使用默认目标可能导致打包产物偏大或者某些新 API 没有被转换运行时报错。这个细节很容易被忽略但它直接影响产物体积和兼容性新手务必在这个环节停一下。3.3 css-loader / style-loader处理样式文件在 Webpack 世界里CSS 文件也可以被 import 进 JS。要做到这一点离不开两个 loadercss-loader负责解析 CSS 文件里的 import、url() 等语法把它们转换成 Webpack 认识的模块。style-loader负责把 CSS 字符串动态地注入到页面的style标签里。安装npm install --save-dev style-loader css-loader在 rules 里追加{ test: /\.css$/, use: [style-loader, css-loader], }然后创建一个 src/style.css并在 index.js 里import ./style.css;启动 devServer 后就能看到样式生效。很多新手第一次写到这里会懵为什么数组顺序是 style-loader 在前、css-loader 在后Webpack 处理 loader 的顺序是从右往左的也就是说 css-loader 先执行它把 CSS 解析成模块解析完的结果再交给左边的 style-loader由它把内容注入到页面。如果顺序反了css-loader 会拿到一段 JS 代码而不是 CSS直接抛错。这个“从右到左”的规则是 Webpack loader 最基础也最容易被忽视的知识点后面处理 sass、postcss 时同样适用。如果你还用到 SCSS 预处理器还需要装 sass-loader 和 sass并把它加到最右边{ test: /\.scss$/, use: [style-loader, css-loader, sass-loader] }顺序依旧是从右到左sass 先编译成 csscss-loader 再解析style-loader 最后注入。3.4 图片字体等静态资源用asset modulesWebpack 5 之前处理图片、字体常用 file-loader 或 url-loader现在则推荐直接使用 asset modules不需要额外安装一堆 loader。最基本的方式{ test: /\.(png|jpe?g|gif|svg|woff2?|eot|ttf)$/, type: asset/resource, }asset/resource的作用等同于老版本的 file-loader把文件拷贝到输出目录同时生成对应的访问 URL。如果你希望小图片内联成 base64减少请求数可以把 type 改成asset并设置体积阈值{ test: /\.(png|jpe?g|gif|svg)$/, type: asset, parser: { dataUrlCondition: { maxSize: 8 * 1024, }, }, }小于 8KB 的图片会自动转成 base64 内联超过 8KB 的图片则输出成单独文件。这个方案兼顾了小图片的加载速度和体积控制比当年的 url-loader 配置更简洁。输出文件名最好也带上指纹output: { assetModuleFilename: assets/[name].[hash:8][ext], }这样静态资源更新后URL 也会跟着变化避免浏览器缓存住旧图。3.5 devtool和Source Map报错不再难定位代码经过 Babel 转译和打包之后浏览器控制台报错的位置通常指向压缩后的 bundle.js一行几百个字符根本没办法排查。Source Map 的作用就是建立“编译产物”和“源码”之间的映射关系让你在 DevTools 里看到的报错位置直接对应到 src 里的原始文件。配置项是 devtool。开发环境推荐用devtool: eval-cheap-module-source-map,生产环境我一般建议关闭或者使用nosources-source-map避免把完整源码暴露到线上同时又保留了行列号信息供监控平台使用。devtool 并不是越多越好每种取值在构建速度和源码还原度之间有取舍。新手不需要背全所有选项记住开发用 eval 系列生产用 source-map 系列或直接 false 就够用。有个经常被忽略的小坑如果只配置了 devtool但浏览器开发者工具里的 Source 面板找不到源码先检查一下是不是 devServer 把缓存开得太激进。多数情况下只要 devtool 配置正确刷新页面后源码就会出现在 Sources 面板里。如果你用 Vue 或 React 的调试工具Source Map 也是它们能正确展示组件名的前提。4. 生产构建打磨插件、分包与缓存4.1 HtmlWebpackPlugin自动生成入口HTML手动创建 index.html 然后手写引用 bundle.js 的 script 标签在开发阶段能跑但进入生产构建就麻烦了文件名只要换成 hashHTML 就得手动改。解决这个问题的是 HtmlWebpackPlugin。安装npm install --save-dev html-webpack-plugin配置const HtmlWebpackPlugin require(html-webpack-plugin); module.exports { plugins: [ new HtmlWebpackPlugin({ template: ./public/index.html, filename: index.html, }), ], };在项目里创建 public/index.html作为模板!DOCTYPE html html langzh-CN head meta charsetUTF-8 titleWebpack Demo/title /head body div idapp/div /body /html打包后HtmlWebpackPlugin 会自动把当前构建产生的 JS 和 CSS 文件用正确的路径插入到这个 HTML 模板里。就算你后面把输出文件改成带 hash 的名字HTML 里的引用也会跟着更新再也不用担心改文件名导致页面白屏。关于页面里的静态资源引用我建议模板里不要写死任何带版本号的静态路径全部交给打包工具去注入。尤其是碰上 CDN 部署、子路径部署这些需求时模板越干净后面调整越方便。4.2 抽离CSSMiniCssExtractPlugin开发环境用 style-loader 把 CSS 通过 JS 注入很方便但生产环境这样做有问题页面要先等 JS 执行完才能看到样式容易白屏而且 CSS 混在 JS 里没法利用浏览器对 CSS 的并行加载能力。所以生产构建要用 MiniCssExtractPlugin 把 CSS 抽成独立文件。安装npm install --save-dev mini-css-extract-plugin配置时注意 loader 部分跟开发环境不一样const MiniCssExtractPlugin require(mini-css-extract-plugin); module.exports { module: { rules: [ { test: /\.css$/, use: [MiniCssExtractPlugin.loader, css-loader], }, ], }, plugins: [ new MiniCssExtractPlugin({ filename: css/[name].[contenthash:8].css, }), ], };这个插件会把 css-loader 处理后的样式写到独立的 css 文件里而不是塞进 JS。要注意MiniCssExtractPlugin 的 loader 不能和 style-loader 同时使用二者职责冲突一个要注入 style 标签一个要写文件。通常做法是开发用 style-loader生产用 MiniCssExtractPlugin.loader再配合环境配置来切换。文件名里的内容 hash 很重要。如果只写死文件名浏览器会认为这是同一个 CSS 文件导致修改样式后用户端一直加载旧缓存。使用[contenthash:8]后内容一变文件名就变浏览器自然会重新拉取这是前端缓存控制最基础也最有效的一招。4.3 文件名hash和持久化缓存很多团队把 dist 目录直接扔到服务器上浏览器会缓存 JS、CSS 文件。为了让用户尽快看到新版本打包后的文件名一般会带上哈希。Webpack 5 里常见的占位符有[hash]一次构建的全局哈希只要任何一个文件变化所有文件名都会变。[chunkhash]按 chunk 维度计算每个模块块有自己独立的哈希。[contenthash]按文件内容计算内容没变哈希就不变最适合做长缓存。我个人的做法是JS 文件用[name].[contenthash:8].jsCSS 文件也用[contenthash:8]图片、字体这类资源用[hash:8]就够了。这样能最大限度利用浏览器缓存公共依赖单独打包成一个 chunk业务代码更新时公共依赖文件不动用户不用重复下载整个应用。Webpack 5 还提供了文件系统级缓存cache: { type: filesystem, },显式开启后多次构建之间可以复用未变化的模块编译结果显著加快重复构建速度。需要注意缓存目录默认在 node_modules/.cache如果你在 CI 环境里打包可以考虑把缓存目录做持久化否则每次构建都从头开始。也有人会遇到“改了代码不生效”的情况多数时候不是缓存导致而是 loader 的 include/exclude 范围太宽把不该缓存的动态文件也算了进去后面排查章节会细说。4.4 splitChunks公共依赖分包项目只要上了脚手架大概率会引入框架类库这些第三方依赖很少变化但体积很大。如果全部打包进业务 bundle用户每次更新业务代码都要重新下载完整文件浪费流量也影响首屏速度。Webpack 4 开始内置了 splitChunks它可以把公共依赖自动抽成独立 chunk。示例配置optimization: { splitChunks: { chunks: all, cacheGroups: { vendor: { test: /[\\/]node_modules[\\/]/, name: vendors, priority: 10, }, }, }, },先把 chunks 设为all让异步加载的模块和初始模块都能参与分包再用 cacheGroups 把 node_modules 里的依赖集中到 vendors 文件里。配合上一条的内容 hash只要这些依赖版本不变vendors 文件名就一直不变浏览器会一直用缓存业务代码更新时只下载业务 bundle。分包并不是越细越好。拆得太碎会产生大量小请求HTTP 连接开销反而拖慢页面拆得太粗又达不到缓存优化目的。我的经验是先按 node_modules 归一个 vendor 包再把页面级公共模块放到一组遇到体积超过 100KB 的单个依赖再单独拆不要一开始就追求极致的代码分割。5. 避坑实录版本、路径、顺序和缓存5.1 版本冲突先统一版本再谈配置新手遇到的大部分 Webpack 报错根源不是配置写错而是版本不匹配。最常见的情况是照着教程装了一堆包教程是旧版你装的是新版配置项名称不一样于是出现configuration has an unknown option contentBase这类报错。我整理了一个简单的版本对照表适合 Webpack 5 项目依赖建议版本常见注意事项webpack5.x核心库不与 webpack 4 混用webpack-cli5.x提供 webpack 命令webpack-dev-server4.x使用 static 配置不用 contentBasebabel-loader9.x需要 babel/core 7 配合html-webpack-plugin5.x适配 webpack 5mini-css-extract-plugin2.x适配 webpack 5如果项目里同时出现 Webpack 4 的插件和 Webpack 5 的配置优先怀疑版本兼容。安装任何 loader 或 plugin 之前先到它主页看 peerDependencies确认它声明支持的 webpack 版本范围。我已经养成一个习惯新项目装依赖前先统一版本矩阵避免踩无谓的坑。5.2 publicPath和相对路径的陷阱打包产物部署到服务器时静态资源路径如果写错页面能打开但样式、图片全部 404。路径问题通常会指向 output.publicPath。默认情况下 publicPath 为空字符串资源引用是相对路径。如果你的应用部署在域名根目录这没问题但如果部署在某个子路径比如https://example.com/admin/相对路径就可能把资源引到错误位置。此时需要设置output: { publicPath: /admin/, }或者配合 CDNoutput: { publicPath: https://cdn.example.com/assets/, }publicPath 影响的是运行时加载 chunk、图片、字体等资源时拼接的基础路径。新手很容易把它和 output.path 混淆。output.path 是本地输出目录决定文件写到磁盘的哪个位置publicPath 是线上 URL 前缀决定运行时从哪里加载这些文件。一个是“写”一个是“读”别搞反。如果在 devServer 里遇到图片能加载、但访问子路径时资源 404通常就是 devServer 的 static 配置和 publicPath 没配合好。开发环境的 publicPath 可以设成/生产环境再按部署位置调整配置上区分环境会更省心。5.3 loader执行顺序从右到左之前提过Webpack loader 的 use 数组是从右往左执行的。这一点我再强调一次因为实际项目里“顺序不对”带来的报错很迷惑。比如处理 SCSS 时正确顺序是use: [style-loader, css-loader, sass-loader]执行流程是 sass-loader 先跑把 SCSS 编译成 CSScss-loader 再处理 CSS 里的 url、importstyle-loader 最后注入页面。如果把顺序写成[sass-loader, css-loader, style-loader]Webpack 会拿 style-loader 的输出去喂给 css-loader得到的往往是You may need an appropriate loader这样让人一头雾水的提示。还有一类坑和 test 正则有关。如果想同时处理 css 和 scss可以写{ test: /\.(css|scss)$/, use: [style-loader, css-loader, sass-loader], }但这样可能有隐患某些纯 CSS 文件也会被 sass-loader 处理只要 sass 编译严格模式打开遇到奇怪的空分支就会报错。更稳妥的方式是把 css 和 scss 拆成两条 rule各管各的规则之间不要互相干扰。遇到报错时先看 loader 顺序再看匹配范围这是排查规则类问题的基本思路。5.4 开发模式不更新缓存和HMR排查“我改了代码页面怎么没变”这是我被问过最多的问题之一。通常有这么几个层次浏览器缓存。devServer 下如果开启了强缓存可能把旧的 bundle 缓存住了。无痕窗口看一次或者检查 devServer 的 headers 里有没有设置 Cache-Control。webpack-dev-server 的 hot 没真正开启。配置了 hot: true 还不够还要保证项目里没有插件把热更新关掉。如果你用了 React Fast Refresh 或 Vue HMR 的插件它们的配置优先级会高于 devServer.hot。配置了固定的输出文件名且 devServer 的 static 目录包含旧文件。有时需要手动删除 dist 目录再刷新。cache.type 为 filesystem 时个别 loader 的缓存 key 不稳定导致内容变化没有被识别。可以先把 cache 关掉验证确定“改了没错”之后再逐步把缓存打开。我自己的排查习惯是先看 devServer 终端有没有重新编译的日志再看浏览器 Network 面板里 bundle 请求是否真的返回了新内容最后才怀疑配置问题。按这个顺序走至少能排除 80% 的“不生效”问题。5.5 新手常见报错速查表把这段时间里最容易被问到的几类报错整理成一张表方便直接对照报错信息常见原因解决办法Module not found: Error: Cant resolve入口或 import 路径写错、文件不存在、缺少扩展名检查路径配置 resolve.extensionsLoader not found: style-loader依赖没安装执行 npm install --save-dev style-loaderYou may need an appropriate loader文件类型没有被 rules 覆盖或 loader 顺序不对检查 test 正则和 use 数组顺序configuration has an unknown option contentBasedevServer 版本过新配置项已改名改成 staticConflict: Multiple assets emit to the same filename多个入口或插件输出同名文件修改 filename 模板增加 chunk hashdigital envelope routines::unsupported新版 Node 与 Webpack 4 默认哈希不兼容升级 Webpack 5或临时加 openssl-legacy-providerasset size limit 警告单个包体积超过建议阈值使用 splitChunks 分包或做代码分割这张表不是让你背而是提供一个排错起点。大部分报错信息本身就是线索先根据“什么操作触发报错”缩小范围再针对性地查配置和依赖版本。千万不要一遇到报错就删依赖重装那样往往浪费时间还会引入新的不稳定因素。6. 脚手架进阶配置分离和常用扩展6.1 按环境拆分webpack配置项目变大后把开发和生产配置写在一个文件里会让 entry、module、plugins 变得越来越臃肿。常见的做法是把配置拆成三个文件webpack.common.js、webpack.dev.js、webpack.prod.js再用 webpack-merge 合并。先安装npm install --save-dev webpack-merge一个简单的拆分思路是webpack.common.js 放所有环境都会用到的入口、输出、loader 规则、别名、公共插件。webpack.dev.js 放 devServer、热更新、更快的 source map、style-loader。webpack.prod.js 放生产模式的插件、CSS 抽离、文件指纹、splitChunks。合并后的 webpack.dev.js 大致长这样const { merge } require(webpack-merge); const common require(./webpack.common.js); module.exports merge(common, { mode: development, devtool: eval-cheap-module-source-map, devServer: { static: ./dist, port: 9000, hot: true, }, });这样做最大的好处是职责清晰。开发环境想改端口只动 dev 文件生产环境想加 CDN 路径只动 prod 文件不会误伤到公共逻辑。懒一点的做法也有比如在 config 里用环境变量判断const isProduction process.env.NODE_ENV production;但当配置项多起来之后if/else 嵌套看着就难受。我更推荐从一开始就用 merge 拆文件虽然多两个文件但后续维护成本会低很多。6.2 resolve.alias和extensions把路径写短项目里模块路径一深import 会变成这样import utils from ../../../../src/utils/format;看的人懵改起来也容易错。Webpack 配置中的 resolve.alias 能帮你把常用目录映射成短别名resolve: { alias: { : path.resolve(__dirname, src), }, },之后就可以写import utils from /utils/format;你还可以设置 resolve.extensions让 import 时可以省略扩展名resolve: { extensions: [.js, .json, .vue], },这样 Webpack 在解析 ./utils 时会自动尝试 .js、.json、.vue。注意 extensions 数组的顺序代表优先尝试的顺序如果你有两个同名文件 utils.js 和 utils.vue写 import utils from ./utils 时会优先匹配数组里靠前的 .js。这个细节在混合 Vue 和 JS 的项目里尤其容易产生“我 import 的是组件怎么跑到 JS 文件去了”的困惑。alias 和 extensions 都属于“开发体验增强”不会改变业务逻辑但能明显减少输入错误。给新项目配置 alias 时建议统一用一个前缀比如 表示 src避免团队成员各自发明别名维护起来更省心。6.3 从脚手架到工程化工作流手搭 Webpack 脚手架的意义不只是得到一个能用的模板而是理解前端工程化的一条主线。当你自己配过一轮 entry、loader、plugin 之后再去看任何工程化框架比如各种 CLI 生成的模板都会更容易接受它们无非是把我们手动做的事情自动化了再把某些最佳实践固化成默认值。基于我自己的经验建议在脚手架稳定之后再补充这几样东西代码规范在 package.json 里加 lint 脚本跟构建分开。环境变量注入用 DefinePlugin 或者环境判断把 API 地址等配置按环境区分。体积分析装一个打包分析插件隔一段时间看一眼 bundle 组成发现问题。CI 集成把 build 脚本接到自动构建流程里让每次提交都跑一遍构建检查。这些扩展不会改变你手写 Webpack 的底层心智模型反而会加强它。说到底脚手架是手段工程化能力才是目的。当你能根据自己的项目情况加减配置而不是只会复制粘贴的时候Webpack 入门这件事才算真正完成。我个人每次接新项目也会先回看这套配置而不是盲目找模板因为只有自己搭过一遍才知道哪块配置在解决什么问题该不该留着。
阅读完成 · 觉得有帮助?