开发环境

本指南继续沿用 管理输出 指南中的代码示例。

如果你一直跟随之前的指南,应该对一些 webpack 基础知识有着很扎实的理解。在我们继续之前,先来看看如何设置一个开发环境,使我们的开发体验变得更轻松一些。

本指南中的工具仅用于开发环境,请不要在生产环境中使用它们!

在开始前,我们先将 mode 设置为 'development'

webpack.config.js

  1. const path = require('path');
  2. const HtmlWebpackPlugin = require('html-webpack-plugin');
  3. const CleanWebpackPlugin = require('clean-webpack-plugin');
  4. module.exports = {
  5. + mode: 'development',
  6. entry: {
  7. app: './src/index.js',
  8. print: './src/print.js'
  9. },
  10. plugins: [
  11. new CleanWebpackPlugin(['dist']),
  12. new HtmlWebpackPlugin({
  13. title: '开发环境'
  14. })
  15. ],
  16. output: {
  17. filename: '[name].bundle.js',
  18. path: path.resolve(__dirname, 'dist')
  19. }
  20. };

使用 source map

当 webpack 打包源代码时,可能会很难追踪到 error(错误) 和 warning(警告) 在源代码中的原始位置。例如,如果将三个源文件(a.js, b.jsc.js)打包到一个 bundle(bundle.js)中,而其中一个源文件包含一个错误,那么堆栈跟踪就会直接指向到 bundle.js。你可能需要准确地知道错误来自于哪个源文件,所以这种提示这通常不会提供太多帮助。

为了更容易地追踪 error 和 warning,JavaScript 提供了 source map 功能,可以将编译后的代码映射回原始源代码。如果一个错误来自于 b.js,source map 就会明确的告诉你。

source map 有许多 可用选项,请务必仔细阅读它们,以便可以根据需要进行配置。

对于本指南,我们将使用 inline-source-map 选项,这有助于解释说明示例意图(此配置仅用于示例,不要用于生产环境):

webpack.config.js

  1. const path = require('path');
  2. const HtmlWebpackPlugin = require('html-webpack-plugin');
  3. const CleanWebpackPlugin = require('clean-webpack-plugin');
  4. module.exports = {
  5. mode: 'development',
  6. entry: {
  7. app: './src/index.js',
  8. print: './src/print.js'
  9. },
  10. + devtool: 'inline-source-map',
  11. plugins: [
  12. new CleanWebpackPlugin(['dist']),
  13. new HtmlWebpackPlugin({
  14. title: 'Development'
  15. })
  16. ],
  17. output: {
  18. filename: '[name].bundle.js',
  19. path: path.resolve(__dirname, 'dist')
  20. }
  21. };

现在,让我们来做一些调试,在 print.js 文件中生成一个错误:

src/print.js

  1. export default function printMe() {
  2. - console.log('I get called from print.js!');
  3. + cosnole.log('I get called from print.js!');
  4. }

运行 npm run build,编译如下:

  1. ...
  2. Asset Size Chunks Chunk Names
  3. app.bundle.js 1.44 MB 0, 1 [emitted] [big] app
  4. print.bundle.js 6.43 kB 1 [emitted] print
  5. index.html 248 bytes [emitted]
  6. ...

现在,在浏览器中打开生成的 index.html 文件,点击按钮,并且在控制台查看显示的错误。错误应该如下:

  1. Uncaught ReferenceError: cosnole is not defined
  2. at HTMLButtonElement.printMe (print.js:2)

我们可以看到,此错误包含有发生错误的文件(print.js)和行号(2)的引用。这是非常有帮助的,因为现在我们可以确切地知道,所要解决问题的位置。

选择一个开发工具

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

在每次编译代码时,手动运行 npm run build 会显得很麻烦。

webpack 提供几种可选方式,帮助你在代码发生变化后自动编译代码:

NaN. webpack watch mode(webpack 观察模式) NaN. webpack-dev-server NaN. webpack-dev-middleware

多数场景中,你可能需要使用 webpack-dev-server,但是不妨探讨一下以上的所有选项。

使用 watch mode(观察模式)

你可以指示 webpack “watch” 依赖图中所有文件的更改。如果其中一个文件被更新,代码将被重新编译,所以你不必再去手动运行整个构建。

我们添加一个用于启动 webpack watch mode 的 npm scripts:

package.json

  1. {
  2. "name": "development",
  3. "version": "1.0.0",
  4. "description": "",
  5. "main": "webpack.config.js",
  6. "scripts": {
  7. "test": "echo \"Error: no test specified\" && exit 1",
  8. + "watch": "webpack --watch",
  9. "build": "webpack"
  10. },
  11. "keywords": [],
  12. "author": "",
  13. "license": "ISC",
  14. "devDependencies": {
  15. "clean-webpack-plugin": "^0.1.16",
  16. "css-loader": "^0.28.4",
  17. "csv-loader": "^2.1.1",
  18. "file-loader": "^0.11.2",
  19. "html-webpack-plugin": "^2.29.0",
  20. "style-loader": "^0.18.2",
  21. "webpack": "^3.0.0",
  22. "xml-loader": "^1.2.1"
  23. }
  24. }

现在,你可以在命令行中运行 npm run watch,然后就会看到 webpack 是如何编译代码。 然而,你会发现并没有退出命令行。这是因为此 script 当前还在 watch 你的文件。

现在,webpack 观察文件的同时,先移除我们之前加入的错误:

src/print.js

  1. export default function printMe() {
  2. - cosnole.log('I get called from print.js!');
  3. + console.log('I get called from print.js!');
  4. }

现在,保存文件并检查 terminal(终端) 窗口。应该可以看到 webpack 自动地重新编译修改后的模块!

唯一的缺点是,为了看到修改后的实际效果,你需要刷新浏览器。如果能够自动刷新浏览器就更好了,因此接下来我们会尝试通过 webpack-dev-server 实现此功能。

使用 webpack-dev-server

webpack-dev-server 为你提供了一个简单的 web server,并且具有 live reloading(实时重新加载) 功能。设置如下:

  1. npm install --save-dev webpack-dev-server

修改配置文件,告诉 dev server,从什么位置查找文件:

webpack.config.js

  1. const path = require('path');
  2. const HtmlWebpackPlugin = require('html-webpack-plugin');
  3. const CleanWebpackPlugin = require('clean-webpack-plugin');
  4. module.exports = {
  5. mode: 'development',
  6. entry: {
  7. app: './src/index.js',
  8. print: './src/print.js'
  9. },
  10. devtool: 'inline-source-map',
  11. + devServer: {
  12. + contentBase: './dist'
  13. + },
  14. plugins: [
  15. new CleanWebpackPlugin(['dist']),
  16. new HtmlWebpackPlugin({
  17. title: 'Development'
  18. })
  19. ],
  20. output: {
  21. filename: '[name].bundle.js',
  22. path: path.resolve(__dirname, 'dist')
  23. }
  24. };

以上配置告知 webpack-dev-server,将 dist 目录下的文件 serve 到 localhost:8080 下。(译注:serve,将资源作为 server 的可访问文件)

webpack-dev-server 在编译之后不会写入到任何输出文件。而是将 bundle 文件保留在内存中,然后将它们 serve 到 server 中,就好像它们是挂载在 server 根路径上的真实文件一样。如果你的页面希望在其他不同路径中找到 bundle 文件,则可以通过 dev server 配置中的 publicPath 选项进行修改。

我们添加一个可以直接运行 dev server 的 script:

package.json

  1. {
  2. "name": "development",
  3. "version": "1.0.0",
  4. "description": "",
  5. "main": "webpack.config.js",
  6. "scripts": {
  7. "test": "echo \"Error: no test specified\" && exit 1",
  8. "watch": "webpack --watch",
  9. + "start": "webpack-dev-server --open",
  10. "build": "webpack"
  11. },
  12. "keywords": [],
  13. "author": "",
  14. "license": "ISC",
  15. "devDependencies": {
  16. "clean-webpack-plugin": "^0.1.16",
  17. "css-loader": "^0.28.4",
  18. "csv-loader": "^2.1.1",
  19. "file-loader": "^0.11.2",
  20. "html-webpack-plugin": "^2.29.0",
  21. "style-loader": "^0.18.2",
  22. "webpack": "^3.0.0",
  23. "xml-loader": "^1.2.1"
  24. }
  25. }

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

webpack-dev-server 具有许多可配置的选项。关于其他更多配置,请查看 配置文档

现在,server 正在运行,你可能需要尝试 模块热替换(hot module replacement)

使用 webpack-dev-middleware

webpack-dev-middleware 是一个封装器(wrapper),它可以把 webpack 处理过的文件发送到一个 server。 webpack-dev-server 在内部使用了它,然而它也可以作为一个单独的 package 来使用,以便根据需求进行更多自定义设置。下面是一个 webpack-dev-middleware 配合 express server 的示例。

首先,安装 expresswebpack-dev-middleware

  1. npm install --save-dev express webpack-dev-middleware

现在,我们需要调整 webpack 配置文件,以确保 middleware(中间件) 功能能够正确启用:

webpack.config.js

  1. const path = require('path');
  2. const HtmlWebpackPlugin = require('html-webpack-plugin');
  3. const CleanWebpackPlugin = require('clean-webpack-plugin');
  4. module.exports = {
  5. mode: 'development',
  6. entry: {
  7. app: './src/index.js',
  8. print: './src/print.js'
  9. },
  10. devtool: 'inline-source-map',
  11. devServer: {
  12. contentBase: './dist'
  13. },
  14. plugins: [
  15. new CleanWebpackPlugin(['dist']),
  16. new HtmlWebpackPlugin({
  17. title: '管理输出'
  18. })
  19. ],
  20. output: {
  21. filename: '[name].bundle.js',
  22. path: path.resolve(__dirname, 'dist'),
  23. + publicPath: '/'
  24. }
  25. };

我们将会在 server 脚本使用 publicPath,以确保文件资源能够正确地 serve 在 http://localhost:3000 下,稍后我们会指定 port number(端口号)。接下来是设置自定义 express server:

project

  1. webpack-demo
  2. |- package.json
  3. |- webpack.config.js
  4. + |- server.js
  5. |- /dist
  6. |- /src
  7. |- index.js
  8. |- print.js
  9. |- /node_modules

server.js

  1. const express = require('express');
  2. const webpack = require('webpack');
  3. const webpackDevMiddleware = require('webpack-dev-middleware');
  4. const app = express();
  5. const config = require('./webpack.config.js');
  6. const compiler = webpack(config);
  7. // 告诉 express 使用 webpack-dev-middleware,
  8. // 以及将 webpack.config.js 配置文件作为基础配置
  9. app.use(webpackDevMiddleware(compiler, {
  10. publicPath: config.output.publicPath
  11. }));
  12. // 将文件 serve 到 port 3000。
  13. app.listen(3000, function () {
  14. console.log('Example app listening on port 3000!\n');
  15. });

现在,添加一个 npm script,以使我们更方便地运行服务:

package.json

  1. {
  2. "name": "development",
  3. "version": "1.0.0",
  4. "description": "",
  5. "main": "webpack.config.js",
  6. "scripts": {
  7. "test": "echo \"Error: no test specified\" && exit 1",
  8. "watch": "webpack --watch",
  9. "start": "webpack-dev-server --open",
  10. + "server": "node server.js",
  11. "build": "webpack"
  12. },
  13. "keywords": [],
  14. "author": "",
  15. "license": "ISC",
  16. "devDependencies": {
  17. "clean-webpack-plugin": "^0.1.16",
  18. "css-loader": "^0.28.4",
  19. "csv-loader": "^2.1.1",
  20. "express": "^4.15.3",
  21. "file-loader": "^0.11.2",
  22. "html-webpack-plugin": "^2.29.0",
  23. "style-loader": "^0.18.2",
  24. "webpack": "^3.0.0",
  25. "webpack-dev-middleware": "^1.12.0",
  26. "xml-loader": "^1.2.1"
  27. }
  28. }

现在,在 terminal(终端) 中执行 npm run server,将会有类似如下信息输出:

  1. Example app listening on port 3000!
  2. ...
  3. Asset Size Chunks Chunk Names
  4. app.bundle.js 1.44 MB 0, 1 [emitted] [big] app
  5. print.bundle.js 6.57 kB 1 [emitted] print
  6. index.html 306 bytes [emitted]
  7. ...
  8. webpack: Compiled successfully.

现在,打开浏览器,访问 http://localhost:3000。应该看到webpack 应用程序已经运行!

如果想要了解更多关于模块热替换(hot module replacement)的运行机制,我们推荐你查看 模块热替换(hot module replacement) 指南。

调整文本编辑器

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

在一些常见的编辑器中禁用此功能,查看以下列表:

  • Sublime Text 3:在用户首选项(user preferences)中添加 atomic_save: 'false'
  • JetBrains IDEs (e.g. WebStorm):在 Preferences > Appearance & Behavior > System Settings 中取消选中 “Use safe write”。
  • Vim:在设置(settings)中增加 :set backupcopy=yes

结论

现在,你已经学会了如何自动编译代码,并运行一个简单的 development server,查看下一个指南,其中将介绍 模块热替换(hot module replacement)


贡献人员

Calinou Calinou EugeneHlushko EugeneHlushko GAumala GAumala SpaceK33z SpaceK33z TheDutchCoder TheDutchCoder WojciechKo WojciechKo byzyk byzyk fvgs fvgs rafde rafde trivikr trivikr