前言
这是我学习webpack的第三篇笔记,前文之路
- webpack学习笔记(一):初次见面,请多指教
- webpack学习笔记(二):基本属性管理 在这一篇博客中,我想介绍的是如何进行webpack工程的环境配置。说到环境配置,很自然的就能想到之前提到过的 config.webpack.js 这个文件。没错,这个文件就是用来进行webpack环境配置的。但是,在实际的工程应用场合,需要将开发环境和生产环境分开进行配置,来达到工程优化的目的。在这篇博客中,主要介绍三个内容:开发环境,生产环境,环境变量。在学完这些东西之后,基本上就可以搭建一个简单的webpack工程模板了。
开发环境VS生产环境
前面两个笔记只是让webpack可以正常运行起来,但这远远不能满足实际开发的需要。在工程实践中,必然是要做到开发环境和生产环境分离的。在实际的开发中,需要做到:
- 提供http服务而不是在本地打开html文件看效果;
- 最好还能做到实时预览,而不是每次更改文件内容就得重新打包;
- 在开发中方便调试,准确定位出源文件的错误。 区别于开发环境,在生产环境中,我们更多的会关注压缩bundle、更轻量的source map、资源优化等。因此,我们需要将开发环境和生产环境分开。
开发环境
webpack开发环境的搭建分为以下三步:
- 将
mode
设置为development
在 config.webpack.js 文件中设置 mode 属性即可:
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',
plugins: [
new HtmlWebpackPlugin({
title: 'Development',
}),
],
output: {
filename: '[name].bundle.js',
path: path.resolve(__dirname, 'dist'),
clean: true,
},
};
- 使用source map追踪源文件中的错误
当使用webpack打包源代码时,在程序的执行过程中遇到错误会直接定位到打包好的bundle文件中,很难追溯到源代码中的错误。这就给代码的调试带来了很大的不便。
为了解决这种调试的困难,JavaScript提供了source map的功能,可以将编译后的代码映射回源代码。这样就方便追踪程序执行过程中遇到的错误(error)和警告(warning)了。
要使用source map,只需要在config.webpack.js文件中声明devtool
属性即可:
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', // 新添加的
plugins: [
new HtmlWebpackPlugin({
title: 'Development',
}),
],
output: {
filename: '[name].bundle.js',
path: path.resolve(__dirname, 'dist'),
clean: true,
},
};
在这里的代码中使用的是inline-source-map
选项。除此之外,还有其他的source-map选项,如下表所示:
devtool选项 | 配置结果 |
---|---|
source-map | 在一个单独的文件中产生一个完整且功能完全的文件。这个文件具有最好的source map 但他会减慢打包速度 |
cheap-module-source-map | 在一个单独的文件中生成一个不带列映射的map ,不带列映射提高了打包速度,但是也使得浏览器开发者工具只能对应到具体的行,不能对应到具体的列(符号),会对调试造成不便 |
eval-source-map | 使用eval 打包源文件模块,在同一个文件中生成干净的完整的source map ,但是对打包后输出的JS文件的执行具有性能和安全的隐患。在开发阶段这是一个非常好的选项,在生产阶段则一定不要启用这个选项 |
cheap-module-eval-source-map | 这是在打包文件时最快的生成source map 的方法,生成的source map 会和打包后的JavaScript文件同行显示,没有列映射,和eval-source-map 选项具有相似的缺点 |
其中,eval-source-map
对于小到中型项目而言是一个很好的选项,但是应该只在开发阶段使用它;cheap-module-eval-source-map
方法构建速度更快,但是不利于调试,推荐在大型项目考虑时间成本时使用。
- 选择一种开发工具 webpack提供了三种在开发中方便调试的工具:
- webpack's Watch Mode: 如果文件被更新,代码将被重新编译,无需手动创建
- webpack-dev-server: 提供了一个基本的web server
- webpack-dev-middleware: 可以把webpack处理过的文件发送到一个server,需要手动创建一个server
Watch Mode
这个工具不需要额外安装插件,只需要指示webpack "watch"则可以启动Watch Mode。如果采用npm script命令来运行,则需要在package.json文件中添加命令。
- 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": {
"html-webpack-plugin": "^4.5.0",
"webpack": "^5.4.0",
"webpack-cli": "^4.2.0"
},
"dependencies": {
"lodash": "^4.17.20"
}
}
配置完成后,只需要运行如下命令即可。
npm run watch
webpack-dev-server
- 安装webpack-dev-server插件
npm install --save-dev webpack-dev-server
- 配置wepack.config.js
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: { // 新添加的
contentBase: './dist', // 新添加的
}, // 新添加的
plugins: [
new HtmlWebpackPlugin({
title: 'Development',
}),
],
output: {
filename: '[name].bundle.js',
path: path.resolve(__dirname, 'dist'),
clean: true,
},
};
- 添加直接运行dev server的script
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"watch": "webpack --watch",
"start": "webpack serve --open", // 新添加的
"build": "webpack"
},
webpack-dev-middleware
- 安装webpack-dev-middleware和express(根据需求选择安装的位置,如果仅在开发中需要,则安装在devDependencies中;如果在生产中也需要,则安装在dependencies中)
npm install --save-dev webpack-dev-middleware
npm install --save express 或者 npm install --save-dev express
- 调整webpack.config.js文件
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: {
contentBase: './dist',
},
plugins: [
new HtmlWebpackPlugin({
title: 'Development',
}),
],
output: {
filename: '[name].bundle.js',
path: path.resolve(__dirname, 'dist'),
clean: true,
publicPath: '/', // 新添加的
},
};
- 在工程目录添加server.js
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');
});
- 添加直接运行server的script
在package.json文件中
scripts
添加相应的命令:
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"watch": "webpack --watch",
"start": "webpack serve --open",
"server": "node server.js", // 新添加的
"build": "webpack"
},
生产环境
在这一部分中,首先需要做的是将生产环境和开发环境进行一个分离,即将webpack.config.js分成两个文件:webpack.dev.js 和 webpack.prod.js。这两个文件分别配置开发环境和生产环境。分开之后的文件目录如下:
webpack-demo
| - package.json
| - webpack.dev.js
| - webpack.prod.js
| - /dist
| - /src
| - index.js
| - /node_modules
然后就是编写这两个文件中的配置:
- webpack.dev.js
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
mode: 'development',
devtool: 'inline-source-map',
devServer: {
contentBase: './dist',
},
entry: {
app: './src/index.js',
},
plugins: [
new HtmlWebpackPlugin({
title: 'Production',
}),
],
output: {
filename: '[name].bundle.js',
path: path.resolve(__dirname, 'dist'),
clean: true,
},
};
- webpack.prod.js
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
mode: 'production',
entry: {
app: './src/index.js',
},
plugins: [
new HtmlWebpackPlugin({
title: 'Production',
}),
],
output: {
filename: '[name].bundle.js',
path: path.resolve(__dirname, 'dist'),
clean: true,
},
};
最后,将scripts
指向新的命令:
"scripts": {
"start": "webpack serve --open --config webpack.dev.js",
"build": "webpack --config webpack.prod.js"
},
容易发现,这两个文件中有很多相同的地方。本着不重复的原则,考虑将两个文件中相同的部分放到同一个文件webpack.common.js中,webpack.dev.js和webpack.prod.js中只放各自不同的配置即可。此时的目录结构变成:
webpack-demo
| - package.json
| - webpack.common.js
| - webpack.dev.js
| - webpack.prod.js
| - /dist
| - /src
|- index.js
| - /node_modules
更改完文件目录之后,还应该安装一个可以将两个文件的配置合并在一起的插件:
npm install --save-dev webpack-merge 三个文件的代码如下:
- webpack.common.js
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
entry: {
app: './src/index.js',
},
plugins: [
new HtmlWebpackPlugin({
title: 'Production',
}),
],
output: {
filename: '[name].bundle.js',
path: path.resolve(__dirname, 'dist'),
clean: true,
},
};
- webpack.dev.js
const { merge } = require('webpack-merge');
const common = require('./webpack.common.js');
module.exports = merge(common, {
mode: 'development',
devtool: 'inline-source-map',
devServer: {
contentBase: './dist',
},
});
- webpack.prod.js
const { merge } = require('webpack-merge');
const common = require('./webpack.common.js');
module.exports = merge(common, {
mode: 'production',
});
环境变量
webpack 命令行环境配置的 --env
参数,可以允许传入任意数量的环境变量。关于环境变量的访问问题,可以在 webpack.config.js 中可以访问到这些环境变量;也可以通过node环境获取:
process.env.NAME