一、起步
1、基本安装
首先我们创建一个目录,初始化 npm,然后 在本地安装 webpack,接着安装 webpack-cli(此工具用于在命令行中运行 webpack):
mkdir webpack-demo
cd webpack-demo
npm init -y
npm install webpack webpack-cli --save-dev
现在,我们将创建以下目录结构、文件和内容:
project
webpack-demo
|- package.json
+ |- index.html
+ |- /src
+ |- index.js
src/index.js
function component() {
const element = document.createElement('div');
// https://www.lodashjs.com/
// lodash(目前通过一个 script 引入)对于执行这一行是必需的
element.innerHTML = _.join(['Hello', 'webpack'], ' ');
return element;
}
document.body.appendChild(component());
index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>起步</title>
<script src="https://unpkg.com/lodash@4.17.20"></script>
</head>
<body>
<script src="./src/index.js"></script>
</body>
</html>
我们还需要调整 package.json
文件,以便确保我们安装包是 private(私有的)
,并且移除 main
入口。这可以防止意外发布你的代码。
package.json
{
"name": "webpack-demo",
"version": "1.0.0",
"description": "",
- "main": "index.js",
+ "private": true,
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC",
"devDependencies": {
"webpack": "^5.4.0",
"webpack-cli": "^4.2.0"
}
}
启动项目
npx http-server
在此示例中,<script>
标签之间存在隐式依赖关系。在 index.js
文件执行之前,还需要在页面中先引入 lodash
。这是因为 index.js
并未显式声明它需要 lodash
,只是假定推测已经存在一个全局变量 _
。
使用这种方式去管理 JavaScript 项目会有一些问题:
- 无法直接体现,脚本的执行依赖于外部库。
- 如果依赖不存在,或者引入顺序错误,应用程序将无法正常运行。
- 如果依赖被引入但是并没有使用,浏览器将被迫下载无用代码。
让我们使用 webpack 来管理这些脚本。
2、创建一个bundle
首先,我们稍微调整下目录结构,创建分发代码(./dist
)文件夹用于存放分发代码,源代码(./src
)文件夹仍存放源代码。
project
webpack-demo
|- package.json
+ |- /dist
+ |- index.html
- |- index.html
|- /src
|- index.js
要在 index.js
中打包 lodash
依赖,我们需要在本地安装 library:
npm install --save lodash
src/index.js
+import _ from 'lodash';
+
function component() {
const element = document.createElement('div');
- // lodash(目前通过一个 script 引入)对于执行这一行是必需的
+ // lodash,现在通过一个 script 引入
element.innerHTML = _.join(['Hello', 'webpack'], ' ');
return element;
}
document.body.appendChild(component());
dist/index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>起步</title>
- <script src="https://unpkg.com/lodash@4.17.20"></script>
</head>
<body>
- <script src="./src/index.js"></script>
+ <script src="main.js"></script>
</body>
</html>
开始打包
$ npx webpack
3、模块
ES2015 中的 import 和 export 语句已经被标准化。虽然大多数浏览器还无法支持它们,但是 webpack 却能够提供开箱即用般的支持。
事实上,webpack 在幕后会将代码“转译”,以便旧版本浏览器可以执行。如果你检查 dist/main.js
,你可以看到 webpack 具体如何实现,这是独创精巧的设计!除了 import
和 export
,webpack 还能够很好地支持多种其他模块语法,更多信息请查看 模块 API。
注意,webpack 不会更改代码中除 import
和 export
语句以外的部分。如果你在使用其它 ES2015 特性,请确保你在 webpack loader 系统 中使用了 Babel
请查看编译后的代码。
4、使用一个配置文件
project
webpack-demo
|- package.json
+ |- webpack.config.js
|- /dist
|- index.html
|- /src
|- index.js
webpack.config.js
const path = require('path');
module.exports = {
entry: './src/index.js',
output: {
filename: 'main.js',
path: path.resolve(__dirname, 'dist'),
},
};
现在,让我们通过新的配置文件再次执行构建:
5、npm scripts
package.json
{
"name": "webpack-demo",
"version": "1.0.0",
"description": "",
"private": true,
"scripts": {
- "test": "echo \"Error: no test specified\" && exit 1"
+ "test": "echo \"Error: no test specified\" && exit 1",
+ "build": "webpack"
},
"keywords": [],
"author": "",
"license": "ISC",
"devDependencies": {
"webpack": "^5.4.0",
"webpack-cli": "^4.2.0"
},
"dependencies": {
"lodash": "^4.17.20"
}
}
现在运行以下命令:
$ npm run build
二、管理资源
1、设置
在开始之前,让我们对项目做一个小的修改:
dist/index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
- <title>起步</title>
+ <title>管理资源</title>
</head>
<body>
- <script src="main.js"></script>
+ <script src="bundle.js"></script>
</body>
</html>
webpack.config.js
const path = require('path');
module.exports = {
entry: './src/index.js',
output: {
- filename: 'main.js',
+ filename: 'bundle.js',
path: path.resolve(__dirname, 'dist'),
},
};
2、加载 CSS
为了在 JavaScript 模块中 import
一个 CSS 文件,你需要安装 style-loader 和 css-loader,并在 module 中添加这些 loader:
npm install --save-dev style-loader css-loader
webpack.config.js
const path = require('path');
module.exports = {
entry: './src/index.js',
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'dist'),
},
+ module: {
+ rules: [
+ {
+ test: /\.css$/i,
+ use: ['style-loader', 'css-loader'],
+ },
+ ],
+ },
};
我们尝试一下,通过在项目中添加一个新的 style.css
文件,并将其 import 到我们的 index.js
中:
project
webpack-demo
|- package.json
|- webpack.config.js
|- /dist
|- bundle.js
|- index.html
|- /src
+ |- style.css
|- index.js
|- /node_modules
src/style.css
.hello {
color: red;
}
src/index.js
import _ from 'lodash';
+import './style.css';
function component() {
const element = document.createElement('div');
// Lodash, now imported by this script
element.innerHTML = _.join(['Hello', 'webpack'], ' ');
+ element.classList.add('hello');
return element;
}
document.body.appendChild(component());
现在运行 build 命令:
$ npm run build
3、加载 images 图像
假如,现在我们正在下载 CSS,但是像 background 和 icon 这样的图像,要如何处理呢?在 webpack 5 中,可以使用内置的 Asset Modules,我们可以轻松地将这些内容混入我们的系统中:
webpack.config.js
const path = require('path');
module.exports = {
entry: './src/index.js',
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'dist'),
},
module: {
rules: [
{
test: /\.css$/i,
use: ['style-loader', 'css-loader'],
},
+ {
+ test: /\.(png|svg|jpg|jpeg|gif)$/i,
+ type: 'asset/resource',
+ },
],
},
};
我们向项目添加一个图像,然后看它是如何工作的,你可以使用任何你喜欢的图像:
project
webpack-demo
|- package.json
|- webpack.config.js
|- /dist
|- bundle.js
|- index.html
|- /src
+ |- icon.png
|- style.css
|- index.js
|- /node_modules
src/index.js
import _ from 'lodash';
import './style.css';
+import Icon from './icon.png';
function component() {
const element = document.createElement('div');
// Lodash, now imported by this script
element.innerHTML = _.join(['Hello', 'webpack'], ' ');
element.classList.add('hello');
+ // 将图像添加到我们已经存在的 div 中。
+ const myIcon = new Image();
+ myIcon.src = Icon;
+
+ element.appendChild(myIcon);
+
return element;
}
document.body.appendChild(component());
src/style.css
.hello {
color: red;
+ background: url('./icon.png');
}
重新构建并再次打开 index.html
文件:
$ npm run build
4、加载 fonts 字体
那么,像字体这样的其他资源如何处理呢?使用 Asset Modules 可以接收并加载任何文件,然后将其输出到构建目录。这就是说,我们可以将它们用于任何类型的文件,也包括字体。让我们更新 webpack.config.js
来处理字体文件:
webpack.config.js
const path = require('path');
module.exports = {
entry: './src/index.js',
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'dist'),
},
module: {
rules: [
{
test: /\.css$/i,
use: ['style-loader', 'css-loader'],
},
{
test: /\.(png|svg|jpg|jpeg|gif)$/i,
type: 'asset/resource',
},
+ {
+ test: /\.(woff|woff2|eot|ttf|otf)$/i,
+ type: 'asset/resource',
+ },
],
},
};
在项目中添加一些字体文件:
project
webpack-demo
|- package.json
|- webpack.config.js
|- /dist
|- bundle.js
|- index.html
|- /src
+ |- my-font.woff
+ |- my-font.woff2
|- icon.png
|- style.css
|- index.js
|- /node_modules
配置好 loader 并将字体文件放在合适的位置后,你可以通过一个 @font-face
声明将其混合。本地的 url(...)
指令会被 webpack 获取处理,就像它处理图片一样:
src/style.css
+@font-face {
+ font-family: 'MyFont';
+ src: url('./my-font.woff2') format('woff2'),
+ url('./my-font.woff') format('woff');
+ font-weight: 600;
+ font-style: normal;
+}
+
.hello {
color: red;
+ font-family: 'MyFont';
background: url('./icon.png');
}
现在,让我们重新构建,然后看下 webpack 是否处理了我们的字体:
$ npm run build
三、管理输出
到目前为止,我们都是在 index.html
文件中手动引入所有资源,然而随着应用程序增长,并且一旦开始 在文件名中使用 hash 并输出 多个 bundle,如果继续手动管理 index.html
文件,就会变得困难起来。然而,通过一些插件可以使这个过程更容易管控。
1、预先准备
首先,调整一下我们的项目:
project
webpack-demo
|- package.json
|- webpack.config.js
|- /dist
|- /src
|- index.js
+ |- print.js
|- /node_modules
我们在 src/print.js
文件中添加一些逻辑:
src/print.js
export default function printMe() {
console.log('I get called from print.js!');
}
并且在 src/index.js
文件中使用这个函数:
src/index.js
import _ from 'lodash';
+import printMe from './print.js';
function component() {
const element = document.createElement('div');
+ const btn = document.createElement('button');
element.innerHTML = _.join(['Hello', 'webpack'], ' ');
+ btn.innerHTML = 'Click me and check the console!';
+ btn.onclick = printMe;
+
+ element.appendChild(btn);
+
return element;
}
document.body.appendChild(component());
还要更新 dist/index.html
文件,来为 webpack 分离入口做好准备:
dist/index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
- <title>管理资源</title>
+ <title>管理输出</title>
+ <script src="./print.bundle.js"></script>
</head>
<body>
- <script src="bundle.js"></script>
+ <script src="./index.bundle.js"></script>
</body>
</html>
现在调整配置。我们将在 entry 添加 src/print.js
作为新的入口起点(print
),然后修改 output,以便根据入口起点定义的名称,动态地产生 bundle 名称:
webpack.config.js
const path = require('path');
module.exports = {
- entry: './src/index.js',
+ entry: {
+ index: './src/index.js',
+ print: './src/print.js',
+ },
output: {
- filename: 'bundle.js',
+ filename: '[name].bundle.js',
path: path.resolve(__dirname, 'dist'),
},
};
执行 npm run build
我们可以看到,webpack 生成 print.bundle.js
和 index.bundle.js
文件,这也和我们在 index.html
文件中指定的文件名称相对应。如果你在浏览器中打开 index.html
,就可以看到在点击按钮时会发生什么。
但是,如果我们更改了我们的一个入口起点的名称,甚至添加了一个新的入口,会发生什么?会在构建时重新命名生成的 bundle,但是我们的 index.html
文件仍然引用旧的名称。让我们用 HtmlWebpackPlugin 来解决这个问题。
2、设置 HtmlWebpackPlugin
首先安装插件,并且调整 webpack.config.js
文件:
npm install --save-dev html-webpack-plugin
webpack.config.js
const path = require('path');
+const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
entry: {
index: './src/index.js',
print: './src/print.js',
},
+ plugins: [
+ new HtmlWebpackPlugin({
+ title: '管理输出',
+ }),
+ ],
output: {
filename: '[name].bundle.js',
path: path.resolve(__dirname, 'dist'),
},
};
在我们构建之前,你应该了解,虽然在 dist/
文件夹我们已经有了 index.html
这个文件,然而 HtmlWebpackPlugin
还是会默认生成它自己的 index.html
文件。也就是说,它会用新生成的 index.html
文件,替换我们的原有文件。
如果在代码编辑器中打开 index.html
,你会看到 HtmlWebpackPlugin
创建了一个全新的文件,所有的 bundle 会自动添加到 html 中。
3、清理 /dist
文件夹
你可能已经注意到,由于遗留了之前的指南和代码示例,我们的 /dist
文件夹显得相当杂乱。webpack 将生成文件并放置在 /dist
文件夹中,但是它不会追踪哪些文件是实际在项目中用到的。
通常比较推荐的做法是,在每次构建前清理 /dist
文件夹,这样只会生成用到的文件。让我们实现这个需求。
clean-webpack-plugin 是一个流行的清理插件,安装和配置它。
npm install --save-dev clean-webpack-plugin
webpack.config.js
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
+const { CleanWebpackPlugin } = require('clean-webpack-plugin');
module.exports = {
entry: {
index: './src/index.js',
print: './src/print.js',
},
plugins: [
+ new CleanWebpackPlugin(),
new HtmlWebpackPlugin({
title: 'Output Management',
}),
],
output: {
filename: '[name].bundle.js',
path: path.resolve(__dirname, 'dist'),
},
};
现在,执行 npm run build
,检查 /dist
文件夹。如果一切顺利,现在只会看到构建后生成的文件,而没有旧文件!
4、manifest
你可能会很感兴趣,webpack 和 webpack 插件似乎“知道”应该生成哪些文件。答案是,webpack 通过 manifest,可以追踪所有模块到输出 bundle 之间的映射。如果你想要知道如何以其他方式来控制 webpack 输出,了解 manifest 是个好的开始。
通过 WebpackManifestPlugin 插件,可以将 manifest 数据提取为一个容易使用的 json 文件。
首先安装 webpack-manifest-plugin:
npm install webpack-manifest-plugin --save-dev
webpack.config.js
const path = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const { CleanWebpackPlugin } = require('clean-webpack-plugin')
+const { WebpackManifestPlugin } = require('webpack-manifest-plugin')
module.exports = {
mode: 'development',
entry: {
index: './src/index.js',
print: './src/print.js',
},
plugins: [
new CleanWebpackPlugin(),
new HtmlWebpackPlugin({
title: '管理输出',
}),
+ new WebpackManifestPlugin()
],
output: {
filename: '[name].bundle.js',
path: path.resolve(__dirname, 'dist'),
}
};
现在,执行 npm run build
,会发现 dist/manifest.json
文件,文件内容如下:
{
"index.js": "autoindex.bundle.js",
"print.js": "autoprint.bundle.js",
"index.html": "autoindex.html"
}
四、开发环境
如果你一直跟随之前的指南,应该对一些 webpack 基础知识有着很扎实的理解。在我们继续之前,先来看看如何设置一个开发环境,使我们的开发体验变得更轻松一些。
在开始前,我们先将 mode 设置为 'development',并将 title
设置为 'Development'
。
webpack.config.js
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
module.exports = {
+ mode: 'development',
entry: {
index: './src/index.js',
print: './src/print.js',
},
plugins: [
new CleanWebpackPlugin(),
new HtmlWebpackPlugin({
- title: 'Output Management',
+ title: 'Development',
}),
],
output: {
filename: '[name].bundle.js',
path: path.resolve(__dirname, 'dist'),
},
};
1、使用 source-map
当 webpack 打包源代码时,可能会很难追踪到 error(错误) 和 warning(警告) 在源代码中的原始位置。例如,如果将三个源文件(a.js
, b.js
和 c.js
)打包到一个 bundle(bundle.js
)中,而其中一个源文件包含一个错误,那么堆栈跟踪就会直接指向到 bundle.js
。你可能需要准确地知道错误来自于哪个源文件,所以这种提示这通常不会提供太多帮助。
为了更容易地追踪 error 和 warning,JavaScript 提供了 source maps 功能,可以将编译后的代码映射回原始源代码。如果一个错误来自于 b.js
,source map 就会明确的告诉你。
(1) devtool 配置说明表
devtool | performance | production | quality | comment |
(none) | build: fastest rebuild: fastest | yes | bundle | Recommended choice for production builds with maximum performance. |
| build: fast rebuild: fastest | no | generated | Recommended choice for development builds with maximum performance. |
| build: ok rebuild: fast | no | transformed | Tradeoff choice for development builds. |
| build: slow rebuild: fast | no | original lines | Tradeoff choice for development builds. |
| build: slowest rebuild: ok | no | original | Recommended choice for development builds with high quality SourceMaps. |
| build: ok rebuild: slow | no | transformed | |
| build: slow rebuild: slow | no | original lines | |
| build: slowest rebuild: slowest | yes | original | Recommended choice for production builds with high quality SourceMaps. |
| build: ok rebuild: slow | no | transformed | |
| build: slow rebuild: slow | no | original lines | |
| build: slowest rebuild: slowest | no | original | Possible choice when publishing a single file |
| build: ok rebuild: fast | no | transformed | source code not included |
| build: slow rebuild: fast | no | original lines | source code not included |
| build: slowest rebuild: ok | no | original | source code not included |
| build: ok rebuild: slow | no | transformed | source code not included |
| build: slow rebuild: slow | no | original lines | source code not included |
| build: slowest rebuild: slowest | no | original | source code not included |
| build: ok rebuild: slow | no | transformed | source code not included |
| build: slow rebuild: slow | no | original lines | source code not included |
| build: slowest rebuild: slowest | yes | original | source code not included |
| build: ok rebuild: slow | no | transformed | no reference, source code not included |
| build: slow rebuild: slow | no | original lines | no reference, source code not included |
| build: slowest rebuild: slowest | yes | original | no reference, source code not included |
| build: ok rebuild: slow | no | transformed | no reference |
| build: slow rebuild: slow | no | original lines | no reference |
| build: slowest rebuild: slowest | yes | original | no reference. Possible choice when using SourceMap only for error reporting purposes. |
- 开发环境推荐配置
以下选项非常适合开发环境:eval
- 每个模块都使用eval()
执行,并且都有//@ sourceURL
。此选项会非常快地构建。主要缺点是,由于会映射到转换后的代码,而不是映射到原始代码(没有从 loader 中获取 source map),所以不能正确的显示行数。eval-source-map
- 每个模块使用eval()
执行,并且 source map 转换为 DataUrl 后添加到eval()
中。初始化 source map 时比较慢,但是会在重新构建时提供比较快的速度,并且生成实际的文件。行数能够正确映射,因为会映射到原始代码中。它会生成用于开发环境的最佳品质的 source map。eval-cheap-source-map
- 类似eval-source-map
,每个模块使用eval()
执行。这是 “cheap(低开销)” 的 source map,因为它没有生成列映射(column mapping),只是映射行数。它会忽略源自 loader 的 source map,并且仅显示转译后的代码,就像eval
devtool。eval-cheap-module-source-map
- 类似eval-cheap-source-map
,并且,在这种情况下,源自 loader 的 source map 会得到更好的处理结果。然而,loader source map 会被简化为每行一个映射(mapping)。 - 生产环境推荐配置
这些选项通常用于生产环境中:(none)
(省略devtool
选项) - 不生成 source map。这是一个不错的选择。source-map
- 整个 source map 作为一个单独的文件生成。它为 bundle 添加了一个引用注释,以便开发工具知道在哪里可以找到它。hidden-source-map
- 与source-map
相同,但不会为 bundle 添加引用注释。如果你只想 source map 映射那些源自错误报告的错误堆栈跟踪信息,但不想为浏览器开发工具暴露你的 source map,这个选项会很有用。nosources-source-map
- 创建的 source map 不包含sourcesContent(源代码内容)
。它可以用来映射客户端上的堆栈跟踪,而无须暴露所有的源代码。你可以将 source map 文件部署到 web 服务器。
(2) 配置示例
webpack 仓库中包含一个 显示所有 devtool。这些例子或许会有助于你理解这些差异之处。
example.coffee
# Taken from http://coffeescript.org/
# Objects:
math =
root: Math.sqrt
square: square
cube: (x) -> x * square x
# Splats:
race = (winner, runners...) ->
print winner, runners
webpack.config.js
var path = require("path");
module.exports = [
"eval",
"eval-cheap-source-map",
"eval-cheap-module-source-map",
"eval-source-map",
"cheap-source-map",
"cheap-module-source-map",
"inline-cheap-source-map",
"inline-cheap-module-source-map",
"source-map",
"inline-source-map",
"hidden-source-map",
"nosources-source-map"
].map(devtool => ({
mode: "development",
entry: {
bundle: "coffee-loader!./example.coffee"
},
output: {
path: path.join(__dirname, "dist"),
filename: `./[name]-${devtool}.js`
},
devtool,
optimization: {
runtimeChunk: true
}
}));
Generated source-maps
- source-map.js and source-map.js.map
(self["webpackChunk"] = self["webpackChunk"] || []).push([[0],[
/* 0 */
/*!*********************************************************************!*\
!*** ../../node_modules/coffee-loader/dist/cjs.js!./example.coffee ***!
\*********************************************************************/
/*! unknown exports (runtime-defined) */
/*! runtime requirements: */
/***/ (() => {
// Taken from http://coffeescript.org/
// Objects:
var math, race;
math = {
root: Math.sqrt,
square: square,
cube: function(x) {
return x * square(x);
}
};
// Splats:
race = function(winner, ...runners) {
return print(winner, runners);
};
/***/ })
],
0,[[0,1]]]);
//# sourceMappingURL=bundle-source-map.js.map
{"version":3,"sources":["webpack:///./example.coffee"],"names":[],"mappings":";;;;;;;;;AAEU;;;AAAA;;AACV,OACE;EAAA,MAAQ,IAAI,CAAC,IAAb;EACA,QAAQ,MADR;EAEA,MAAQ,SAAC,CAAD;WAAO,IAAI,OAAO,CAAP;EAAX;AAFR,EAFQ;;;AAOV,OAAO,SAAC,MAAD,KAAS,OAAT;SACL,MAAM,MAAN,EAAc,OAAd;AADK","file":"./bundle-source-map.js","sourcesContent":["# Taken from http://coffeescript.org/\n\n# Objects:\nmath =\n root: Math.sqrt\n square: square\n cube: (x) -> x * square x\n\n# Splats:\nrace = (winner, runners...) ->\n print winner, runners\n"],"sourceRoot":""}
- hidden-source-map.js and hidden-source-map.js.map
(self["webpackChunk"] = self["webpackChunk"] || []).push([[0],[
/* 0 */
/*!*********************************************************************!*\
!*** ../../node_modules/coffee-loader/dist/cjs.js!./example.coffee ***!
\*********************************************************************/
/*! unknown exports (runtime-defined) */
/*! runtime requirements: */
/***/ (() => {
// Taken from http://coffeescript.org/
// Objects:
var math, race;
math = {
root: Math.sqrt,
square: square,
cube: function(x) {
return x * square(x);
}
};
// Splats:
race = function(winner, ...runners) {
return print(winner, runners);
};
/***/ })
],
0,[[0,1]]]);
{"version":3,"sources":["webpack:///./example.coffee"],"names":[],"mappings":";;;;;;;;;AAEU;;;AAAA;;AACV,OACE;EAAA,MAAQ,IAAI,CAAC,IAAb;EACA,QAAQ,MADR;EAEA,MAAQ,SAAC,CAAD;WAAO,IAAI,OAAO,CAAP;EAAX;AAFR,EAFQ;;;AAOV,OAAO,SAAC,MAAD,KAAS,OAAT;SACL,MAAM,MAAN,EAAc,OAAd;AADK","file":"./bundle-hidden-source-map.js","sourcesContent":["# Taken from http://coffeescript.org/\n\n# Objects:\nmath =\n root: Math.sqrt\n square: square\n cube: (x) -> x * square x\n\n# Splats:\nrace = (winner, runners...) ->\n print winner, runners\n"],"sourceRoot":""}
- inline-source-map.js
(self["webpackChunk"] = self["webpackChunk"] || []).push([[0],[
/* 0 */
/*!*********************************************************************!*\
!*** ../../node_modules/coffee-loader/dist/cjs.js!./example.coffee ***!
\*********************************************************************/
/*! unknown exports (runtime-defined) */
/*! runtime requirements: */
/***/ (() => {
// Taken from http://coffeescript.org/
// Objects:
var math, race;
math = {
root: Math.sqrt,
square: square,
cube: function(x) {
return x * square(x);
}
};
// Splats:
race = function(winner, ...runners) {
return print(winner, runners);
};
/***/ })
],
0,[[0,1]]]);
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIndlYnBhY2s6Ly8vLi9leGFtcGxlLmNvZmZlZSJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7Ozs7Ozs7QUFFVTs7O0FBQUE7O0FBQ1YsT0FDRTtFQUFBLE1BQVEsSUFBSSxDQUFDLElBQWI7RUFDQSxRQUFRLE1BRFI7RUFFQSxNQUFRLFNBQUMsQ0FBRDtXQUFPLElBQUksT0FBTyxDQUFQO0VBQVg7QUFGUixFQUZROzs7QUFPVixPQUFPLFNBQUMsTUFBRCxLQUFTLE9BQVQ7U0FDTCxNQUFNLE1BQU4sRUFBYyxPQUFkO0FBREsiLCJmaWxlIjoiLi9idW5kbGUtaW5saW5lLXNvdXJjZS1tYXAuanMiLCJzb3VyY2VzQ29udGVudCI6WyIjIFRha2VuIGZyb20gaHR0cDovL2NvZmZlZXNjcmlwdC5vcmcvXG5cbiMgT2JqZWN0czpcbm1hdGggPVxuICByb290OiAgIE1hdGguc3FydFxuICBzcXVhcmU6IHNxdWFyZVxuICBjdWJlOiAgICh4KSAtPiB4ICogc3F1YXJlIHhcblxuIyBTcGxhdHM6XG5yYWNlID0gKHdpbm5lciwgcnVubmVycy4uLikgLT5cbiAgcHJpbnQgd2lubmVyLCBydW5uZXJzXG4iXSwic291cmNlUm9vdCI6IiJ9
- nosources-source-map.js.map
{"version":3,"sources":["webpack:///./example.coffee"],"names":[],"mappings":";;;;;;;;;AAEU;;;AAAA;;AACV,OACE;EAAA,MAAQ,IAAI,CAAC,IAAb;EACA,QAAQ,MADR;EAEA,MAAQ,SAAC,CAAD;WAAO,IAAI,OAAO,CAAP;EAAX;AAFR,EAFQ;;;AAOV,OAAO,SAAC,MAAD,KAAS,OAAT;SACL,MAAM,MAAN,EAAc,OAAd;AADK","file":"./bundle-nosources-source-map.js","sourceRoot":""}
- eval-source-map.js
/*
* ATTENTION: An "eval-source-map" devtool has been used.
* This devtool is neither made for production nor for readable output files.
* It uses "eval()" calls to create a separate source file with attached SourceMaps in the browser devtools.
* If you are trying to read the output file, select a different devtool (https://webpack.js.org/configuration/devtool/)
* or disable the default devtool with "devtool: false".
* If you are looking for production-ready output files, see mode: "production" (https://webpack.js.org/configuration/mode/).
*/
(self["webpackChunk"] = self["webpackChunk"] || []).push([[0],[
/* 0 */
/*!*********************************************************************!*\
!*** ../../node_modules/coffee-loader/dist/cjs.js!./example.coffee ***!
\*********************************************************************/
/*! unknown exports (runtime-defined) */
/*! runtime requirements: */
/***/ (() => {
eval("// Taken from http://coffeescript.org/\n\n// Objects:\nvar math, race;\n\nmath = {\n root: Math.sqrt,\n square: square,\n cube: function(x) {\n return x * square(x);\n }\n};\n\n// Splats:\nrace = function(winner, ...runners) {\n return print(winner, runners);\n};\n//# sourceURL=[module]\n//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyJ3ZWJwYWNrOi8vLy4vZXhhbXBsZS5jb2ZmZWU/MjQxNiJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFFVTs7O0FBQUEsSUFBQSxJQUFBLEVBQUE7O0FBQ1YsSUFBQSxHQUNFO0VBQUEsSUFBQSxFQUFRLElBQUksQ0FBQyxJQUFiO0VBQ0EsTUFBQSxFQUFRLE1BRFI7RUFFQSxJQUFBLEVBQVEsUUFBQSxDQUFDLENBQUQsQ0FBQTtXQUFPLENBQUEsR0FBSSxNQUFBLENBQU8sQ0FBUDtFQUFYO0FBRlIsRUFGUTs7O0FBT1YsSUFBQSxHQUFPLFFBQUEsQ0FBQyxNQUFELEVBQUEsR0FBUyxPQUFULENBQUE7U0FDTCxLQUFBLENBQU0sTUFBTixFQUFjLE9BQWQ7QUFESyIsInNvdXJjZXNDb250ZW50IjpbIiMgVGFrZW4gZnJvbSBodHRwOi8vY29mZmVlc2NyaXB0Lm9yZy9cblxuIyBPYmplY3RzOlxubWF0aCA9XG4gIHJvb3Q6ICAgTWF0aC5zcXJ0XG4gIHNxdWFyZTogc3F1YXJlXG4gIGN1YmU6ICAgKHgpIC0+IHggKiBzcXVhcmUgeFxuXG4jIFNwbGF0czpcbnJhY2UgPSAod2lubmVyLCBydW5uZXJzLi4uKSAtPlxuICBwcmludCB3aW5uZXIsIHJ1bm5lcnNcbiJdLCJmaWxlIjoiMC5qcyJ9\n//# sourceURL=webpack-internal:///0\n");
/***/ })
],
0,[[0,1]]]);
- eval.js
/*
* ATTENTION: The "eval" devtool has been used (maybe by default in mode: "development").
* This devtool is neither made for production nor for readable output files.
* It uses "eval()" calls to create a separate source file in the browser devtools.
* If you are trying to read the output file, select a different devtool (https://webpack.js.org/configuration/devtool/)
* or disable the default devtool with "devtool: false".
* If you are looking for production-ready output files, see mode: "production" (https://webpack.js.org/configuration/mode/).
*/
(self["webpackChunk"] = self["webpackChunk"] || []).push([[0],[
/* 0 */
/*!*********************************************************************!*\
!*** ../../node_modules/coffee-loader/dist/cjs.js!./example.coffee ***!
\*********************************************************************/
/*! unknown exports (runtime-defined) */
/*! runtime requirements: */
/***/ (() => {
eval("// Taken from http://coffeescript.org/\n\n// Objects:\nvar math, race;\n\nmath = {\n root: Math.sqrt,\n square: square,\n cube: function(x) {\n return x * square(x);\n }\n};\n\n// Splats:\nrace = function(winner, ...runners) {\n return print(winner, runners);\n};\n\n\n//# sourceURL=webpack:///./example.coffee?../../node_modules/coffee-loader/dist/cjs.js");
/***/ })
],
0,[[0,1]]]);
- eval-cheap-source-map.js
/*
* ATTENTION: An "eval-source-map" devtool has been used.
* This devtool is neither made for production nor for readable output files.
* It uses "eval()" calls to create a separate source file with attached SourceMaps in the browser devtools.
* If you are trying to read the output file, select a different devtool (https://webpack.js.org/configuration/devtool/)
* or disable the default devtool with "devtool: false".
* If you are looking for production-ready output files, see mode: "production" (https://webpack.js.org/configuration/mode/).
*/
(self["webpackChunk"] = self["webpackChunk"] || []).push([[0],[
/* 0 */
/*!*********************************************************************!*\
!*** ../../node_modules/coffee-loader/dist/cjs.js!./example.coffee ***!
\*********************************************************************/
/*! unknown exports (runtime-defined) */
/*! runtime requirements: */
/***/ (() => {
eval("// Taken from http://coffeescript.org/\n\n// Objects:\nvar math, race;\n\nmath = {\n root: Math.sqrt,\n square: square,\n cube: function(x) {\n return x * square(x);\n }\n};\n\n// Splats:\nrace = function(winner, ...runners) {\n return print(winner, runners);\n};\n//# sourceURL=[module]\n//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiMC5qcyIsInNvdXJjZXMiOlsid2VicGFjazovLy8uL2V4YW1wbGUuY29mZmVlP2VlNTgiXSwic291cmNlc0NvbnRlbnQiOlsiLy8gVGFrZW4gZnJvbSBodHRwOi8vY29mZmVlc2NyaXB0Lm9yZy9cblxuLy8gT2JqZWN0czpcbnZhciBtYXRoLCByYWNlO1xuXG5tYXRoID0ge1xuICByb290OiBNYXRoLnNxcnQsXG4gIHNxdWFyZTogc3F1YXJlLFxuICBjdWJlOiBmdW5jdGlvbih4KSB7XG4gICAgcmV0dXJuIHggKiBzcXVhcmUoeCk7XG4gIH1cbn07XG5cbi8vIFNwbGF0czpcbnJhY2UgPSBmdW5jdGlvbih3aW5uZXIsIC4uLnJ1bm5lcnMpIHtcbiAgcmV0dXJuIHByaW50KHdpbm5lciwgcnVubmVycyk7XG59O1xuIl0sIm1hcHBpbmdzIjoiQUFBQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOyIsInNvdXJjZVJvb3QiOiIifQ==\n//# sourceURL=webpack-internal:///0\n");
/***/ })
],
0,[[0,1]]]);
- eval-cheap-module-source-map.js
/*
* ATTENTION: An "eval-source-map" devtool has been used.
* This devtool is neither made for production nor for readable output files.
* It uses "eval()" calls to create a separate source file with attached SourceMaps in the browser devtools.
* If you are trying to read the output file, select a different devtool (https://webpack.js.org/configuration/devtool/)
* or disable the default devtool with "devtool: false".
* If you are looking for production-ready output files, see mode: "production" (https://webpack.js.org/configuration/mode/).
*/
(self["webpackChunk"] = self["webpackChunk"] || []).push([[0],[
/* 0 */
/*!*********************************************************************!*\
!*** ../../node_modules/coffee-loader/dist/cjs.js!./example.coffee ***!
\*********************************************************************/
/*! unknown exports (runtime-defined) */
/*! runtime requirements: */
/***/ (() => {
eval("// Taken from http://coffeescript.org/\n\n// Objects:\nvar math, race;\n\nmath = {\n root: Math.sqrt,\n square: square,\n cube: function(x) {\n return x * square(x);\n }\n};\n\n// Splats:\nrace = function(winner, ...runners) {\n return print(winner, runners);\n};\n//# sourceURL=[module]\n//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyJ3ZWJwYWNrOi8vLy4vZXhhbXBsZS5jb2ZmZWU/MjQxNiJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFFVTs7O0FBQUEsSUFBQSxJQUFBLEVBQUE7O0FBQ1YsSUFBQSxHQUNFO0VBQUEsSUFBQSxFQUFRLElBQUksQ0FBQyxJQUFiO0VBQ0EsTUFBQSxFQUFRLE1BRFI7RUFFQSxJQUFBLEVBQVEsUUFBQSxDQUFDLENBQUQsQ0FBQTtXQUFPLENBQUEsR0FBSSxNQUFBLENBQU8sQ0FBUDtFQUFYO0FBRlIsRUFGUTs7O0FBT1YsSUFBQSxHQUFPLFFBQUEsQ0FBQyxNQUFELEVBQUEsR0FBUyxPQUFULENBQUE7U0FDTCxLQUFBLENBQU0sTUFBTixFQUFjLE9BQWQ7QUFESyIsInNvdXJjZXNDb250ZW50IjpbIiMgVGFrZW4gZnJvbSBodHRwOi8vY29mZmVlc2NyaXB0Lm9yZy9cblxuIyBPYmplY3RzOlxubWF0aCA9XG4gIHJvb3Q6ICAgTWF0aC5zcXJ0XG4gIHNxdWFyZTogc3F1YXJlXG4gIGN1YmU6ICAgKHgpIC0+IHggKiBzcXVhcmUgeFxuXG4jIFNwbGF0czpcbnJhY2UgPSAod2lubmVyLCBydW5uZXJzLi4uKSAtPlxuICBwcmludCB3aW5uZXIsIHJ1bm5lcnNcbiJdLCJmaWxlIjoiMC5qcyJ9\n//# sourceURL=webpack-internal:///0\n");
/***/ })
],
0,[[0,1]]]);
- cheap-module-source-map.js.map
{"version":3,"file":"./bundle-cheap-module-source-map.js","sources":["webpack:///./example.coffee"],"sourcesContent":["# Taken from http://coffeescript.org/\n\n# Objects:\nmath =\n root: Math.sqrt\n square: square\n cube: (x) -> x * square x\n\n# Splats:\nrace = (winner, runners...) ->\n print winner, runners\n"],"mappings":";;;;;;;;;AAEA;AACA;;AADA;AACA;AAAA;AACA;AACA;AACA;AAAA;AAAA;AAFA;AACA;;AAIA;AACA;AADA;AACA;AACA;A;;A","sourceRoot":""}
- cheap-source-map.js.map
{"version":3,"file":"./bundle-cheap-source-map.js","sources":["webpack:///./example.coffee"],"sourcesContent":["// Taken from http://coffeescript.org/\n\n// Objects:\nvar math, race;\n\nmath = {\n root: Math.sqrt,\n square: square,\n cube: function(x) {\n return x * square(x);\n }\n};\n\n// Splats:\nrace = function(winner, ...runners) {\n return print(winner, runners);\n};\n"],"mappings":";;;;;;;;;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;A;;A","sourceRoot":""}
(3) 演练
对于本指南,我们将使用 inline-source-map
选项,这有助于解释说明示例意图(此配置仅用于示例,不要用于生产环境):
webpack.config.js
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
module.exports = {
mode: 'development',
entry: {
index: './src/index.js',
print: './src/print.js',
},
+ devtool: 'inline-source-map',
plugins: [
new CleanWebpackPlugin(),
new HtmlWebpackPlugin({
title: 'Development',
}),
],
output: {
filename: '[name].bundle.js',
path: path.resolve(__dirname, 'dist'),
},
};
现在,让我们来做一些调试,在 print.js
文件中生成一个错误:
src/print.js
export default function printMe() {
- console.log('I get called from print.js!');
+ cosnole.log('I get called from print.js!');
}
**运行 npm run build**
现在,在浏览器中打开生成的 index.html
文件,点击按钮,并且在控制台查看显示的错误。错误应该如下:
Uncaught ReferenceError: cosnole is not defined
at HTMLButtonElement.printMe (print.js:2)
我们可以看到,此错误包含有发生错误的文件(print.js
)和行号(2)的引用。这是非常有帮助的,因为现在我们可以确切地知道,所要解决问题的位置。
2、选择一个开发工具
在每次编译代码时,手动运行 npm run build
会显得很麻烦。
webpack 提供几种可选方式,帮助你在代码发生变化后自动编译代码:
多数场景中,你可能需要使用 webpack-dev-server
,但是不妨探讨一下以上的所有选项。
(1) 使用 watch mode(观察模式)
你可以指示 webpack “watch” 依赖图中所有文件的更改。如果其中一个文件被更新,代码将被重新编译,所以你不必再去手动运行整个构建。
我们添加一个用于启动 webpack watch mode 的 npm scripts:
package.json
{
"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": {
"clean-webpack-plugin": "^3.0.0",
"html-webpack-plugin": "^4.5.0",
"webpack": "^5.4.0",
"webpack-cli": "^4.2.0"
},
"dependencies": {
"lodash": "^4.17.20"
}
}
如果不想在 watch 触发增量构建后删除 index.html
文件,可以在 CleanWebpackPlugin
中配置 cleanStaleWebpackAssets 来实现:
webpack.config.js
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
module.exports = {
mode: 'development',
entry: {
index: './src/index.js',
print: './src/print.js',
},
devtool: 'inline-source-map',
plugins: [
- new CleanWebpackPlugin(),
+ new CleanWebpackPlugin({ cleanStaleWebpackAssets: false }),
new HtmlWebpackPlugin({
title: 'Development',
}),
],
output: {
filename: '[name].bundle.js',
path: path.resolve(__dirname, 'dist'),
},
};
现在,你可以在命令行中运行 npm run watch
,然后就会看到 webpack 是如何编译代码。 然而,你会发现并没有退出命令行。这是因为此 script 当前还在 watch 你的文件。
现在,webpack 观察文件的同时,先移除我们之前加入的错误:
src/print.js
export default function printMe() {
- cosnole.log('I get called from print.js!');
+ console.log('I get called from print.js!');
}
现在,保存文件并检查 terminal(终端) 窗口。应该可以看到 webpack 自动地重新编译修改后的模块!
唯一的缺点是,为了看到修改后的实际效果,你需要刷新浏览器。如果能够自动刷新浏览器就更好了,因此接下来我们会尝试通过 webpack-dev-server
实现此功能。
(2) 使用 webpack-dev-server
webpack-dev-server
为你提供了一个简单的 web server,并且具有 live reloading(实时重新加载) 功能。设置如下:
npm install --save-dev webpack-dev-server
修改配置文件,告知 dev server,从什么位置查找文件:
webpack.config.js
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
module.exports = {
mode: 'development',
entry: {
index: './src/index.js',
print: './src/print.js',
},
devtool: 'inline-source-map',
+ devServer: {
+ contentBase: './dist',
+ },
plugins: [
new CleanWebpackPlugin({ cleanStaleWebpackAssets: false }),
new HtmlWebpackPlugin({
title: 'Development',
}),
],
output: {
filename: '[name].bundle.js',
path: path.resolve(__dirname, 'dist'),
},
};
以上配置告知 webpack-dev-server
,将 dist
目录下的文件 serve 到 localhost:8080
下。
我们添加一个可以直接运行 dev server 的 script:
package.json
{
"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": {
"clean-webpack-plugin": "^3.0.0",
"html-webpack-plugin": "^4.5.0",
"webpack": "^5.4.0",
"webpack-cli": "^4.2.0",
"webpack-dev-server": "^3.11.0"
},
"dependencies": {
"lodash": "^4.17.20"
}
}
现在,在命令行中运行 npm start
,我们会看到浏览器自动加载页面。如果你更改任何源文件并保存它们,web server 将在编译代码后自动重新加载。试试看!
(3) 使用 webpack-dev-middleware
webpack-dev-middleware
是一个封装器(wrapper),它可以把 webpack 处理过的文件发送到一个 server。 webpack-dev-server
在内部使用了它,然而它也可以作为一个单独的 package 来使用,以便根据需求进行更多自定义设置。下面是一个 webpack-dev-middleware 配合 express server 的示例。
首先,安装 express
和 webpack-dev-middleware
:
npm install --save-dev express webpack-dev-middleware
现在,我们需要调整 webpack 配置文件,以确保 middleware(中间件) 功能能够正确启用:
webpack.config.js
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
module.exports = {
mode: 'development',
entry: {
index: './src/index.js',
print: './src/print.js',
},
devtool: 'inline-source-map',
devServer: {
contentBase: './dist',
},
plugins: [
new CleanWebpackPlugin({ cleanStaleWebpackAssets: false }),
new HtmlWebpackPlugin({
title: 'Development',
}),
],
output: {
filename: '[name].bundle.js',
path: path.resolve(__dirname, 'dist'),
+ publicPath: '/',
},
};
我们将会在 server 脚本使用 publicPath
,以确保文件资源能够正确地 serve 在 http://localhost:3000
下,稍后我们会指定 port number(端口号)。接下来是设置自定义 express
server:
project
webpack-demo
|- package.json
|- webpack.config.js
+ |- server.js
|- /dist
|- /src
|- index.js
|- print.js
|- /node_modules
server.js
const express = require('express');
const webpack = require('webpack');
const webpackDevMiddleware = require('webpack-dev-middleware');
const app = express();
const config = require('./webpack.config.js');
const compiler = webpack(config);
// 告知 express 使用 webpack-dev-middleware,
// 以及将 webpack.config.js 配置文件作为基础配置。
app.use(
webpackDevMiddleware(compiler, {
publicPath: config.output.publicPath,
})
);
// 将文件 serve 到 port 3000。
app.listen(3000, function () {
console.log('Example app listening on port 3000!\n');
});
现在,添加一个 npm script,以使我们更方便地运行 server:
package.json
{
"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": {
"clean-webpack-plugin": "^3.0.0",
"express": "^4.17.1",
"html-webpack-plugin": "^4.5.0",
"webpack": "^5.4.0",
"webpack-cli": "^4.2.0",
"webpack-dev-middleware": "^4.0.2",
"webpack-dev-server": "^3.11.0"
},
"dependencies": {
"lodash": "^4.17.20"
}
}
现在,在 terminal(终端) 中执行 npm run server
,将会有类似如下信息输出:
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.4.0 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.4.0 compiled successfully in 19 ms
<i> [webpack-dev-middleware] Compiled successfully.
现在,打开浏览器,访问 http://localhost:3000
。应该看到 webpack 应用程序已经运行!