正文
知乎上有个提问,叫“如何成为高级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 init
或
yarn init
可以快速地生成
package.json
文件。我通常习惯于先在
scripts
中把主要的开发命令写出来,以下是我的
scripts
配置:
{
"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
配置为:
{
"main": "dist/geoeye.js"
}
规划目录结构
我认为工程的目录结构非常重要,它能够反映代码的模块划分,好的目录结构让人赏心悦目、容易理解。我按照以下目录进行组织:
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,安装命令如下:
npm install --save-dev webpack webpack-cli
接下来就要配置
webpack.dev.config.js
和
webpack.prod.config.js
。webpack官方文档推荐用一个
webpack.common.config.js
提取公共配置后,再用
webpack-merge
合并。由于本项目配置比较简单,重复的地方不多,所以就不用引入额外的复杂度了。如果项目的配置复杂到同一种配置需要重复3次以上,那么还是需要采用
webpack-merge
合并的,因为同时更改3个地方很容易出错。
本项目的
webpack.dev.config.js
的配置如下:
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
的配置如下:
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
配置,会自动做一些优化,可以为
development
或
production
,不能省略
mode
的配置;
5.
mode
为
development
时,HotModuleReplacementPlugin不是默认载入的,所以为了使开发时候能够热替换,需要手动加上这个配置;
配置babel
babel负责将高语言特性JS源代码转译为低语言特性JS代码,以兼容低版本浏览器,当前推荐采用
babel-preset-env
,它能根据要兼容的浏览器版本,有选择性地转译,而不是像以前一样统统转译为ES5。
使用以下命令安装babel以及配套工具:
npm install --save-dev babel-core babel-preset-env babel-loader
.babelrc
配置如下:
{
"presets": [
[
"env",
{
"targets": {
"browsers": ["last 2 versions", "ie 11"]
}
}
]
]
}
配置eslint和prettier
eslint能够检查源代码中的格式错误以及少量的语法错误,prettier是用来自动地格式化代码。它们的主要区别在于,eslint主要用来检查代码格式,prettier主要用来修复代码格式。虽然
eslint --fix
也能自动修复一些格式错误,但只能修复少数几种格式错误,功能十分有限。prettier的格式修复功能很强,但是如果代码中有错误,例如有尾逗号、引用未知变量,prettier不管这些,仍然帮你格式化,这就让你很难提早发现代码中的错误。