学习webpack系列之四 ---- (学习开发环境)
本章所用的代码继续沿用上一章(输出管理)的代码。
在开始之前我们要想想,为什么要了解webpack的开发环境?
其实很简单,自动化打包工具就是为我们的开发提供便利的。之前的几个章节讲的都是比较基础的一些功能以及用法,我们的目的就是要在开发的过程中尽可能的提高工作效率,所以我们有必要来学习他的一个开发环境的配置。
OK准备好了我们就开始!
一、开发环境MODE
mode有三个参数:none,development,production。如果没有设置,webpack 会给 mode 的默认值设置为 production。
选项 | 描述 |
---|---|
none | 不使用任何默认优化选项 |
development | 会将 DefinePlugin 中 process.env.NODE_ENV 的值设置为 development. 为模块和 chunk 启用有效的名 |
production | 会将 DefinePlugin 中 process.env.NODE_ENV 的值设置为 production。为模块和 chunk 启用确定性的混淆名称,FlagDependencyUsagePlugin,FlagIncludedChunksPlugin,ModuleConcatenationPlugin,NoEmitOnErrorsPlugin 和 TerserPlugin |
实际上,三种模式下是分别需要三个配置文件的,文章之前用的webpack.config.js
是基础文件。也就是说如果选择none
模式的时候,如果项目目录下有webpack.custom.config.js
该文件的话,webpack
就会优先加载该文件,而不是加载webpack.config.js
文件。各模式对应的文件名称如下:
选项 | 文件 |
---|---|
none | 优先加载webpack.custom.config.js |
development | 优先加载webpack.development.config.js |
production | 优先加载webpack.production.config.js |
但是如果不想配置那么多文件,只想用webpack.config.js
一个文件来更改打包行为,则必须将配置导出为函数,而不是导出对象:
var config = {
entry: './app.js',
//...
};
module.exports = (env, argv) => {
if (argv.mode === 'development') {
config.devtool = 'source-map';
}
if (argv.mode === 'production') {
//...
}
return config;
};
二、使用sourcemap
说到sourcemap,可以看看我以前写的一篇文章:sourcemap传送地址
当 webpack 打包源代码时,可能会很难追踪到 error(错误) 和 warning(警告) 在源代码中的原始位置。例如,如果将三个源文件(a.js, b.js 和 c.js)打包到一个 bundle(bundle.js)中,而其中一个源文件包含一个错误,那么堆栈跟踪就会直接指向到 bundle.js。你可能需要准确地知道错误来自于哪个源文件,所以这种提示这通常不会提供太多帮助。
为了更容易地追踪 error 和 warning,JavaScript 提供了 source maps 功能,可以将编译后的代码映射回原始源代码。如果一个错误来自于 b.js,source map 就会明确的告诉你。
sourcemap的作用就是用来溯源代码。
sourcemap 有许多 可用选项,请务必仔细阅读它们,以便可以根据需要进行配置。
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
mode: 'development',
entry: {
index: './src/index.js',
printer: './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
修改一下,写一个错误:
export default function printMe() {
conle.log('I get called from print.js!');
}
接着观察效果:
注意,他直接指出了,在文件当中的第2行出现了错误,如果没有配置,他只会单独报错,而不会帮你具体指出错误在哪里,这就是sourcemap的好处。
三、了解观察模式
在学习配置热加载之前,我们先来了解一下webpack
的观察模式。
我们添加一个用于启动 webpack watch mode
的 npm scripts:
{
"name": "webpack-simple-project",
"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": "^4.5.0",
"webpack": "^5.4.0",
"webpack-cli": "^4.2.0"
},
"dependencies": {
"lodash": "^4.17.20"
}
}
现在,你可以在命令行中运行 npm run watch,然后就会看到 webpack 是如何编译代码。 然而,你会发现并没有退出命令行。这是因为此 script 当前还在 watch 你的文件。
然后我们把原来的print.js
的文件修改回来:
export default function printMe() {
console.log('I get called from print.js!');
}
现在,保存文件并检查 terminal(终端) 窗口。应该可以看到 webpack 自动地重新编译修改后的模块!
唯一的缺点是,为了看到修改后的实际效果,你需要刷新浏览器。如果能够自动刷新浏览器就更好了,因此接下来我们会尝试通过 webpack-dev-server 实现此功能,接着看。
四、配置项目热加载
使用 webpack-dev-server
:webpack-dev-server 为你提供了一个基本的 web server,并且具有 live reloading(实时重新加载) 功能。
首先下载安装包:
npm install --save-dev webpack-dev-server
接着修改配置文件,告知 dev server,从什么位置查找文件:
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
mode: 'development',
entry: {
index: './src/index.js',
print: './src/print.js',
},
devtool: 'inline-source-map',
// 这里添加devServer
devServer: {
static: './dist',
hot: true
},
plugins: [
new HtmlWebpackPlugin({
title: 'Development',
}),
],
output: {
filename: '[name].bundle.js',
path: path.resolve(__dirname, 'dist'),
clean: true,
},
};
以上配置告知 webpack-dev-server,将 dist 目录下的文件 serve 到 localhost:8080 下。
webpack-dev-server 会从 output.path 中定义的目录为服务提供 bundle 文件,即,文件将可以通过 http://[devServer.host]:[devServer.port]/[output.publicPath]/[output.filename] 进行访问。
最后我们添加一个可以直接运行 dev server 的 script,并将其运行起来:
{
"name": "webpack-simple-project",
"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": "^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 将在编译代码后自动重新加载。试试看!
这里提示一下,关于hot的配置,如果我们运用webpack-dev-server的话呢,只需要关注这一个配置项就足够了。但如果我们在开发的过程中,有其他需求,需要配置自己的server的话,那就需要深入了解。这点我后续会继续更新文章,毕竟比较复杂。
而关于自己配置的server,我们可以接着看。因为随着项目的增量和需求的增加,我们是很有可能需要自己配置符合项目调试的server的。虽然这点可能平时不太会用得上,但是个人认为还是有必要了解一下的。各位看官自行取舍~
五、配置项目热加载server
使用 webpack-dev-middleware
:webpack-dev-middleware 是一个封装器(wrapper),它可以把 webpack 处理过的文件–compiler发送到一个 server。webpack-dev-server 在内部使用了这个插件,然而它也可以作为一个单独的 package 来供我们配置使用,以便根据需求进行更多自定义设置。下面是一个 webpack-dev-middleware 配合 express server 的示例。
首先,安装 express 和 webpack-dev-middleware:
npm install --save-dev express webpack-dev-middleware
现在,我们需要调整 webpack 配置文件,以确保 middleware(中间件) 功能能够正确启用:
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
mode: 'development',
entry: {
index: './src/index.js',
print: './src/print.js',
},
devtool: 'inline-source-map',
// 将devServer去掉,用我们自己配置的服务,不需要 webpack-dev-server
// devServer: {
// static: './dist',
// },
plugins: [
new HtmlWebpackPlugin({
title: 'Development',
}),
],
output: {
filename: '[name].bundle.js',
path: path.resolve(__dirname, 'dist'),
clean: true,
publicPath: '/', // 注意这个
},
};
我们将会在 server 脚本使用 publicPath
,前面也提及到了,webpack-dev-server是通过http://[devServer.host]:[devServer.port]/[output.publicPath]/[output.filename] 进行访问的。所以我们这里必须要设置这个参数,以确保文件资源能够正确地 serve 在 http://localhost:3000 下,稍后我们会指定 port number(端口号)。
重新整理一下项目目录,增加server.js,配置属于我们自己的服务:
webpack-simple-project
|- 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');
// 实例化 express
// 引入 webpack 配置文件
// 将 config 打包成 compiler package
const app = express();
const config = require('./webpack.config.js');
const compiler = webpack(config);
// 告知 express 使用 webpack-dev-middleware
// 以及将 compiler package 作为基础配置
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:
{
"name": "webpack-simple-project",
"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": "^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"
}
}
可以看见listening on port 3000!
就是成功了,页面也成功的运行起来了:
以上就是我们自行配置webpack服务的效果,如果你已经跟着写一遍并且理解了,我相信你会明白其中的一些妙不可言的地方,因为这确实是特别的人性化,自己想配置什么配置什么,不受约束,nice~
下一篇我们趁热打铁,继续学习webpack的模块热替换章节,就是所谓的热更新功能!