一、使用 NPM Scripts 来管理开发命令
- 使用
NPM Scripts
来配置开发命令,即package.json
的scripts
字段,这样即使我们修改脚本甚至切换Webpack
到其他的打包工具,对于团队其他成员来说,使用的命令还是不变的,建议的命令包括:
npm start
:相当于npm run start
,用于开发命令,快速启动本地开发服务;npm run build
:用于生产环境打包;- 其他命令,类似
npm run test/lint
等,根据相关的需要添加即可
- 在
package.json
中使用cross-env
来区分环境,下面来看个示例:
{
// ...
"scripts": {
"start": "cross-env NODE_ENV=development webpack --config webpack.config.dev.js --mode development",
"build": "cross-env NODE_ENV=production webpack --config webpack.config.prod.js --mode producation",
"analyzer": "cross-env NODE_ENV=production webpack --config webpack.config.analyzer.js --mode producation",
"lint": "lint-staged"
}
// ...
}
二、Webpack 区分多环境配置
- 区分生产环境和开发环境配置,并且封装通用配置,即将
Webpack
配置文件分为:
- 通用配置
webpack.config.base.js
; - 开发环境配置
webpack.config.dev.js
; - 生产环境配置
webpack.config.prod.js
;
- webpack.config.base.js
通用配置webpack.config.base.js
用于通用的配置,例如entry、loader 和 plugin
等,但是有些需要根据cross-env
传入NODE_ENV
环境变量进行相关的配置,例如:NODE_ENV=development
的时候使用style-loader
,而production
的时候使用mini-css-extract-plugin
的loader
将生产环境的CSS
生成单独的CSS
文件;
// webpack.config.base.js
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const isProduction = process.env.NODE_ENV === 'production';
module.exports = {
// ...
module: {
rules: [
{
test: /\.css?$/,
use: [
{
loader: isProduction ? MiniCssExtractPlugin.loader : 'style-loader'
},
{
loader: 'css-loader',
options: {
importLoaders: 1,
sourceMap: !isProduction
}
},
{
loader: 'postcss-loader',
options: {
sourceMap: !isProduction
}
}
]
}
]
}
// ...
};
-
webpack.config.dev.js
开发环境配置webpack.config.dev.js
主要用于开发环境配置,主要是devServer
、API mock
等相关配置,这部分配置注重的是效率,所以打包速度优化也是很重要的。 -
webpack.config.prod.js
webpack.config.prod.js
用于生产环境配置,这部分配置注重的是线上最优打包配置,包括splitChunks
、压缩资源、CDN
路径配置(在output
配置)等相关配置,还可以在terser-webpack-plugin
配置中强制去除一些忘记删除的调试信息:例如debugger、alert
。
注意生产环境打包不建议生成
sourcemap
,如果生成了也不要上传到线上环境,因为如果sourcemap
上线之后,等于别人就可以通过Chrome
等工具直接查看线上代码的源码,这是十分危险的!但是如果你项目使用类似Sentry
的JavaScript
报错收集分析平台,可以将sourcemap
经过Webpack
生成,上传到对应的平台之后记得删除上线包中的这些文件,防止上传到线上!
-
webpack.config.analyzer.js
除了以上三个配置文件之外,为了方便我们分析打包是否合理做了代码拆分,我们可以增加一个webpack.config.analyzer.js
,这个配置实际是继承webpack.config.prod.js
,然后增加webpack-bundle-analyzer
插件配置。 -
使用
webpack-merge
管理配置文件关系
Webpack
配置文件拆分之后,各自之间都有依赖关系,具体关系如下:
webpack.config.dev.js
是合并了webpack.config.base.js
和自己的配置;webpack.config.prod.js
合并了webpack.config.base.js
和自己的配置;webpack.config.analyzer.js
合并了webpack.config.prod.js
和自己的配置,而webpack.config.prod.js
又是来自于webpack.config.base.js
。
- 如果维护这个配置关系,那么就需要使用
webpack-merge
这个工具库,webpack-merge
主要是提供一个Webpack
配置对象Merge
函数,用来合并两个配置,类似于Object.assign
函数的功能。如webpack.config.analyzer.js
来看下webpack-merge
怎么使用:
const merge = require('webpack-merge');
const prodWebpackConfig = require('./webpack.config.prod.js');
const {
BundleAnalyzerPlugin} = require('webpack-bundle-analyzer');
module.exports = merge(prodWebpackConfig, {
// 增加 webpack-bundle-analyzer 配置
plugins: [new BundleAnalyzerPlugin()]
});
三、合理使用splitChunks
- 在使用
splitChunks
主要是为了合理的划分资源大小,提高缓存命中率,从而降低资源的加载时间,在划分合理性上一定要注意把握力度,太细不能充分利用HTTP Cache
,太粗又会导致加载速度慢,这个度不好笼统的来定义,但是一般来说可以按照下面三个原则来拆分代码:
- 变更频次;
- 页面
Router
; - 动静分离。
-
变更频次
代码按照变更频次来使用splitChunks
进行拆分,即将这些不经常修改的通用框架和库放到一起作为common
代码,然后把业务代码按照页面间公共部分和私有部分进行拆分 -
页面 Router
不经常变动的框架和库代码拆分完之后,剩下的是业务代码,业务代码可以根据不同的页面之间公共代码拆分到一起,这样可以保证访问一个页面就可以将框架代码和公共代码缓存到浏览器中,再访问第二个页面就不会增加框架代码和公共代码页面请求了。 -
动静分离
动静分离指的是页面内使用频次不高或者需要动态异步加载(使用import()或者require.ensure())
的模块代码可以单独拆分到各自的chunks
,这样保证了页面首屏展现速度。另外类似Vue、React
这类单页应用,页面Router
之间的代码也是可以异步加载的,整个页面第一个入口就将大框架和当前页面的代码加载进来了,等点击跳到二级页面的时候只需要动态加载对应Router
的代码即可。
四、指定 chunk 的哈希值
- 在生产环境打包,一定要配置文件
filename
的hash
,推荐hash
配置规则如下:
JavaScript
文件使用:[chunkhash]
;CSS
文件使用:[contenthash]
;- 其他静态资源用:
[hash]
,例如图片、字体等,在url-loader
中配置[hash]
五、语法层面的最佳实践
-
使用
ES6 Modules
语法,以保证Tree-Shaking
起作用。 -
合理使用
Ployfill
,推荐使用@babel/preset-env
的useBuiltIns='usage'
方案。 -
合理使用
Webpack
的魔法注释(magic comments)
,比如:动态加载的模块要用webpackChunkName
进行命名,还可以重要资源使用webpackPrefetch
提前预加载。 -
框架或者类库使用合理的版本,举例说明:
Lodash
使用lodash-es
版本,并且按模块使用;Momentjs
使用date-fns
代替,并且按模块使用;- 移动页面使用
Zepto
代替jQuery
; Vue、San、React
这类框架库根据实际情况选择合适构建版本,以Vue
为例,其实构建版本包含浏览器版本、ESM
版本、UMD
版本、完整版等多个版本
六、其他 Webpack 配置的最佳实践
- 生产环境使用
mini-css-extract-plugin
导出CSS
文件; - 生产环境使用压缩功能,包括
JavaScript、CSS、图片、SVG
等; - 合理配置查找路径,减少查找时间,比如设置
alias
、添加项目路径、排查node_modules
查找等; - 在
rule
配置上,有test、include、exclude
三个可以控制范围的配置,最佳实践是:
- 只在
test
和 文件名匹配 中使用正则表达式; - 在
include
和exclude
中使用绝对路径数组; - 尽量避免
exclude
,更倾向于使用include
。
icon
类图片文件太多使用CSS Sprite
来合并图片,防止设置url-loader
和svg-url-loader
的limit
值不合理,导致icon
文件都以Base64
方式引入CSS
文件中,导致CSS
文件过大。
七、其他方面最佳实践
- 规范化
Git
工作流:
- 使用
Git Hook
,类似Husky
这类Git Hook
库,可以帮助我们在每次提交之前(pre-commit)
自动做lint
检查; - 使用
Commitizen
来规范Git
的提交Commit Log
;
-
组件化开发,公共
UI
组件、公共函数库建设;这里说的比较抽象,需要具体项目来划分,例如我们可以将多个页面常用的UI
组件抽象出来;也可以将通用的工具函数库建设起来,类似Lodash
这类库; -
选择一个顺手的
CSS
预处理语言,Sass、Less、Stylus
,只需要团队使用顺手即可; -
指定规则约定,包括代码规范、目录结构,文档规范等;
-
前后端分离,选择合适的
Mock
方案; -
将最佳实践做成标准项目的脚手架,新项目使用脚手架工具来创建;
-
抽象解决方案,融合到
Webpack
配置中,甚至是基于Webpack
做自己的最佳实践工具链。