知乎上有个提问,叫“如何成为高级Webpack配置工程师”,戏谑Webpack已经复杂到成为一门专业学问了。但Webpack确实是非常复杂的,一般人只能做到入门而无法精通。Webpack的复杂性在于要完成各种各样的功能,即不仅要处理js、css、html、图片、字体等各种格式的前端资源,还要对这些资源进行转译、精简、提取、分割、打包等一系列操作。

Webpack在当前前端工程化中占有很重要的地位,前端工程化是为了提高前端开发的效率,但是前端工程化中的工具链配置的复杂度也逐渐提高。有时候新开一个项目,光是配置这些工具就要花一两天,这对于小型项目有点得不偿失。在配置工具链的时间花费大,诚然webpack本身配置参数多,我认为更重要的原因在于平时过于依赖于vue-cli、react-scripts这类自动化生成工具,没有具体地了解每个配置项的作用。

最近要开发一个可视化的JS库,但是vue-cli、react-scripts这类自动化生成工具主要针对的是SPA,对开发Library支持不好,因而尝试下手动配置webpack、eslint、babel、prettier这些工具。好在开发的是Library,主要处理JS代码,如果是SPA,那就需要处理各种各样的前端资源,还是建议使用vue-cli、react-scripts。

本文主要是做步骤记录,用于指导以后进行简单项目的手动配置,因而本篇文章不探讨深度内容。

生成package.json

这块没什么好说的,借助npm inityarn init可以快速地生成package.json文件。我通常习惯于先在scripts中把主要的开发命令写出来,以下是我的scripts配置:

1
2
3
4
5
6
7
8
9
10
{
"scripts": {
"build-dev": "webpack --config build/webpack.dev.config.js",
"build-prod": "webpack --config build/webpack.prod.config.js",
"build": "npm run build-dev && npm run build-prod",
"start": "webpack-dev-server --config build/webpack.dev.config.js",
"lint": "eslint src/*.js",
"format": "prettier-eslint --write src/*.js"
},
}

上面的scripts配置也体现了下文要讲的工程目录结构,即build里面放webpack配置文件,src里面放源代码,详细的内容在下一节描述。

main默认是index.js,即指向源代码的入口文件。但是本项目主要开发的库是作为其他项目的node_modules,一般不会再对库进行转译,所以为了方便将本库集成到前段工程项目中,main应该指向转译好的UMD格式文件。本项目的main配置为:

1
2
3
{
"main": "dist/geoeye.js"
}

规划目录结构

我认为工程的目录结构非常重要,它能够反映代码的模块划分,好的目录结构让人赏心悦目、容易理解。我按照以下目录进行组织:

1
2
3
4
5
6
7
8
9
10
11
build  // 编译配置
|- webpack.dev.config.js
|- webpack.prod.config.js
dist // 编译好的库文件
|- geoeye.js
|- geoeye.min.js
|- geoeye.min.js.map
src // 源代码
debug // 用于调试
test // 测试
package.json

配置webpack

Webpack 4刚发布,据说简化了配置,所以本项目就来尝尝鲜。Webpack 4相比原来需要额外安装一个webpack-cli,并且要求node版本不小于6.11.5,安装命令如下:

1
npm install --save-dev webpack webpack-cli

接下来就要配置webpack.dev.config.jswebpack.prod.config.js。webpack官方文档推荐用一个webpack.common.config.js提取公共配置后,再用webpack-merge合并。由于本项目配置比较简单,重复的地方不多,所以就不用引入额外的复杂度了。如果项目的配置复杂到同一种配置需要重复3次以上,那么还是需要采用webpack-merge合并的,因为同时更改3个地方很容易出错。

本项目的webpack.dev.config.js的配置如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
const path = require('path')
const webpack = require('webpack')

module.exports = {
entry: './src/index.js',
output: {
path: path.join(__dirname, '../dist'),
filename: 'geoeye.js',
library: 'geoeye',
libraryTarget: 'umd',
umdNamedDefine: true
},
devtool: 'cheap-module-eval-source-map',
devServer: {
contentBase: './debug',
publicPath: '/dist/',
hot: true,
open: true,
overlay: true
},
module: {
rules: [
{
test: /\.js$/,
loader: 'babel-loader',
exclude: /node_modules/
}
]
},
mode: 'development',
plugins: [
new webpack.HotModuleReplacementPlugin()
]
}

本项目的webpack.prod.config.js的配置如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
const path = require('path')
const webpack = require('webpack')

module.exports = {
entry: './src/index.js',
output: {
path: path.join(__dirname, '../dist'),
filename: 'geoeye.min.js',
library: 'geoeye',
libraryTarget: 'umd',
umdNamedDefine: true
},
devtool: 'source-map',
module: {
rules: [
{
test: /\.js$/,
loader: 'babel-loader',
exclude: /node_modules/
}
]
},
mode: 'production'
}

以上有以下几个地方需要注意:

1.entry中使用相对于当前目录时,./不能省略,即./src/index.js不能写为src/index.js
2.output中的path一定要是绝对路径;
3.libraryTarget设为umd以兼容浏览器和commonjs环境;
4.webpack 4新引入了mode配置,会自动做一些优化,可以为developmentproduction,不能省略mode的配置;
5.modedevelopment时,HotModuleReplacementPlugin不是默认载入的,所以为了使开发时候能够热替换,需要手动加上这个配置;

配置babel

babel负责将高语言特性JS源代码转译为低语言特性JS代码,以兼容低版本浏览器,当前推荐采用babel-preset-env,它能根据要兼容的浏览器版本,有选择性地转译,而不是像以前一样统统转译为ES5。

使用以下命令安装babel以及配套工具:

1
npm install --save-dev babel-core babel-preset-env babel-loader

.babelrc配置如下:

1
2
3
4
5
6
7
8
9
10
11
12
{
"presets": [
[
"env",
{
"targets": {
"browsers": ["last 2 versions", "ie 11"]
}
}
]
]
}

配置eslint和prettier

eslint能够检查源代码中的格式错误以及少量的语法错误,prettier是用来自动地格式化代码。它们的主要区别在于,eslint主要用来检查代码格式,prettier主要用来修复代码格式。虽然eslint --fix也能自动修复一些格式错误,但只能修复少数几种格式错误,功能十分有限。prettier的格式修复功能很强,但是如果代码中有错误,例如有尾逗号、引用未知变量,prettier不管这些,仍然帮你格式化,这就让你很难提早发现代码中的错误。

eslint和prettier相爱相杀,让它们和谐相处,才能更好地为我们提供服务。总体思想是eslint的检查规则尽量与prettier的格式规则保持一致,代码先用prettier格式化之后再用eslint --fix修复并检查。

需要先安装一下几个包:

1
npm install --save-dev eslint prettier eslint-config-prettier eslint-plugin-prettier prettier-eslint-cli

eslint-config-prettier是用来将prettier的格式化规则作为eslint的检查规则,eslint-plugin-prettier则是用来对比prettier格式化前后,代码中出现的错误。prettier-eslint-cli是用来依次执行prettier和eslint --fix,自动格式化代码。

.eslintrc的配置如下:

1
2
3
4
5
6
7
8
9
10
11
12
{
"env": {
"browser": true,
"es6": true
},
"parserOptions": {
"sourceType": "module"
},
"extends": ["prettier"],
"plugins": ["prettier"],
"rules": { "prettier/prettier": "error" }
}

需要指出的是如果使用了ES6的importexport,则需要配置"sourceType": "module"

我是无分号党,.prettierrc配置如下:

1
2
3
4
{
"singleQuote": true,
"semi": false
}

配置.gitignore

.gitignore用来排除不需要git管理的文件,配置如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
.DS_Store
node_modules/
dist/
npm-debug.log*
yarn-debug.log*
yarn-error.log*

# Editor directories and files
.idea
.vscode
*.suo
*.ntvs*
*.njsproj
*.sln

总结

配置一个Library开发环境,分为生成package.json、规划目录结构、配置webpack、配置babel、配置eslint和prettier、配置.gitignore几个步骤。本文仅仅是流水账记录,深度不够,写到后面我都觉得乏味了,以后不写这种水文章了,匿了。