上两篇博客简单总结了webpack4.0的一些基础配置,写完之后对webpack有了一个入门的认识。这两天又继续对遗留的几个知识点进行了梳理,如两种开发模式的配置流程,path
和publicPath
的区别与作用等。但是看一些关于webpack运行原理的博客时还是会一脸懵逼,完全不知所云,而且目前来讲我并没有真正的从0配置一个webpack的项目,所有的这些知识点还都没进入实践去检验。接下来写完这一两篇博客后,会着手开始react
框架的学习,到时候结合项目,再去深入的理解webpack
。
生产模式&&开发模式
现代前端工程项目,不管是使用angular-cli
还是vue-cli
来搭建,都会有本地开发和线上生产两种模式,命令行运行npm run dev
即可在本地起一个服务,方便本地开发调试。之前讲webpack4.0
新增了mode
模式,之后会简化两种模式的配置,但是我们还是先来看一下4.0之前是如何配置两种模式的配置文件的,这里以vue
为例。
vue的最佳实践
vue-cli
脚手架生成的工程项目结构非常清晰,值得反复看,反复理解,它关于生产和开发模式的配置主要是这些文件:
webpack.base.conf.js
中是一些公共的配置,然后通过webpack-merge
把这些公共配置项和环境特定的配置项 merge 起来,成为一个完整的配置项。比如在webpack.dev.conf.js
中:
1 |
|
通过这种方式,开发和生产两种模式就可以有各种独有的wenpack配置项,更关键的是,每个配置中用 webpack.DefinePlugin
向代码注入了 NODE\_ENV
这个环境变量。这个变量在不同环境下有不同的值,比如 dev 环境下就是 development。这些环境变量的值是在 config 文件夹下的配置文件中定义的。Webpack 首先从配置文件中读取这个值,然后注入。比如这样:
1 | //webpack.dev.conf.js |
1 | //dev.env.js |
这样我们就可以根据当前环境做判断,执行不同逻辑的代码,比如这样:
1 | //utils.js |
这种区分不同的环境,并给环境变量设置不同的值的实践,让我们开启了编译时按环境对代码进行针对性优化的可能。
模拟4.0之前
看的再多不如手写一遍。我们先来手动实现vue的模式。首先我们安装两个依赖:
1 | npm i webpack-merge uglifyjs-webpack-plugin -D |
然后新建webpack.dev.conf.js
和webpack.prod.conf.js
两个文件,同时将webpack.conf.js
修改为webpack.base.conf.js
:
我们将devServer
配置移动到webpack.dev.conf.js
中,同时新增一个devtool
配置:
1 | const path = require('path'); |
使用webpack-merge
合并基础配置和开发配置,这里就简单这么配置。接下来配置一下webpack.prod.conf.js
:
1 | const merge = require('webpack-merge'); |
这里我们配置了一个不同的devtool
配置,然后新引入一个uglifyjs-webpack-plugin
插件,用来压缩混淆js文件。最后我们为了方便测试效果,将package.json
中的命令这么修改:
1 | ... |
接下来我们分别运行npm run dev
和npm run build
,观察生成的dist
文件夹有什么不同:
从这个简单的实践我们就实现了一个很粗糙的运行模式拆分。剩下的就是针对具体的项目需求来编写更加强大的webpack
配置了,当然还是强烈推荐去看vue
脚手架生成的项目结构,越看越觉得有收获。
webpack4.0模式初探
webpack4.0
新增了mode
配置,可选值有development
/production
/none
,借用官方文档:
选项 | 配置 |
---|---|
development | 会将 process.env.NODE_ENV 的值设为 development 。启用 NamedChunksPlugin 和 NamedModulesPlugin 。 |
production | 会将 process.env.NODE_ENV 的值设为 production 。启用 FlagDependencyUsagePlugin , FlagIncludedChunksPlugin , ModuleConcatenationPlugin , NoEmitOnErrorsPlugin , OccurrenceOrderPlugin , SideEffectsFlagPlugin 和 UglifyJsPlugin . |
none | 不选用任何默认优化选项 |
在不同的模式下,webpack
会自动执行不同的插件:
mode: development
1 | // webpack.dev.conf.js |
mode: production
1 | // webpack.production.config.js |
mode: none
1 | // webpack.custom.config.js |
如果你想要根据 webpack.config.js 中的 mode 变量去影响编译行为,那你必须将导出对象,改为导出一个函数:
1 | var config = { |
但是这个选项的加入,如何优雅的跟之前拆分文件的形式结合,还得看社区大神们的整合方式,不管怎样,以后遇到心里就有底了。
path && publicPath
先来看一下现在我们output
配置:
1 | const path = require('path'); |
output
配置中path
指定了webpack
打包后生成的文件输入到哪里,我们之前运行npm run build
的时候,文件都是打包到了这里,这个比较好理解,不过需要注意的是,该路径必须为绝对路径,所以我们使用了path.resolve
来输出。
简单了说完了,比较迷惑的publicPath
来了,这个路径指什么呢?刚开始我也比较迷惑,path
已经配置了,还要这个publicPath
干什么?反正你静态资源(css
、image
)都是以此为基准嘛,等等,真的是这样吗?
我们先来运行一下npm run dev
,注意,这时候我们把命令调回到webpack-dev-server
,同时,我们设置output
内publicPath
的值为"/"
,该值不设置的话默认为空字符串。
1 | "dev": "webpack-dev-server --config webpack.dev.conf.js" |
服务启动后,打开控制台,观察html
、css
和image
的加载路径:
这里无法一次看完,手动写一下,第一个localhost
即document
文件路径是http://localhost:4200/
,第二个style.css
即stylesheet
文件路径是http://localhost:4200/static/css/style.css?4cb..
,第三个wp7.png
即png
文件路径是http://localhost:4200/static/img/wp7.png
。
这里我们发现,所有的静态资源文件路径都是基于/
,即我们设置的publicPath
,这个配置指定了你上传所有打包文件的位置(相对于服务器根目录),不过要注意,有时候我们的项目可不一定就是在网站根目录下配置的啊,有时候我们做的项目可能在xxx.com/project/
下面部署,如果你不相应的修改publicPath
值为/project/
,你会发现静态资源路径依然会基于/
,即根目录,从而导致资源无法访问到。
我们来试一试,现在将publicPath
改为"/project/"
,再来运行npm run dev
,发现页面空白,显示Cannot GET/
,控制台内也是404,这时候我们访问http://localhost:4200/project/
,页面加载成功,再看控制台:
所有的资源路径都是在/project/
下。
这里面其实涉及到了项目打包部署的问题,我对这方面了解的比较少,所以前面才会一直搞不明白这个publicPath
是干什么的,最近翻了翻vue
脚手架项目,又反复测试,才渐渐有点理解。不过这里面确实需要不断的实践才能更好的理解,就如官方文档中举的例子:
1 | module.exports = { |
上面还涉及到了资源在CDN的情况,理解了这些,以后就可以愉快的配合运维同学一起部署上线了,再遇到问题,查看官方文档就有迹可循了。
webpack
系列暂时告一段落,但是绝不是结束,后续的运行原理还等着我去研究和学习,到现在为止,感觉继续单纯的纸上谈兵已经不能有较大的提升了,接下来就是到项目中去,结合项目进行学习,巩固和调整。
参考文章:官方中文文档,Webpack 4 配置最佳实践