Library项目的Webpack配置

673 阅读6分钟
原文链接: jingsam.github.io

知乎上有个提问,叫“如何成为高级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配置:

{
  "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.jswebpack.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配置,会自动做一些优化,可以为developmentproduction,不能省略mode的配置;
5.modedevelopment时,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不管这些,仍然帮你格式化,这就让你很难提早发现代码中的错误。

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

需要先安装一下几个包:

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的配置如下:

{
  "env": {
    "browser": true,
    "es6": true
  },
  "parserOptions": {
    "sourceType": "module"
  },
  "extends": ["prettier"],
  "plugins": ["prettier"],
  "rules": [{ "prettier/prettier": "error" }]
}

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

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

{
  "singleQuote": true,
  "semi": false
}

配置.gitignore

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

.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几个步骤。本文仅仅是流水账记录,深度不够,写到后面我都觉得乏味了,以后不写这种水文章了,匿了。