知海

开发环境

webpackjsorg-main指南-教程

开发环境

T> 本指南基于输出管理指南中的代码示例展开。

如果你一直跟着前面的指南走,应该已经对 webpack 的基础知识有了扎实的理解。在继续之前,我们先来搭建一个开发环境,让我们的生活更轻松一些。

W> 本指南中的工具仅用于开发,请避免在生产环境中使用它们!

让我们首先将 mode 设置为 'development',并将 title 设置为 'Development'

webpack.config.js

diff 复制代码
 import path from 'node:path';
 import { fileURLToPath } from 'node:url';
 import HtmlWebpackPlugin from 'html-webpack-plugin';

 const __filename = fileURLToPath(import.meta.url);
 const __dirname = path.dirname(__filename);

 export default {
+  mode: 'development',
   entry: {
     index: './src/index.js',
     print: './src/print.js',
   },
   plugins: [
     new HtmlWebpackPlugin({
-      title: 'Output Management',
+      title: 'Development',
     }),
   ],
   output: {
     filename: '[name].bundle.js',
     path: path.resolve(__dirname, 'dist'),
     clean: true,
   },
 };

使用 source map

当 webpack 打包你的源代码时,可能会难以追踪错误和警告到它们的原始位置。例如,如果你将三个源文件(a.jsb.jsc.js)打包到一个 bundle(bundle.js)中,而其中一个源文件包含错误,那么堆栈跟踪将指向 bundle.js。这并不总是有帮助,因为你可能想知道错误究竟来自哪个源文件。

为了更容易地追踪错误和警告,JavaScript 提供了 source map,它会将编译后的代码映射回原始源代码。如果错误源自 b.js,source map 会准确地告诉你这一点。

关于 source map,有许多不同的选项可用。请务必查看它们,以便根据需要进行配置。

对于本指南,我们使用 inline-source-map 选项,它对于说明目的很有帮助(尽管不适用于生产环境):

webpack.config.js

diff 复制代码
 import path from 'node:path';
 import { fileURLToPath } from 'node:url';
 import HtmlWebpackPlugin from 'html-webpack-plugin';

 const __filename = fileURLToPath(import.meta.url);
 const __dirname = path.dirname(__filename);

 export default {
   mode: 'development',
   entry: {
     index: './src/index.js',
     print: './src/print.js',
   },
+  devtool: 'inline-source-map',
   plugins: [
     new HtmlWebpackPlugin({
       title: 'Development',
     }),
   ],
   output: {
     filename: '[name].bundle.js',
     path: path.resolve(__dirname, 'dist'),
     clean: true,
   },
 };

现在,让我们确保有东西可以调试,所以在 print.js 文件中创建一个错误:

src/print.js

diff 复制代码
 export default function printMe() {
-  console.log('I get called from print.js!');
+  cosnole.log('I get called from print.js!');
 }

运行 npm run build,它应该会编译出类似如下的内容:

bash 复制代码
...
[webpack-cli] Compilation finished
asset index.bundle.js 1.38 MiB [emitted] (name: index)
asset print.bundle.js 6.25 KiB [emitted] (name: print)
asset index.html 272 bytes [emitted]
runtime modules 1.9 KiB 9 modules
cacheable modules 530 KiB
  ./src/index.js 406 bytes [built] [code generated]
  ./src/print.js 83 bytes [built] [code generated]
  ./node_modules/lodash/lodash.js 530 KiB [built] [code generated]
webpack 5.x.x compiled successfully in 706 ms

现在在浏览器中打开生成的 index.html 文件。点击按钮,查看控制台中显示的错误。错误信息应该类似这样:

bash 复制代码
Uncaught ReferenceError: cosnole is not defined
   at HTMLButtonElement.printMe (print.js:2)

我们可以看到,错误还包含了发生错误的文件(print.js)和行号(2)的引用。这很好,因为我们现在确切地知道去哪里查找并修复问题。

选择开发工具

W> 某些文本编辑器具有“安全写入”功能,可能会干扰下面的一些工具。请阅读调整文本编辑器以解决这些问题。

每次编译代码时都手动运行 npm run build 很快就会变得很麻烦。

webpack 提供了几种不同的选项,可以帮助你在代码更改时自动编译代码:

  1. webpack 的监视模式
  2. webpack-dev-server
  3. webpack-dev-middleware

在大多数情况下,你可能希望使用 webpack-dev-server,但让我们探索以上所有选项。

使用监视模式

你可以指示 webpack “监视”依赖图中所有文件的更改。如果这些文件之一被更新,代码将被重新编译,这样你就不必手动运行完整构建。

让我们添加一个 npm 脚本,用于启动 webpack 的监视模式:

package.json

diff 复制代码
 {
   "name": "webpack-demo",
   "version": "1.0.0",
   "description": "",
   "private": true,
   "scripts": {
     "test": "echo \"Error: no test specified\" && exit 1",
+    "watch": "webpack --watch",
     "build": "webpack"
   },
   "keywords": [],
   "author": "",
   "license": "ISC",
   "devDependencies": {
     "html-webpack-plugin": "^5.6.6",
     "webpack": "^5.105.0",
     "webpack-cli": "^7.0.0"
   },
   "dependencies": {
     "lodash": "^4.17.21"
   }
 }

现在在命令行中运行 npm run watch,看看 webpack 如何编译你的代码。
你可以看到它没有退出命令行,因为脚本当前正在监视你的文件。

现在,当 webpack 监视你的文件时,让我们移除之前引入的错误:

src/print.js

diff 复制代码
 export default function printMe() {
-  cosnole.log('I get called from print.js!');
+  console.log('I get called from print.js!');
 }

现在保存文件并检查终端窗口。你应该看到 webpack 自动重新编译了更改的模块!

唯一的缺点是必须刷新浏览器才能看到更改。如果这也能自动发生就更好了,所以让我们试试 webpack-dev-server,它可以做到这一点。

使用 webpack-dev-server

webpack-dev-server 为你提供了一个基础的 Web 服务器,并具备实时重新加载的功能。让我们设置它:

bash 复制代码
npm install --save-dev webpack-dev-server

修改配置文件,告诉开发服务器在哪里查找文件:

webpack.config.js

diff 复制代码
 import path from 'node:path';
 import { fileURLToPath } from 'node:url';
 import HtmlWebpackPlugin from 'html-webpack-plugin';

 const __filename = fileURLToPath(import.meta.url);
 const __dirname = path.dirname(__filename);

 export default {
   mode: 'development',
   entry: {
     index: './src/index.js',
     print: './src/print.js',
   },
   devtool: 'inline-source-map',
+  devServer: {
+    static: './dist',
+  },
   plugins: [
     new HtmlWebpackPlugin({
       title: 'Development',
     }),
   ],
   output: {
     filename: '[name].bundle.js',
     path: path.resolve(__dirname, 'dist'),
     clean: true,
   },
+  optimization: {
+    runtimeChunk: 'single',
+  },
 };

这告诉 webpack-dev-serverlocalhost:8080 上提供 dist 目录中的文件。

T> 添加 optimization.runtimeChunk: 'single' 是因为在这个例子中,我们在单个 HTML 页面上有多个入口点。如果没有这个,我们可能会遇到这里描述的问题。有关更多详细信息,请阅读代码分离章节。

T> webpack-dev-serveroutput.path 中定义的目录提供打包文件,即文件将在 http://[devServer.host]:[devServer.port]/[output.publicPath]/[output.filename] 下可用。

W> webpack-dev-server 编译后不会写入任何输出文件。相反,它将 bundle 文件保存在内存中,并将它们当作挂载在服务器根路径上的真实文件来提供。如果你的页面期望在另一个路径上找到 bundle 文件,可以通过开发服务器配置中的 devMiddleware.publicPath 选项来更改。

让我们也添加一个脚本,方便地运行开发服务器:

package.json

diff 复制代码
 {
   "name": "webpack-demo",
   "version": "1.0.0",
   "description": "",
   "private": true,
   "scripts": {
     "test": "echo \"Error: no test specified\" && exit 1",
     "watch": "webpack --watch",
+    "start": "webpack serve --open",
     "build": "webpack"
   },
   "keywords": [],
   "author": "",
   "license": "ISC",
   "devDependencies": {
     "html-webpack-plugin": "^5.6.6",
     "webpack": "^5.105.0",
     "webpack-cli": "^7.0.0",
     "webpack-dev-server": "^5.2.3"
   },
   "dependencies": {
     "lodash": "^4.17.21"
   }
 }

现在我们可以从命令行运行 npm start,我们会看到浏览器自动加载我们的页面。如果你现在更改任何源文件并保存,Web 服务器将在代码编译后自动重新加载。试试看!

webpack-dev-server 带有许多可配置的选项。前往文档了解更多信息。

T> 既然你的服务器已经工作了,你可能想尝试一下模块热替换

使用 webpack-dev-middleware

webpack-dev-middleware 是一个包装器,它将 webpack 处理的文件发送到服务器。这在 webpack-dev-server 内部使用,但也可以作为单独的包使用,以便在需要时允许更自定义的设置。我们将看一个将 webpack-dev-middleware 与 express 服务器结合的示例。

让我们安装 expresswebpack-dev-middleware 以便开始:

bash 复制代码
npm install --save-dev express webpack-dev-middleware

现在我们需要对 webpack 配置文件进行一些调整,以确保中间件能正常工作:

webpack.config.js

diff 复制代码
 import path from 'node:path';
 import { fileURLToPath } from 'node:url';
 import HtmlWebpackPlugin from 'html-webpack-plugin';

 const __filename = fileURLToPath(import.meta.url);
 const __dirname = path.dirname(__filename);

 export default {
   mode: 'development',
   entry: {
     index: './src/index.js',
     print: './src/print.js',
   },
   devtool: 'inline-source-map',
   devServer: {
     static: './dist',
   },
   plugins: [
     new HtmlWebpackPlugin({
       title: 'Development',
     }),
   ],
   output: {
     filename: '[name].bundle.js',
     path: path.resolve(__dirname, 'dist'),
     clean: true,
+    publicPath: '/',
   },
 };

publicPath 也将在我们的服务器脚本中使用,以确保文件在 http://localhost:3000 上正确提供。我们稍后指定端口号。下一步是设置我们的自定义 express 服务器:

project

diff 复制代码
  webpack-demo
   ├── package.json
   ├── package-lock.json
   ├── webpack.config.js
+  ├── server.js
   ├── /dist
   ├── /src
   │   ├── index.js
   │   └── print.js
   └── /node_modules

server.js

js 复制代码
import express from "express";
import webpack from "webpack";
import webpackDevMiddleware from "webpack-dev-middleware";
import config from "./webpack.config.js";

const app = express();
const compiler = webpack(config);

// 告诉 express 使用 webpack-dev-middleware,
// 并使用 webpack.config.js 配置文件作为基础。
app.use(
  webpackDevMiddleware(compiler, {
    publicPath: config.output.publicPath,
  }),
);

// 在 3000 端口上提供文件。
app.listen(3000, () => {
  console.log("Example app listening on port 3000!\n");
});

现在添加一个 npm 脚本,以便更容易运行服务器:

package.json

diff 复制代码
 {
   "name": "webpack-demo",
   "version": "1.0.0",
   "description": "",
   "private": true,
   "scripts": {
     "test": "echo \"Error: no test specified\" && exit 1",
     "watch": "webpack --watch",
     "start": "webpack serve --open",
+    "server": "node server.js",
     "build": "webpack"
   },
   "keywords": [],
   "author": "",
   "license": "ISC",
   "devDependencies": {
     "express": "^5.2.1",
     "html-webpack-plugin": "^5.6.6",
     "webpack": "^5.105.0",
     "webpack-cli": "^7.0.0",
     "webpack-dev-middleware": "^8.0.3",
     "webpack-dev-server": "^5.2.3"
   },
   "dependencies": {
     "lodash": "^4.17.21"
   }
 }

现在在你的终端中运行 npm run server,它应该会给你类似以下的输出:

bash 复制代码
Example app listening on port 3000!
...
<i> [webpack-dev-middleware] asset index.bundle.js 1.38 MiB [emitted] (name: index)
<i> asset print.bundle.js 6.25 KiB [emitted] (name: print)
<i> asset index.html 274 bytes [emitted]
<i> runtime modules 1.9 KiB 9 modules
<i> cacheable modules 530 KiB
<i>   ./src/index.js 406 bytes [built] [code generated]
<i>   ./src/print.js 83 bytes [built] [code generated]
<i>   ./node_modules/lodash/lodash.js 530 KiB [built] [code generated]
<i> webpack 5.x.x compiled successfully in 709 ms
<i> [webpack-dev-middleware] Compiled successfully.
<i> [webpack-dev-middleware] Compiling...
<i> [webpack-dev-middleware] assets by status 1.38 MiB [cached] 2 assets
<i> cached modules 530 KiB (javascript) 1.9 KiB (runtime) [cached] 12 modules
<i> webpack 5.x.x compiled successfully in 19 ms
<i> [webpack-dev-middleware] Compiled successfully.

现在打开你的浏览器并访问 http://localhost:3000。你应该看到你的 webpack 应用正在运行!

T> 如果你想了解更多关于模块热替换的工作原理,我们推荐你阅读模块热替换指南。

调整文本编辑器

使用自动编译代码时,在保存文件时可能会遇到问题。某些编辑器具有“安全写入”功能,可能会干扰重新编译。

要在一些常见的编辑器中禁用此功能,请参阅以下列表:

  • Sublime Text 3:在你的用户偏好设置中添加 atomic_save: 'false'
  • JetBrains IDE(如 WebStorm):在 Preferences > Appearance & Behavior > System Settings 中取消勾选“Use safe write”。
  • Vim:在你的设置中添加 :set backupcopy=yes

总结

现在你已经学会了如何自动编译代码并运行开发服务器,你可以查看下一个指南,它将介绍代码分离

帮助我们改进文档

发现翻译问题或内容错误?请告诉我们。