重学 webpack

第三章:编写可维护的 webpack 构建配置

2020-01-04  本文已影响0人  晓风残月1994

演示仓库地址:https://github.com/wangpeng1994/builder-webpack

1. 构建配置包设计

将 webpack 配置包抽离成 npm 包,好处如下:

可选方案:

不同环境采用不同的 webpack 配置:

通过 webpack-merge 工具更好的合并 webpack 配置项,如 dev 的最终配置就是合并自 base 配置:

const merge = require('webpack-merge');
const baseConfig = require('./webpack.base');

const devConfig = {
  mode: 'development',
  devServer: {
    contentBase: './dist',
    hot: true,
    stats: 'errors-only',
  },
  devtool: 'cheap-source-map',
};

module.exports = merge(baseConfig, devConfig);

2. 功能模块设计和目录结构

image.pngimage.png

目录结构:

+ |- /test
+ |- /lib
+   |- webpack.dev.js
+   |- webpack.prod.js
+   |- webpack.ssr.js
+   |- webpack.base.js
+ |- README.md
+ |- CHANGELOG.md
+ |- .eslinrc.js
+ |- package.json
+ |- index.js

3. 使用 ESLint 规范构建脚本

// .eslintrc.js

module.exports = {
  "parser": "babel-eslint",
  "extends": "airbnb-base",
  "env": {
    "browser": true,
    "node": true
  }
};
// package.json

"scripts": {
  "eslint": "eslint ./lib --fix", // eslint --fix 可以自动处理空格
  // ...
},

4. 冒烟测试和实际运用

冒烟测试是指对提交测试的软件在进行详细深入的测试之前而进行的预测试,这种预测试的主要目的是暴露导致软件需重新发布的基本功能失效等严重问题。

目录结构:

template 目录里存放之前学习 webpack 时的演示项目,与 template 目录平级的是用于冒烟测试的脚本。

image.pngimage.png
// index.js

/**
 * 测试构建是否成功
 */

const process = require('process');
const path = require('path');
const webpack = require('webpack');
const rimraf = require('rimraf');
const Mocha = require('mocha');

const mocha = new Mocha({
  timeout: '10000ms'
});

// 变更当前 node 进程的工作目录为 template 目录
// 之后遇到的除了 require() 方法之外的相对目录,都是相对于工作目录
// require() 方法中的路径是指相对于当前文件的路径

process.chdir(path.join(__dirname, 'template'));

// 先删除输出目录
rimraf('./dist', () => {
  const prodConfig = require('../../lib/webpack.prod.js');

  // 直接引入 webpack 方法并传入配置文件后执行
  webpack(prodConfig, (err, stats) => {
    if (err) {
      console.error(err);
      process.exit(2);
    }
        // 这里观察是否有成功构建后的输出(是否冒烟)
    console.log(stats.toString({
      colors: true,
      modules: false,
      children: false
    }));

    console.log('Webpack build successfully, begin to run tests.')
    
    // 下面继续测试基本功能是否正常

    // 这里 __dirname 是指当前文件(index.js)所在目录的绝对路径,所以也可以不受当前进程工作路径的影响
    mocha.addFile(path.join(__dirname, 'html-test.js'));
    mocha.addFile(path.join(__dirname, 'css-js-test.js'));

    mocha.run();
  });
});
// css-js-test.js

/**
 * 测试构建完成后的 build 目录是否有内容输出
 */

const glob = require('glob-all');

describe('Checking generated css js files', () => {
  it('should generate css js files', (done) => {
    const files = glob.sync([
      './dist/index_*.js',
      './dist/index_*.css',
      './dist/search_*.js',
      './dist/search_*.css'
    ]);

    if (files.length > 0) {
      done();
    } else {
      throw new Error('no css js files generated');
    }
  });
});
// html-test.js

/**
 * 测试构建完成后的 build 目录是否有内容输出
 */

const glob = require('glob-all');

describe('Checking generated html files', () => {
  it('should generate html files', (done) => {
    const files = glob.sync([
      './dist/index.html',
      './dist/search.html'
    ]);

    if (files.length > 0) {
      done();
    } else {
      throw new Error('no html files generated');
    }
  });
});

5. 单元测试和测试覆盖率

image.pngimage.png
image.pngimage.png

技术选型:Mocha + Chai(演示中虽然没有使用 Chai,但大同小异,使用了 assert,来自于 nodejs 的断言 API)

// index.js

/**
 * 单元测试入口文件
 */

const path = require('path');

process.chdir(path.join(__dirname, 'smoke/template'));

describe('builder-webpack test case', () => {
  require('./unit/webpack-base-test');
  // 可以继续测试其他环境的 webpack 文件
});
// webpack-base-test.js

var assert = require('assert');

describe('webpack.base.js test case', () => {
  const baseConfig = require('../../lib/webpack.base.js');
  console.log(baseConfig);
  
  it('entry', () => {
    assert.equal(baseConfig.entry.index.includes('builder-webpack/test/smoke/template/src/index/index.js'), true);
    assert.equal(baseConfig.entry.search.includes('builder-webpack/test/smoke/template/src/search/index.js'), true);
  });
  
  // 可以继续测试其他字段
});

istanbul 可以输出测试覆盖率,nyc (内部依赖于 istanbul)结合 package.json 中的 scripts 脚本使用起来更方便。

// package.json

"scripts": {
  "test": "nyc mocha",
  "test:smoke": "node ./test/smoke/index.js",
  // ...
},
image.pngimage.png

6. 持续集成和 Travis CI

当前项目构建地址:https://travis-ci.org/wangpeng1994/builder-webpack

  1. https://travis-ci.org/ 使用 GitHub 账号登录
  2. https://travis-ci.org/account/repositories 为项目开启
  3. 项目根目录下新增 .travis.yml
language: node_js

sudo: false

cache:
  apt: true
  directories:
    - node_modules

node_js: stable

install:
  - npm install # 安装构建器依赖
  - cd ./test/smoke/template
  - npm install # 安装模板项目依赖
  - cd ../../../

script:
  - npm run test
  - npm run test:smoke

7. 发布构建包到 npm

具体怎么发送,参考其他文章即可,保障网络通畅,使用原版 npm 而不是 cnpm,并且暂时切换回原版仓库地址(如果使用了 taobao 镜像的话),然后去 npm 注册个账号,npm adduser、npm login、npm publish,注意 package.json 中的包名 name 不能和仓库中现有的重名。

升级版本,可以使用 npm version xxx,会自定更新 package.json 中的版本号,并且打 tag:

使用方法见:https://www.npmjs.com/package/xiaofeng-builder-webpack

8. Git 规范和 CHANGELOG 生成

良好的 Git commit 规范优势:

技术方案:

以后用 git cz 命令代替 git commit 即可。

继续在 package.json 的 scripts 中和 npm version 结合,实现自动升版本之前运行测试用例,然后升级版本,并且自动打 tag 并自动更新 CHANGELOG.md 文件,之后自动推送 tag 和代码。

npm 会在执行 npm version 时,按顺序执行 preversion -> version -> postversion:

// package.json

// ...
 "scripts": {
    "test": "nyc mocha",
    "test:smoke": "node ./test/smoke/index.js",
    "eslint": "eslint ./lib --fix",
    "commit": "git-cz",
    "preversion": "npm test",
    "version": "conventional-changelog -p angular -i CHANGELOG.md -s -r 0 && git add CHANGELOG.md",
    "postversion": "git push origin --tags && git push"
  },
  "config": {
    "commitizen": {
      "path": "./node_modules/cz-conventional-changelog"
    }
  },
// ...

所以现在的开发流程如下:

了解一下提交格式要求(用了 commitizen,命令行中看着选菜单即可):

image.pngimage.png image.pngimage.png

根据提交时的 feat、fix 等元信息生成的 CHANGELOG 形如:

image.pngimage.png

9. 语义化版本(Semantic Versioning)规范格式

开源项目(React)版本信息案例:

image.pngimage.png

语义化版本(Semantic Versioning)规范格式:

先行版本号:

先行版本号可以作为发布正式版之前的版本,格式是在修订版本号后面加上一个连接
号(-),再加上一连串以点(.)分割的标识符,标识符可以由英文、数字和连接号
([0-9A-Za-z-])组成。

上一篇下一篇

猜你喜欢

热点阅读