Skip to content

SourceMap与开发服务自动化(Webpack 3/3) ​

本页对应 课堂视频,共 5 段视频,约 1 小时 10 分。

学习目标 ​

  1. 能够为 HTML 中引用的图片、音频、视频配置 html-loader,并通过构建结果验证资源被正确打包、路径被正确替换,且能说明其与 public 目录直接引用及 asset 输出规则的关系。
  2. 能够安装并配置 ESLint Webpack 插件,创建 .eslintrc.js,设置检查范围、继承规则、运行环境与全局变量,并依据构建报错定位规则、调整规则使检查按预期通过或告警。
  3. 能够配置 babel-loader 与 @babel/preset-env,排除 node_modules,将 ES6+ 语法转译为 ES5,并通过构建产物验证箭头函数、let/const 等语法已被转换。
  4. 能够按需引入 polyfill 并在入口文件中生效,验证旧环境缺失的 API(如 Promise)被补充,并能区分 Babel 语法转译与 polyfill API 兼容的职责。
  5. 能够配置 devtool 生成 Source Map,通过浏览器控制台报错验证错误位置可映射回源码,并能说明行映射与行列映射在开发、生产场景中的取舍。
  6. 能够区分开发模式与生产模式的配置文件,并验证生产模式下 CSS、JS、HTML 资源按生产要求完成处理与输出。

知识点 ​

SourceMap 要解决什么问题 ​

为什么需要:开发时我们写的是 src/app.js,但浏览器最终执行的是打包后的 dist/main.js。一旦运行时报错,控制台给出的行号往往是打包后文件的行号,而不是你源码中的行号;如果代码再被压缩,可能只剩一行,定位错误会非常困难。

怎么想:SourceMap(源代码映射)就是生成一个 xxx.map 文件,里面记录“构建后代码的某一行、某一列”对应“源代码的哪一行、哪一列”。浏览器发现这个映射文件后,就能把报错位置还原到源码位置。

怎么做:在 webpack.config.js 中配置 devtool。笔记中的配置如下:

js
module.exports = {
 	devtool:'cheap-module-source-map',// 只有行映射,打包编译速度快。
 	mode: 'development'
}

详细配置官网地址:https://www.webpackjs.com/configuration/devtool/

易错点:devtool 要和 mode 同级,不要写到 module.rules 里面;属性名是 devtool,不要写成 devTool。

用调试过程理解映射作用 ​

为什么需要:只看概念容易觉得 SourceMap 可有可无,实际排错时差别很明显。

怎么想:转写稿中演示了一个运行时错误:代码里声明了 userName,后面却把 userName 当函数调用,浏览器会报类似“userName is not a function”。这类错误 ESLint 检查不出来,因为 ESLint 主要检查代码规范和表面问题,运行时错误必须等代码运行起来才暴露。

怎么做:先不加 SourceMap 打包运行,控制台提示的是打包后文件中的行号,例如指向 app.js 的第 25 行;但源码里真正出错的位置并不是那一行。加上 devtool 后重新打包,会生成 .map 文件,再次运行,报错就能定位回源码中的第 17 行,排查效率明显提高。

易错点:不要误以为 ESLint 能替代运行时调试;SourceMap 解决的是“构建后位置 → 源码位置”的映射问题,不是语法检查工具。

devtool 的可选值怎么选 ​

为什么需要:不同场景对打包速度和定位精度的要求不同,不能一律配置同一个值。

怎么想:映射越细,生成信息越多,打包编译越慢;开发阶段频繁打包,更看重速度;生产阶段通常只在发布时打包,等待成本相对低,可以考虑更完整的映射。

怎么做:笔记给出两个典型值:

  • cheap-module-source-map:只有行映射,打包编译速度快,适合开发模式。
  • source-map:包含行和列的映射,打包编译速度慢,适合生产模式。

易错点:不要机械理解“开发就必须行列全映射”。转写稿也强调,排错时多数情况知道第几行就够用了;开发过程频繁编译,选 cheap-module-source-map 更合适。生产模式是否使用 source-map,还要结合项目实际要求决定。

资源自动化的总思路 ​

本节把 HTML 中的图片、音视频,以及 JS 规范检查、转译、兼容和调试串起来。核心不是“把文件随便放进项目”,而是让 webpack 在打包时自动处理资源,并让 dist 中的 HTML 引用正确路径。

为什么需要: HTML 里写 ./xxx.png、./videos/a.mp4 时,浏览器最终执行的是 dist 里的页面;如果资源没被复制或改写路径,就会 404。
怎么想: 先区分 public 和 src。public 是直出目录,不经过 JS 构建;src 里的资源才适合参与打包。HTML 本身由插件处理,但 HTML 内部引用的资源要单独加 html-loader。
怎么做: 把图片、音视频放入 src,在 webpack.config.js 的 module.rules 中配置:

js
{
  test: /\.html$/,
  use: 'html-loader'
}

然后运行 npx webpack,观察 dist 中 HTML 的路径是否被改写。
易错点: 不要以为 html-loader 是“打包 HTML 文件本身”;它处理的是 HTML 内部使用的资源。放在 public 里的资源不会自动参与构建,路径也不能想当然。

处理 HTML 中的图片、音频和视频 ​

为什么需要: 页面里不只有 JS/CSS,图片、音频、视频也要进入构建结果。
怎么想: 图片一般会受已有资源规则影响,例如小于阈值时转 base64;音视频通常只做文件输出,不应 base64。转写中的调试过程是:先打包,发现 dist 里 HTML 仍按原路径找图,图片、音视频找不到;加 html-loader 后图片按规则输出,音视频默认落在 dist 根目录。
怎么做: 给音视频补规则,覆盖 mp3、mp4、webm、ogg 等扩展名,输出到 medias 目录,并保留 12 位哈希,例如按课堂配置写成资源输出规则。
易错点: 只配 html-loader 不等于音视频会自动进合适目录;音视频不要套用图片的 base64 逻辑;扩展名要写全,否则新格式仍会走默认输出。

用 ESLint 检查代码规范 ​

为什么需要: 团队协作需要统一风格,也要尽早发现未声明变量、错误写法。
怎么想: ESLint 是检查器,webpack 插件负责把检查接入打包流程。只检查 src,不要检查 node_modules,否则里面历史代码会让检查永远不通过。
怎么做: 安装 ESLint 与 webpack 插件,在配置中引入插件实例,并指定检查根目录;项目根目录创建 .eslintrc.js:

js
module.exports = {
  extends: 'eslint:recommended',
  parserOptions: {
    ecmaVersion: 2023,
    sourceType: 'module'
  },
  env: {
    browser: true,
    node: true
  },
  globals: {
    $: 'readonly'
  },
  rules: {
    'no-console': 0
  }
}

转写中的调试很典型:先报“找不到 ESLint 配置文件”,原因是文件名写错;再报 import 解析错误,需允许 ES6 模块;再报 console is not defined,对应规则 no-undef。规则值 0 忽略、1 警告、2 错误。
易错点: 规则记不全很正常,看到报错后去 ESLint 官方“规则参考”搜规则名;不要盲目继承后又大量覆盖,团队规范要统一。

用 Babel 转译新语法 ​

为什么需要: 旧浏览器不认识 const、箭头函数、类等新语法。
怎么想: Babel 负责“语法转换”,把 ES6+ 转成 ES5;它不负责补 Promise 这类新 API。
怎么做: 配置 babel-loader,只处理 JS,排除 node_modules,并使用预设工具包:

js
{
  test: /\.js$/,
  exclude: /node_modules/,
  use: {
    loader: 'babel-loader',
    options: {
      presets: ['babel-preset-env']
    }
  }
}

转写中通过搜索 const、下划线语法验证:打包前能看到新语法,打包后被转成 ES5 写法。
易错点: 不排除 node_modules 会编译第三方旧代码;不要把 Babel 当成 polyfill。

Polyfill 与 sourceMap ​

为什么需要: Promise 等 API 无法靠语法转换变成旧浏览器能力,需要 polyfill;打包后代码行号错位,需要 source map 定位源码。
怎么想: polyfill 按项目兼容性要求按需引入,通常放入口文件,属于生产依赖;source map 记录“目标文件哪段对应原文件哪段”。
怎么做: 在入口按课堂安装的 polyfill 包引入;webpack 配置 devtool,包含行列映射更精确但慢,只映射行更快,适合频繁开发构建。转写中报错显示第 25 行,加 map 后能定位到真正的第 17 行。
易错点: polyfill 会增加体积,不是所有项目都必须;source map 要区分开发与生产取舍,不能只看“报错行”就改代码。

为什么要把配置拆成开发和生产两份 ​

为什么需要: 开发时我们频繁启动、改代码、重新打包,最在意的是速度快、调试方便;生产时只在上线前打包一次,更在意产物是否正确、路径是否合适、是否需要优化。转写稿里也反复提到“开发过程中频繁编译打包,生产发布只打包一次”,这正是拆分配置的现实动机。
怎么想: 不要把“开发时想要的配置”和“上线时想要的配置”硬塞在同一个 webpack.config.js 里,而是拆成两份,用命令决定加载哪一份。
怎么做: 新建 config 目录,把原来的 webpack.config.js 复制两份,分别命名为开发配置和生产配置。
易错点: 不要只改文件名却忘记改 mode;也不要以为复制完就自动生效,后面还必须通过 --config 指定。

创建 config 目录并复制配置文件 ​

为什么需要: 配置文件集中放在 config 目录,项目根目录更干净,也方便区分开发和生产。
怎么想: 原文件先保留不动,复制出两份作为“分叉”:一份给开发,一份给生产。
怎么做: 创建 config 文件夹,将 webpack.config.js 复制为:

  • ./config/webpack.dev.js
  • ./config/webpack.prod.js

易错点: 路径名、文件名大小写要一致;config 目录位置要和命令里的 ./config/... 对得上。

开发配置:mode 设为 development ​

为什么需要: 开发模式要让 webpack 知道当前不是生产构建,便于采用开发阶段的行为。
怎么想: 开发配置只负责本地开发启动,所以 mode 明确写 development。
怎么做: 打开 ./config/webpack.dev.js,把 mode 设置为:

js
mode: 'development'

易错点: 不要写成 dev、develop;webpack 的合法值常见是 development 和 production。

生产配置:mode 设为 production ​

为什么需要: 生产模式用于上线构建,必须和开发模式区分开。
怎么想: 笔记里写的是 producation,这是拼写错误,应纠正为 production。
怎么做: 打开 ./config/webpack.prod.js,设置:

js
mode: 'production'

易错点: production 容易拼错;如果拼错,webpack 会提示配置不合法。不要把生产配置继续写成 development,否则两份配置就没有本质区别。

在 package.json 中添加执行命令 ​

为什么需要: 有了两份配置后,必须让命令明确告诉 webpack 加载哪一份,否则它仍可能找默认配置。
怎么想: 开发用 webpack serve 启动开发服务器;生产用 webpack 做一次构建。两者都通过 --config 指定文件。
怎么做: 在 package.json 的 scripts 中写:

json
"scripts": {
  "start": "npx webpack serve --config ./config/webpack.dev.js",
  "build": "npx webpack --config ./config/webpack.prod.js"
}

注意原笔记里的 pageage.json 应纠正为 package.json。
易错点: scripts 里多个命令之间要用逗号;路径不要漏掉 ./;start 和 build 的名字要和实际执行命令对应。若执行 npm start 报开发服务器相关依赖缺失,应检查项目是否已安装对应依赖,不要误以为配置文件写错。

验证与常见调试思路 ​

为什么需要: 拆分配置后必须验证“开发命令走开发配置、构建命令走生产配置”,否则等于白拆。
怎么想: 先看终端实际执行的命令是否带上了 --config,再看输出是否按对应 mode 工作。
怎么做: 执行:

bash
npm start
npm run build

分别观察终端加载的是 webpack.dev.js 还是 webpack.prod.js。
易错点: 常见问题不是“配置内容多复杂”,而是路径写错、文件名拼错、mode 拼错、scripts JSON 格式错误。排查时先看命令,再看路径,最后看 mode。后续如果开发和生产还需要不同优化项、调试项,也应分别写进这两份配置文件,而不是重新混回一份。

先厘清:CSS 处理与 HTML 内部资源是两条线 ​

为什么需要:生产打包时,CSS 里的 url()、HTML 里的 <img>、<video>、<audio> 都可能引用资源。CSS 资源此前已经通过 css-loader 等规则进入打包链路,但 HTML 内部引用的图片、音视频不会自动被处理,容易在 dist 中找不到文件。
怎么想:转写稿里强调,“CSS 本身就加了 loader”,所以 CSS 资源能按扩展名受对应 loader 影响;HTML 文件本身由 HtmlWebpackPlugin 打包,但 HTML 内部使用的资源要再加 html-loader。
怎么做:安装并配置 html-loader:

bash
npm i html-loader -D
js
module.exports = {
  module: {
    rules: [
      {
        test: /\.html$/,
        use: 'html-loader'
      }
    ]
  }
}

图片是否内联为 Base64,仍受图片 loader 的 limit 等配置影响;音视频默认可能直接输出到 dist 根目录,若想放到 medias 并带 12 位哈希,可按转写稿的 file-loader 思路单独配规则。
易错点:不要以为装了 HtmlWebpackPlugin 就会处理 HTML 内部资源;html-loader 针对的是 HTML 内部使用的资源,不是替代 HTML 插件。

HTML 中的图片、音视频如何进入打包 ​

为什么需要:如果 HTML 里写 ./images/webpack.png、./videos/xxx.mp4,打包后路径仍按源码目录找,dist 中自然找不到。
怎么想:先判断资源归属:音视频、图片若只被 HTML 直接引用,可放到 public,但要让 webpack 处理并输出,就要让对应扩展名命中 loader。
怎么做:按转写稿给音视频补规则,输出到 medias:

js
{
  test: /\.(mp3|mp4|webm|ogg)$/,
  use: [{
    loader: 'file-loader',
    options: {
      name: 'medias/[hash:12].[ext]'
    }
  }]
}

配置后,HTML 中图片路径会变成打包后的地址,音视频也能被复制到指定位置。
易错点:只配 html-loader 不够,音视频还要有对应扩展名规则;图片若小于阈值会被 Base64 处理,路径表现与直接输出文件不同。

ESLint:先让代码规范可检查 ​

为什么需要:生产打包前要发现明显不规范代码,团队也需要统一规则。
怎么想:转写稿明确说旧 eslint-loader 适配有限,改用 ESLint 插件;插件只检查指定目录,避免把 node_modules 也查了。
怎么做:安装 eslint 与 eslint-webpack-plugin,在 webpack 插件中指定 src:

js
const ESLintPlugin = require('eslint-webpack-plugin')

plugins: [
  new ESLintPlugin({
    context: 'src'
  })
]

项目根目录建 .eslintrc.js:

js
module.exports = {
  env: { browser: true, node: true, es2021: true },
  extends: ['eslint:recommended'],
  parserOptions: { ecmaVersion: 2023, sourceType: 'module' },
  rules: {
    'no-console': 0,
    eqeqeq: 2,
    'no-alert': 0
  },
  globals: { $: 'readonly' }
}

易错点:不指定检查范围会连 node_modules 一起查,几乎不可能通过;no-undef 报 console、document 未定义时,要检查 env.browser;ecmaVersion 太低会误报新语法。

Babel 与 polyfill:语法降级和 API 补齐不是一回事 ​

为什么需要:Babel 把 ES6+ 语法转成 ES5,提高旧浏览器兼容性;但 Promise 这类新 API 不是语法能转出来的,需要 polyfill。
怎么想:先 Babel 降语法,再按兼容要求决定是否引入 polyfill。转写稿里 @babel/preset-env 是预设工具包,babel-loader 接进 webpack。
怎么做:

bash
npm i babel-loader @babel/core @babel/preset-env -D
js
{
  test: /\.js$/,
  exclude: /node_modules/,
  use: {
    loader: 'babel-loader',
    options: {
      presets: ['@babel/preset-env']
    }
  }
}

polyfill 按转写稿在入口引入,并放生产依赖;它是否启用要看项目兼容性要求。
易错点:不排除 node_modules 会编译第三方旧代码;Babel 不是万能的,Promise 等 API 仍需 polyfill。

Source Map:生产排错要能定位源码 ​

为什么需要:打包压缩后控制台报错可能只指向打包文件某行,难以定位源码。
怎么想:source-map 记录目标文件与源文件的映射;完整映射包含行和列,打包慢但定位准,适合生产发布;只映射行更快,适合开发频繁打包。
怎么做:

js
module.exports = {
  devtool: 'source-map'
}

若开发阶段追求速度,可用只映射行的方案。
易错点:生产排错不要只看“第几行”就满足,必要时用完整映射;但开发时频繁打包,完整映射会明显拖慢速度。

本节笔记的核心只有一句:默认生产模式已经开启了 JS 压缩,不需要额外进行配置。下面把转写稿里围绕生产模式 JS 处理的几个环节串起来,重点不是背配置,而是知道每个工具解决什么问题、什么时候该用。

默认生产模式:JS 压缩不用再配 ​

为什么需要: 生产环境要把 JS 体积压小,减少下载时间和带宽,所以打包工具在生产模式下默认就会做压缩。
怎么想: 既然默认已经开启,就不要重复加压缩配置,先确认当前 mode 是生产模式即可。
怎么做: 按笔记结论:默认生产模式已经开启 JS 压缩,不需要额外配置。
易错点: 不要因为“想优化”就再手动叠一套压缩插件,容易重复处理或配置冲突;先看默认行为是否已满足。

ESLint:生产打包前先卡代码规范 ​

为什么需要: 转写稿里 ESLint 用来检查代码规范,比如未声明变量、console、document 使用环境等。生产打包时如果检查不通过,编译会失败。
怎么想: 它不是压缩工具,而是“规则检查器”。转写稿里明确说旧 eslint-loader 适配跟不上,课堂改用 ESLint 插件方式。
怎么做: 安装 ESLint 及 webpack 插件,在插件数组里加入 ESLint 插件,并指定只检查 src 目录;再在项目根目录建 .eslintrc.js,继承推荐规则,配置 env 和 globals,用规则等级 0/1/2 控制忽略、警告、错误。
易错点: 不指定检查根目录会把 node_modules 也查了,几乎不可能通过;import 报解析错误要允许 ES6 模块;console、document 是否可用取决于 env 和 globals。转写稿里的调试顺序就是:先报找不到配置文件,再报 import 解析错误,再报 console is not defined,最后靠规则配置解决。

babel:把新语法转成旧浏览器能识别的语法 ​

为什么需要: 生产环境可能要兼容旧浏览器,而旧浏览器不认识 ES6+ 的新语法,需要转成 ES5。
怎么想: babel 解决“语法层”的兼容,比如箭头函数、const、let 等转成旧写法。转写稿里打包后搜索新语法消失、const 变 var,说明转译生效。
怎么做: 安装 @babel/core、@babel/preset-env、babel-loader,在 webpack 的 rules 中配置 test: /\.js$/、exclude: /node_modules/,use 里写 babel-loader 和预设。

js
{
  test: /\.js$/,
  exclude: /node_modules/,
  use: {
    loader: 'babel-loader',
    options: {
      presets: ['@babel/preset-env']
    }
  }
}

易错点: 一定要排除 node_modules,否则会把依赖里的老代码也编译,慢且不可控;babel 只转语法,不负责给旧浏览器补 Promise 这类新 API。

polyfill:补旧浏览器没有的新 API ​

为什么需要: 转写稿里强调,Promise 这类东西不是“语法替换”能解决的,旧浏览器压根没有这个能力,需要 polyfill 把实现带进代码里。
怎么想: babel 管语法,polyfill 管 API;它通常不是 webpack 的 loader 配置,而是按需引入。
怎么做: 按转写稿安装对应 polyfill 包,在项目入口文件引入,并作为生产依赖,因为上线后仍要解决兼容问题。
易错点: 不要把它当开发依赖;不要以为 babel 能自动补所有 API;按项目兼容性要求按需使用。转写稿打包后能搜到自定义 Promise 实现,说明 polyfill 是“把代码带过去”。

sourceMap:打包后还能定位源码错误 ​

为什么需要: 打包后代码被合并、压缩,控制台报错行号是打包后的行号,不是源码行号。转写稿里故意写错后,未加 sourceMap 时控制台报的是打包后第 25 行,无法直接对应源码。
怎么想: sourceMap 记录“目标文件哪一段对应原文件哪一段”,浏览器报错时据此映射回源码。
怎么做: 在 webpack 配置中加 devtool。转写稿口述有“包含行和列映射”和“只映射行”两种取舍:前者精确但打包慢,后者快一些。

js
module.exports = {
  // ...
  devtool: 'source-map'
}

易错点: 开发时频繁打包,太精确的映射会拖慢速度;生产发布通常只打包一次,可以按需要选择更精确的映射。加完后转写稿里报错从打包后第 25 行定位回源码第 17 行,说明映射生效。生产环境还要考虑是否暴露源码,按团队规范选择。

先分清两件事:HTML 压缩与 HTML 内资源 ​

本节笔记的结论是:默认生产模式已经开启了 HTML 压缩,不需要额外进行配置。但学习这一节时,最容易混淆的是两件事:一是“HTML 文件本身要不要压缩”,二是“HTML 文件里引用的图片、音视频等资源要不要被打包”。前者是生产模式的构建优化,后者需要 html-loader 参与处理。把这两件事分开,后面的配置才不会乱。

生产模式下 HTML 压缩为什么不用再配 ​

为什么需要:生产环境追求更小的体积和更快的加载速度,HTML 中的空格、注释、换行等如果能被压缩,产物会更小。

怎么想:既然本节笔记已经明确“默认生产模式已经开启了 HTML 压缩”,那就不要再重复添加一套 HTML 压缩配置。先确认构建模式是生产模式,再观察产物即可。

怎么做:配置中保证使用生产模式,例如:

js
module.exports = {
  mode: 'production'
};

然后执行打包,查看 dist 下的 HTML 产物是否已经被压缩。按本节笔记结论,这一步不需要额外配置。

易错点:不要以为“HTML 被压缩了”就等于“HTML 里引用的图片、音视频也被打包了”。压缩处理的是 HTML 文本本身,资源路径和资源输出是另一套逻辑。

HTML 里的图片、音视频为什么不能只靠 JS 资源规则 ​

为什么需要:在 HTML 中写 <img src="./webpack.png">、<video src="./videos/xxx.mp4" controls></video>、<audio src="./audios/xxx.mp3" controls></audio> 时,这些资源不是通过 JS 的 import 引入的,Webpack 默认不会自动接管 HTML 内部引用的资源。

怎么想:先判断资源属于谁。图片、音视频如果直接写在 HTML 里,就属于“HTML 文件内部使用的资源”。它们需要专门的 loader 来识别和改写路径。

怎么做:安装并配置 html-loader:

bash
npm install html-loader -D
js
module.exports = {
  module: {
    rules: [
      {
        test: /\.html$/,
        use: ['html-loader']
      }
    ]
  }
};

易错点:html-loader 的名字容易误解。它不是用来打包 HTML 文件本身的;HTML 文件本身的生成和压缩通常由 HtmlWebpackPlugin 等插件负责。html-loader 处理的是 HTML 文件内部引用的资源。

调试过程:不配置时为什么在 dist 中找不到资源 ​

为什么需要:只看配置很难理解 loader 的作用,必须观察打包前后的路径变化。

怎么想:在 HTML 中写相对路径,例如 ./webpack.png、./videos/xxx.mp4、./audios/xxx.mp3。如果不加 html-loader,打包后的 dist/index.html 很可能仍然保留这些相对路径。浏览器会从 dist 当前目录去找,但资源并没有按这个路径出现在 dist 中,所以找不到。

怎么做:先不加 html-loader 打包一次,打开 dist/index.html 观察图片、音视频的 src;再加上 html-loader 重新打包,观察路径是否被改写为构建后的资源地址。演示中配置后,图片会按资源规则输出,HTML 中的路径也会随之变化;音视频如果希望放到指定目录,还需要额外配置输出位置。

易错点:public 是模板或静态参考目录,不是最终产物目录。不能因为资源放在 public 里,就认为它会自动出现在 dist 中并被正确引用。最终要看 dist 里的实际路径。

输出位置与资源类型的易错点 ​

为什么需要:图片、音视频的输出位置会影响 HTML 中的引用路径。图片可能受之前资源模块规则影响,例如小于一定体积时被转成 base64;音视频一般不适合内联,通常按资源文件输出。

怎么想:先看扩展名,再看已有 loader 或 asset module 规则。图片走图片规则,音视频走音视频规则。若希望音视频统一放到 static/media,就单独写规则,例如:

js
{
  test: /\.(mp4|webm|ogg|mp3)$/,
  type: 'asset/resource',
  generator: {
    filename: 'static/media/[name].[hash:12][ext]'
  }
}

怎么做:配置后重新打包,检查 dist/static/media 是否出现音视频文件,并检查 dist/index.html 中的 src 是否指向新位置。

易错点:不要把所有资源都塞进同一条规则;不要把 html-loader 当成 HTML 压缩工具;也不要重复配置生产模式已经默认完成的 HTML 压缩。先确认“压缩”和“资源打包”各自负责什么,再排查路径问题。

课堂画面 ​

课堂画面 1

课堂画面 2

课堂画面 3

课堂画面 4

课堂画面 5

代码示例 ​

示例 1:开发模式 SourceMap 配置

js
// webpack.config.js
const path = require('path');

module.exports = {
  mode: 'development',
  devtool: 'cheap-module-source-map',
  entry: './src/index.js',
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: 'js/main.js'
  }
};
js
// src/index.js
console.log('sourcemap demo');

关键行与易错点

  • devtool: 'cheap-module-source-map':只有行映射,打包编译速度快,适合开发模式。
  • devtool 必须和 mode、entry、output 同级,不要写进 module.rules。
  • mode: 'development' 表示开发模式。
  • 运行 npx webpack 后,dist 中会生成对应 .map 文件,便于浏览器定位源码错误位置。

示例 2:生产模式 SourceMap 配置

js
// webpack.config.js
const path = require('path');

module.exports = {
  mode: 'production',
  devtool: 'source-map',
  entry: './src/index.js',
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: 'js/main.js'
  }
};
js
// src/index.js
const user = '张三';
user.name();
html
<!-- dist/index.html -->
<script src="./js/main.js"></script>

关键行与易错点

  • devtool: 'source-map':包含行和列的映射,打包编译速度慢,适合生产模式。
  • mode 必须写 production,笔记中出现的 producation 是拼写错误。
  • 生产模式默认已经开启 JS 压缩和 HTML 压缩,不需要额外配置。
  • SourceMap 只帮助定位错误,不会让运行时错误在打包阶段消失;该示例打包仍会成功,错误需在浏览器控制台查看。
  • 生产模式一般只在发布时打包,所以可以使用更精确但更慢的 source-map。

示例 3:开发/生产分配置文件与 npm 命令

json
// package.json
{
  "name": "sourcemap-demo",
  "version": "1.0.0",
  "scripts": {
    "start": "npx webpack serve --config ./config/webpack.dev.js",
    "build": "npx webpack --config ./config/webpack.prod.js"
  }
}
js
// config/webpack.dev.js
const path = require('path');

module.exports = {
  mode: 'development',
  devtool: 'cheap-module-source-map',
  entry: './src/index.js',
  output: {
    path: path.resolve(__dirname, '../dist'),
    filename: 'js/main.js'
  }
};
js
// config/webpack.prod.js
const path = require('path');

module.exports = {
  mode: 'production',
  devtool: 'source-map',
  entry: './src/index.js',
  output: {
    path: path.resolve(__dirname, '../dist'),
    filename: 'js/main.js'
  }
};
js
// src/index.js
console.log('dev and prod config demo');

关键行与易错点

  • --config ./config/webpack.dev.js、--config ./config/webpack.prod.js 用于指定不同环境的配置文件。
  • start 使用 webpack serve,build 使用 webpack。
  • 配置文件放在 config 目录后,__dirname 指向 config,所以输出目录要写 ../dist。
  • 开发配置用 development + cheap-module-source-map,生产配置用 production + source-map。
  • 易错点:--config 后面的路径必须真实存在;mode 拼写必须正确。

常见错误 ​

  1. 现象:HTML 中引用的图片/音视频打包后 404。→ 原因:资源放在 public 却被当作模块资源引用,或 HTML 内资源未交给 html-loader 处理。→ 修法:区分 public 直出资源与 src 模块资源;需要参与打包的 HTML 内资源配置 html-loader,并检查最终输出路径。

  2. 现象:音视频默认被打到 dist 根目录,路径混乱。→ 原因:只配置了图片资源规则,未给 mp3/mp4/webm/ogg 等配置 asset/resource 输出规则。→ 修法:为音视频增加 generator.filename,统一输出到如 static/media,并核对 HTML 中最终路径。

  3. 现象:ESLint 报“未找到配置文件”或检查到 node_modules。→ 原因:配置文件命名/位置错误,或未限定检查目录。→ 修法:在项目根目录使用正确配置文件名;插件配置中限定只检查 src,避免扫描 node_modules。

  4. 现象:ESLint 报 no-undef,如 console/document 未定义。→ 原因:未声明运行环境,或规则默认按未声明变量处理。→ 修法:配置 env.browser 等环境;必要时用 globals 预声明,或在 rules 中按项目规范调整 no-undef。

  5. 现象:Babel 配置后打包变慢或把依赖也编译了。→ 原因:babel-loader 未排除 node_modules。→ 修法:设置 exclude: /node_modules/,test 精确匹配 JS,presets 使用 @babel/preset-env。

  6. 现象:SourceMap 未生成或报错位置仍不准。→ 原因:devtool 与 mode 不同级、值选错,或开发/生产配置未通过 --config 正确加载。→ 修法:在配置对象同级设置 devtool;开发用行映射优先,生产按需选行列映射;检查 scripts 的 --config 路径与 mode 拼写。

练习 ​

  1. 在 src 中准备一张图片,在 public 中准备一个音频;分别在 HTML 中引入,配置 html-loader 与音视频输出规则,打包后验证 dist 中路径与控制台资源加载。

  2. 为项目配置 ESLint:只检查 src,开启浏览器环境,将 no-console 在开发环境设为警告、生产环境设为错误,并说明如何根据报错中的规则名查文档修复。

  3. 配置 babel-loader 将 ES6+ 转为 ES5:排除 node_modules,使用 @babel/preset-env,打包后对比 let/const/箭头函数 转换前后代码。

  4. 为旧浏览器兼容 Promise:安装并引入 polyfill,说明它应放在 dependencies 还是 devDependencies,并验证入口文件引入后打包产物包含兼容代码。

  5. 拆分 webpack.dev.js 与 webpack.prod.js,在 package.json 中配置 start/build 脚本;分别设置合适的 devtool,制造一个运行错误并比较两种模式下控制台定位精度。

原视频对照 ​

#视频时长
1处理HTML中的图片视频等12:20
2处理JS资源-ESlint检查代码规范28:22
3处理JS资源-babel12:40
4处理JS资源-polyfill05:37
5sourceMap11:46

小结 ​

  1. public 资源与 src 模块资源要分清,HTML 内资源是否参与打包取决于是否配置对应 loader。
  2. 图片、音视频等静态资源应显式规划输出目录,避免默认落到 dist 根目录造成路径混乱。
  3. ESLint 排错先看规则名,再查规则文档;配置文件位置、检查范围和运行环境是高频问题源。
  4. babel-loader 必须排除 node_modules,Babel 解决语法转换,polyfill 解决旧环境缺失 API,两者职责不同。
  5. devtool 要与 mode 同级配置,开发追求速度、生产按需精度,并注意生产 SourceMap 的源码暴露风险。
  6. 开发/生产配置拆分后,scripts 中的 --config 路径和 mode 拼写必须准确。
  7. 生产模式下 JS/HTML 压缩通常已由 mode 默认处理,不必重复堆配置。
  8. 遇到构建失败先定位是 loader、plugin、规则检查还是资源路径问题,再按报错信息逐层收敛。