Egg 框架源码解析— Egg-bin 模块

1,709 阅读3分钟

为什么要研究 egg 源码

目前框架种类百花齐放,这是一件好事,促进 Node 本身的发展,开发者也有更多的选择。关于众多框架孰优孰劣我不想参与讨论。

egg 在这么多框架中,也有属于自己的大批使用者, 它解决了我实际生产问题,我曾得益于 egg, 我想去了解 egg,仅此而已。

框架 Stats

image.png

egg-bin 担任的角色

egg-bin 是整个框架的入口,负责开发环境下命令行的解析(启动、调试、测试)。本章主要从源码为大家解析 egg 的 dev 启动流程。

在初始化的 egg 项目 package.json 中可以看到:


"scripts": {

    "dev": "egg-bin dev",

    "debug": "egg-bin debug",

    "test-local": "egg-bin test",

    "cov": "egg-bin cov"

}

egg 项目 package.json 中看到 devdebugtest-localcov 命令都是依赖 egg-bin 启动的。

egg-bin 源码解析

目录结构如下

package.json
index.js
bin
├── egg-bin.js // 执行入口
├── ets.js
└── mocha.js
lib
├── cmd  // 命令行解析目录
│ ├── autod.js
│ ├── cov.js // npm run cov 执行js
│ ├── debug.js
│ ├── dev.js  // npm run dev 执行js
│ ├── local.js
│ ├── pkgfiles.js
│ └── test.js // npm run test 执行js
├── command.js
└── start-cluster

package.json 入口

egg-bin 模块的 package.json 中可以看到 bin 入口文件是 /bin/egg-bin.js


"bin": {

    "egg-bin": "bin/egg-bin.js",

    "mocha": "bin/mocha.js",

    "ets": "bin/ets.js"

},

bin/egg-bin.js


#!/usr/bin/env node

'use strict';

const Command = require('..');

new Command().start();

Command 对象来自 index.js, index.js 代码如下:


const Command = require('./lib/command');

class EggBin extends Command {

    constructor(rawArgv) {

        super(rawArgv);

        this.usage = 'Usage: egg-bin [command] [options]';

        this.load(path.join(__dirname, 'lib/cmd')); // 加载命令行参数对应的 js 文件

    }

}

module.exports = exports = EggBin;

index.js 中我们看到 Command 对象就是 EggBin 类,该类又继承自 Command,关于命令行实现和处理都在该类上实现的。

common-bin 命令行解析

接下来进入 ./lib/command.js


'use strict';

const path = require('path');

const fs = require('fs');

const BaseCommand = require('common-bin');

class Command extends BaseCommand {

    ...

}

module.exports = Command;

从代码中可以看出 Command 类又继承自 BaseCommand, 由此发现 egg-bin 模块命令的实现使用的是 common-bin 公共模块。

这里和大家简单说明一下 common-bin 模块使用(git 仓库代码使用实例)。

前面在 EggBinconstructor 中调用了 this.load 方法


this.load(path.join(__dirname, 'lib/cmd'));

该方法加载了 lib/cmd 目录下的 js 文件:

  • autod.js

  • cov.js

  • debug.js

  • dev.js

  • pkgfiles.js

  • test.js

相当于定义了 autodcovdebugdevpkgfilestest命令,比如当我们执行 npm run dev 的时候,就会执行 dev.js

相当于在 egg-bin 模块目录执行了:


node ./bin/egg-bin.js dev

所以 Egg 启动 npm run dev 执行的是 dev.js 文件。

开启 master 进程

dev.js 主要包含三个功能

  1. 命令行参数设置(根目录、端口、进程数量..)

  2. 进程参数格式化(参数传递、端口探测是否可用)

  3. 进程启动


class DevCommand extends Command {

    constructor(rawArgv) {

        this.options = { // 命令行参数设置

        ...

    };

}
* run(context) {

    ...

    const task = this.helper.forkNode(this.serverBin, devArgs, options); // 进程启动

}
* formatArgs(context) { //进程启动参数格式化

    ...

    const port = yield detect(this.defaultPort);

}
module.exports = DevCommand;

进程在启动之前的参数,也就是 formatArgs 方法的返回值,调式输出如下:


{
    ...

    tscompiler: 'ts-node/register',

    workers: 1, // 进程数量

    baseDir: '/Users/***/study/eggStudy/egg-bin', // 当前进程根目录

    port: 7001, //端口

    framework: '/Users/***/study/eggStudy/egg-bin/node_modules/egg' //框架目录
}

启动进程是执行 this.helper.forkNode , 最开始我以为 this.helper.forkNode 方法是 EggBin 类上面的方法,源码翻一遍也没找到,后来发现该方法是 common-bin 模块的方法,用来 fork 一个子进程。this.serverBin 参数就是执行文件,该参数就是 lib/start-cluster 文件。

start-cluster 文件:


require(options.framework).startCluster(options);

npm run dev 开始执行一系列代码后,暂时走进了 start-cluster 中的代码, egg-bin 模块的任务就结束了。

后续将为大家解析重点模块 —— Egg-cluster ! 希望大家点个赞支持一下 ❤️❤️❤️

下一章 Egg-Cluster 源码解析