我的博客地址
正式地址
测试地址
前端源码
后端源码
文章目录
- 项目及其技术栈介绍
- 前端: 项目初始化
- 前端: 使用Sass和Antd
- 前端: 开发体验优化
- 前端: 搭建路由和状态管理
- 前端: 支持Axios
- 前端: 打包与环境变量设置
- 前端: 团队代码规范
- 后端: 项目初始化和使用Koa相关
- 后端: 使用TypeORM和MySQL
- 部署: 使用nginx部署前端项目
- 部署: 后端部署
- 部署: 使用jenkins自动化部署
前言
在前端开发完成后,都需要将编写的代码编译打包成静态文件,之后才会部署到服务器上,而WebPack就是这样一个打包工具,它所有的loader、plugin都是围绕这一个功能进行的扩展,这篇文章就在之前搭建的基础上来介绍一下WebPack的打包配置吧,所用到的知识点如下:
- 打包路径和命令添加
-
filename
和chunkFilename
:hash
、chunkhash
和contenthash
publicPath
- 单独打包CSS
-
optimization
优化打包 - 环境变量设置区分生产开发环境
打包路径和命令添加
-
添加打包路径
其实在开始介绍前端搭建的第一篇文章中我们就已经将打包的入口和出口都在webpack.config.js
中制定好了:
这段配置意思是让WebPack通过src/index.tsx
作为入口,递归地建立模块依赖关系,然后打包输出模块,模块输出后存放在根目录的dist
文件夹中 -
添加打包命令
去到package.json
加上如下命令:
-
测试打包
我们运行npm run build
之后可以看到包已经打好了:
filename和chunkFilename
在webpack
的output
配置中,filename
是打包文件名,也就是说我们列在entry
中的文件打包出来的名字,在本例中就是app
:
而
chunkFilename
则是未列在entry
中的模块文件,下面划红线的都属于chunk file
:而
filename
和chunkFilename
则是对这两种文件命名的设定,这个命名很重要吗?是的,因为浏览器存在缓存问题,如果按照目前配置,每一次代码变化的情况下打包出来的文件名不变,浏览器优先加载缓存中的文件,然后导致页面没有展示最新的页面情况。
-
要解决这个问题我们先把
chunkFilename
设置好:
-
然后可以通过给文件名设置hash值的方式保证每次打包的文件名都是不同的,
filename
和chunkFilename
都支持该设置:
-
另外这个hash值的设置有三种: 分别是
hash
、chunkhash
和contenthash
-
hash
:hash
和整个项目的配置有关,只要项目中有代码改变,那么所有打包出来的hash值都会变,并且所有文件共用一个hash值 -
chunkhash
:chunkhash
和hash
不同点在于,它根据入口文件进行依赖文件解析,然后构建对应的hash值,也就是每个打包出来的文件hash值都是不一样的,每次修改代码时候,他会根据依赖关系自动修改相关模块的hash值,但是打包出来对应的js和css文件的hash会相同。 -
contenthash
: 在打包代码的时候,一般会将CSS文件分离出来,然后我们通常会在组件中引入CSS文件,这时候如果使用的是chunkhash
,在只修改组件js代码的情况下因为对应的css文件的hash值相同,打包出来的css文件的hash值也会跟着变,这时候就可以使用contenthash
了,他会针对每个文件的内容来计算hash值。
-
在本例中,我们就使用chunkhash
即可,CSS的contenthash
可以在后面对css文件进行分离的时候再配置。
publicPath
publicPath
也是ouput
中的一个属性,用于处理打包后的引入资源路径,比如上面我没有添加publicPath
的时候,打包出来的index.html
引入资源的路径:
现在我把
publicPath
设置为/
,再来看看打包出来的index.html
的引入资源路径就成了绝对路径了:那么这个配置有什么用处呢?
- 用处
在我们对前端项目打包完毕之后,js和css等较大的静态资源都会被上传到cdn上面,而index.html
则会留在我们自己的服务器上,用户访问index.html
的时候,index.html
加载的css和js路径就应该是cdn域名上的资源,也就是说假设我的cdn域名为https://cdn.xxx.com/
,那么这串东西就应在放到publicPath
中让他自己在资源引入的前面加上,比如下面的例子:
单独打包CSS
在前面的打包配置中,我们虽然可以打包成功,但是这时候的css是被嵌入到js文件中的:
这对于模块化来说不大好,而且还会导致js文件变得更大从而降低网页加载速度,所以需要将css额外抽离出来。
使用
mini-css-extract-plugin
抽离css
在webpack4.0后,推荐使用mini-css-extract-plugin
来对css文件进行分离,首先我们需要安装mini-css-extract-plugin
包,这个包里面包含一个loader和一个plugin,后续需要分别进行配置
npm i -D mini-css-extract-plugin
-
配置loader
接下来我们去到build/rules/styleRules.js
文件中引入他:
在这里需要注意一点:
sass-loader
是将sass文件编译为css,而css-loader
是将css转为CommonJS模块,style-loader
是将对应的JS生成为style 节点,那么如果我们要分离css文件的话,应该是在css-loader
之后进行,所以下面我们把之前style-loader
的地方全都替换成MiniCssExtractPlugin
:
-
配置plugin
然后去到build/plugins.js
文件中,引入并使用mini-css-extract-plugin
并使用,配置主要是filename
和chunkFilename
:
注意这里需要用contenthash
而不是chunkhash
,否则打包出来的和引用该css的js文件的hash值是一样的,而且改css还会导致打包后的js文件的hash值产生变化。 -
打包结果
可以看到css文件已经被抽离了出来,并且js和css文件的hash值是不一样的:
使用optimization优化打包
在上面的打包配置中,因为之前做了路由组件的代码分割,所以组件PageA
和PageB
会被单独打包成js文件,再来看看打包的情况表,会发现page-a
的公用模块(引入的库)体积高达800多k,这对于网页加载资源而言是非常大的:
我们可以通过配置WebPack的
optimization
来对代码进一步分割,使得打出来的包更小。
-
首先我们去到
build
文件夹下新建文件optimization.js
,新建并导出一个对象:
然后配置runtimeChunk
:
runtime是指webpack运行环境(模块解析和加载)和模块信息清单,而runtimeChunk
就是询问该清部分清单代码是否单独打包出来,我们这里将其单独打包出来并命名为manifest
。 -
然后我们通过配置
splitChunk
的cacheGroups
将一些公共模块分离出来打包:
这里面的priority
是优先级的意思,数字越大优先级越高,antd依赖moment,所以moment优先级比antd高,然后commons
是剩余模块统一打包不做分割了。 -
接下来我们把optimization导入到webpack中去使用:
然后查看一下打包结果:
可以清楚的看到之前vendor~page-a
文件从发800多k变成了500多k,另外还多出来一个vendor
文件有270多k,说明是可以显著减少打包出来的文件的大小的。
如果还需要继续细分,可以继续在splitChunks.cacheGroups
配置中将公用模块继续细分并添加进去,使得打包的代码越来越小 -
压缩js优化和压缩css代码并优化
-
使用
terser-webpack-plugin
优化js压缩过程:
terser-webpack-plugin
是一个js代码优化插件,他可以使用多线程和缓存更快的压缩js代码,优化打包体验,并且使用非常简单。
为什么使用这个插件呢?因为webpack默认使用的webpack.optimize.UglifyJsPlugin
插件是不支持es6语法的,当然你可以先用babel转一下再用UglifyJsPlugin
也没所谓。
首先我们安装npm i -D terser-webpack-plugin
。
然后在build/optimization.js
文件中配置minimizer
,导入terser-webpack-plugin
并使用即可:
-
压缩css代码并优化压缩过程
在之前的打包中,我们的js代码虽然已经经过了压缩,但是css代码还没有压缩:
这时候我们需要使用optimize-css-assets-webpack-plugin
来做这件事。
首先安装它npm i -D optimize-css-assets-webpack-plugin
。
然后继续在minimizer
中进行配置:
-
最后我们重新打包来看看结果:
可以看到css文件也被压缩了。
环境变量设置区分生产开发环境
在webpack4.0之后,打包的时候会出现这个警告:
这是因为在4.0后webpack受到parcel的竞争,而parcel就是号称0配置的打包器,所以webpack也内置了一套默认的打包配置,但是开发环境和生产环境的配置是不一样的,所以需要通过配置
webpack.mode
属性来告诉webpack处于什么环境,另外开发环境和生产环境有些不一样,例如生产环境一般不需要sourcemap
功能,之前打包出来很多.map
文件就是因为开启了sourcemap
所致,所以我们也需要通过环境变量来对此进行区分。
- 使用
cross-env
设置环境变量
cross-env
是一个专门用于设置webpack运行或者打包时候的进程环境变量的工具,注意是进程环境变量而不是全局变量,这中变量在webpack打包结束后就没有了,所以不能写入到业务代码中(当然也是可以写入的,不过需要另外配置)。
我们首先安装它npm i -D cross-env
。
然后去到package.json
文件中,在script
定义的命令中插入环境变量, 使用windows的同学需要注意,写法可能有点不一样,具体谷歌即可:
这一步的目的就是将
NODE_ENV
这个变量插入到进程中,开发和生产分别是development
和production
。
然后我们去到webpack.config.js
中,添加mode
属性的设置,通过process.env.NODE_ENV
即可获取之前设置的环境变量:
这样就消除了上面所说的webpack的警告了。
-
通过环境变量分别配置开发生产webpack配置
既然已经可以区分生产和开发环境,那么webpack中的有些配置也可以进行区分了:
-
注入环境变量到代码中
按照前面的说法,通过cross-env
注入的变量只能存在于webpack打包或者编译的进程中,那么有时候我们需要在代码运行的过程中获取到这个环境变量怎么办呢?
例如上一章我们配置Axios的时候的baseURL
就需要区分生产和开发环境:
当然也是有解决方案的,我们可以使用WebPack自带的插件DefinePlugin
做到这点。
首先我们去到build/plugins.js
文件中,在里面引入这个插件,然后进行变量配置即可:
之后我们到PageA
组件中,使用process.env.NODE_ENV
变量查看一下效果:
可以看到变量已经被注入到了代码中,而不是只存在于webpack打包和编译的进程中了:
另外还需要记得把baseURL换成生产环境的url,在本例中,生产环境的请求url是https://test.oxcblog.club/api
:
最后
经过上面的这么多步骤,我们的webpack配置也基本算是告一段落了,下一篇文章将会介绍使用团队代码规范相关的插件: commitlint、eslint、stylelint等。