携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第5天,点击查看活动详情
本文 webpack 版本 ^5.73.0、webpack-cli 版本 ^4.10.0
简介
虽然开发、测试、生产的代码都是使用webpack构建工具来构建,但是对于不同的环境对构建后代码的要求肯定是不一样的。
比如,我们肯定想本地开发的时候需要构建的快、能快速定位到源码错误位置。对于生产环境我们肯定希望构建后的代码体积要足够小,图片能压缩、代码能分割等等。
对于这些要求,肯定就会涉及到不同的环境的配置。今天我们就来说说webpack的环境配置。
mode
mode配置选项,告知 webpack 使用相应模式的内置优化。
mode的值有none | development | production三个
对于不同的mode值,webpack有不同的优化策略。
引用官网的一张图:
mode默认值
mode默认值是production。
配置方式
配置方式主要有两种
直接定义
module.exports = {
mode: 'development',
};
从 CLI 参数中传递:
$ webpack --mode=development
可以看到第一种方式不够灵活,不能根据不同环境自动变更。第二种方式通过命令行参数进行传递,相对更灵活,但是由于mode只有development和production两个值,不太能满足我们日常开发的需要,所以我们需要另外再改造下。
一般我们的环境会有本地开发环境、测试环境、生产环境。所以我们就需要定义三种环境,不同的环境使用不同的配置。
获取mode值
对于第二种通过命令行参数传递mode值,我们怎么才能在webpack.config.js中获取到该值呢?
其实我们只要把文件形式改写下就可以了,由导出对象形式改为导出函数就能获取到了。
// webpack.config.js
const config = {
entry: './app.js',
mode: 'production'
//...
};
module.exports = (env, argv) => {
console.log(argv.mode) // development、none、production
return config;
};
前面说了,仅仅两个模式不能满足日常开发需求jq浏览器版本太低请升级,所以我们需要再定义其它的参数来进行更细化的区分。
自定义多种环境
这里我们需要使用到cross-env,它的作用就是能跨环境传递环境参数,也就是能同时兼容mac、windows、linux。
我们先安装下
npm i cross-env
然后我们在package.json里面配置构建命令
{
"scripts": {
"dev": "cross-env MODE=development webpack",
"staging": "cross-env MODE=staging webpack",
"build": "cross-env MODE=production webpack",
}
}
dev构建开发环境,staging构建测试环境,build构建生产环境。
三个环境传递三个不同的MODE值,我们可以在webpack.config.js中通过process.env.MODE获取,然后进行不同的配置啦。
// webpack.config.js
console.log("process.env.MODE=", process.env.MODE);
module.exports = (env, argv) => {
//...
return config;
};
我们来试试
npm run dev
npm run staging
npm run build
可以看到,我们通过不同的命令就能获取到不同的MODE值啦。有了不同的MODE值我们就可以进行不同环境的配置啦。
比如,我们需要不同的环境定义不同的mode,开发环境设置mode为development,测试和生产环境我们设置mode为production,进行不同的优化。
// webpack.config.js
console.log("process.env.MODE=", process.env.MODE);
module.exports = (env, argv) => {
if (process.env.MODE === "development") {
config.mode = "development";
} else {
config.mode = "production";
}
return config;
};
前面说了,mode定义后,会同步设置process.env.NODE_ENV的值,我们可以在我们的源码里面获取到。
如果mode为development则process.env.NODE_ENV的值为development,如果mode为production则process.env.NODE_ENV的值为production。
我们啦测试下,定义入口文件index.js,在里面输出process.env.NODE_ENV的值。
// index.js
console.log(process.env.NODE_ENV);
我们分别构建,然后运行构建后的js。
npm run dev
npm run staging
npm run build
可以看到,开发环境process.env.NODE_ENV的值为development,测试和生产环境的值是production。
这个没问题,那么我们上面设置的process.env.MODE的值能获取到吗?
代码中获取自定义环境变量值
我们再来试试
// index.js
console.log(process.env.NODE_ENV);
console.log(process.env.MODE);
再次构建,发现居然是undefined
这就有问题啦,如果我们代码中需要根据不同环境做不同处理那应该怎么办?其实我们可以用process.env.NODE_ENV的思路,它就是根据mode的不同使用DefinePlugin插件来动态设置值的,所以我们也使用这个插件处理下。
const webpack = require("webpack");
// ...
module.exports = (env, argv) => {
if (process.env.MODE === "development") {
config.mode = "development";
} else {
config.mode = "production";
}
// 根据参数,重新定义三种模式
config.plugins.push(
new webpack.DefinePlugin({
"process.env.MODE": JSON.stringify(process.env.MODE),
})
);
return config;
};
我们分别构建,然后运行构建后的js。
npm run dev
npm run staging
npm run build
可以看到,process.env.NODE_ENV和process.env.MODE的值都能在我们的代码中正确获取到了,这样我们不仅能根据不同环境配置我们的webpack.config.js,还能在我们的代码中根据不同的环境进行不同的处理。
这里我们根据不同的环境只设置了mode,其实还有很多属性都可以在不同的环境进行不同的配置,比如我们下面要说的devtool。
devtool
devtool用于控制是否生成,以及如何生成 source map
什么是 sourceMap
SourceMap是一个映射关系。能够帮我们更好的定位源码的错误。
举个例子,现在我们发现打包出来的dist目录下的main.js的97行报错了,但因为他是打包后的文件,我们知道main.js第几行报错其实没有任何意义。这个时候sourcemap就出来帮我们解决了这个问题,因为他是打包文件和源码的一个映射关系,它知道dist目录下main.js文件的97行 实际上对应的src目录下的index.js文件的第几行,这样我们就能够快速定位问题,并进行修复了。
devtool默认值
devtool默认值在生产环境下jq浏览器版本太低请升级,也就是mode为production,值是false。在开发环境下也就是mode为development,值是eval。
devtool种类
其实devtool的值还有很多,不同的值会生成不同的SourceMap。
从上图中我们可以看到 devtool 有非常多的配置,不同的配置构建的速度会有一些差异,中间的很多参数都是可以穿插使用的。
更多值可以参看devtool官方文档
我们简单来测试下,在源码index.js文件中使用一个未定义的变量
// index.js
// ...
// 定义一个错误
console.log(e);
总的来说说,其实devtool主要分为以下几类,我们分别测试下
false
false就是不使用SourceMap,所以它不会生成SourceMap文件。
因此它就不能准确定位到源码出错位置。
inline
inine有这个的配置,直接会将.map文件以 base64 的形式直接打包到对应的js中去,而不会单独生成.map文件,从而加快相应的速度。
可以看到这种方式它能精准定位到源码行数
cheap
cheap有这个的配置,意思是 map 文件只会帮你定为到具体的某一行,并不会把代码定位到 具体的 某一行 某一列,从而加快速度;cheap还有一个作用,就是这个选项只使针对业务代码,也就是说只能定位到业务代码里面的错误,并不能定位到我们引用的第三方文件(比如说loader,第三方模块)的错误。
会单独生成.map文件
定位的稍微有点问题,不那么准确
module
module有这个的配置,意思是 它不仅会帮我们定位 自己的业务代码中的错误,还会同时帮我们定位第三方模块的错误。
eval
eval有这个的配置,使用eval包裹模块代码,并且使用//@sourceURL引入SourceMap文件,这个是打包速度最快,性能最好的的一种方式,但是有的时候,对于代码比较复杂的情况,它提示出来的错误可能不够全面。
hidden
生成 SourceMap 文件,但不使用。
最佳实践
有了上面的基础,我们就能根据不同环境进行不同devtool的配置啦。
const webpack = require("webpack");
// ...
module.exports = (env, argv) => {
if (process.env.MODE === "development") {
config.mode = "development";
config.devtool = "eval-cheap-module-source-map"
} else if(process.env.MODE === "staging") {
config.mode = "production";
config.devtool = "source-map"
} else {
config.mode = "production";
config.devtool = false
}
// 根据参数,重新定义三种模式
config.plugins.push(
new webpack.DefinePlugin({
"process.env.MODE": JSON.stringify(process.env.MODE),
})
);
return config;
};
本地开发我们配置eval-cheap-module-source-map"使用最详细的SourceMap。
测试环境我们有可能需要在线调试错误,所以配置source-map。
生产环境直接禁用SourceMap,一是能缩小构建后包的体积,其次能防止别人窃取源码。
dev-server
前面讲的,开发环境每次修改代码还要执行npm run dev,这样效率太低了,有没有更好的更快的方法呢?
当然是有的,我们可以使用webpack-dev-server。
webpack-dev-server支持热部署、跨域代理、数据模拟等等。
安装
我们先来安装下
npm i webpack-dev-server -D
配置
使用webpack-dev-server后我们的构建命令不再是webpack了,而是webpack-dev-server,所以我们首先修改下构建命令。
"dev": "cross-env MODE=development webpack-dev-server"
接下来,我们在webpack.config.js里面进行配置
const config = {
// ...
devServer: {
// 新版 配置static而不是
static: path.resolve(__dirname, "./devdist"), // 静态文件目录
port: 8080, // 端口号
open:true // 是否自动打开浏览器
},
}
static就是服务的目录,这里我们定位到构建后的目录。
我们执行npm run dev,就会自动打开浏览器,在8080端口启动服务了。我们修改代码就能实时编译了。
如果有调用后端api需求的话,我们还可以配置代理
配置代理
const config = {
// ...
devServer: {
// 新版 配置static而不是
static: path.resolve(__dirname, "./devdist"), // 静态文件目录
port: 8080, // 端口号
open:true, // 是否自动打开浏览器
proxy: {
'/api': 'http://localhost:3000',
// 接受在 HTTPS 上运行且证书无效的后端服务器
secure: false,
},
},
}
当我们请求/api的时候就可以代理到:3000上去了。
mock数据
我们还可以利用webpack-dev-server来mock数据。
我们在我们的index.js添加请求
// index.js
fetch("/user")
.then((res) => res.json())
.then((data) => {
console.log(data);
});
然后使用devServer进行数据mock。
devServer: {
// 新版 配置static而不是
static: path.resolve(__dirname, "./devdist"), // 静态文件目录
compress: true, //是否启动压缩 gzip
port: 8080, // 端口号
// open:true // 是否自动打开浏览器
// 新版不是before,而是onBeforeSetupMiddleware
onBeforeSetupMiddleware(devServer) {
devServer.app.get("/user", (req, res) => {
res.json({ name: "randy" });
});
},
},
构建,在浏览器控制台可以看到我们mock的数据输出来了。
当然devServer远不止这些功能,大家可以看看devServer官方文档。
系列文章
webpack入门之css处理(css预处理器和css后置处理器)
webpack入门之图片、字体、文本、数据文件处理
webpack入门之js处理(babel、babel polyfill)
webpack入门之ts处理(ts-loadr和babel-loader的选择)
webpack入门之开发环境(mode、dev-server、devtool)
后记
好了,关于webpack环境相关的笔者今天先讲到这里啦。
感谢小伙伴们的耐心观看,本文为笔者个人学习笔记,如有谬误,还请告知,万分感谢!如果本文对你有所帮助,还请点个关注点个赞~,您的支持是笔者不断更新的动力!
版权声明
本文仅代表作者观点。
本文系作者授权发表,未经许可,不得转载。
发表评论