egg 启动过程

前言:最近在写node后台,使用了egg,不了解框架的运行过程,更多的是在搬砖配置,心里没底!

今天翻出源代码,在此记录一下

启动命令行:egg-bin dev

为了看清运行过程,使用node调试:

进入项目运行 node --inspect-brk=6666  ./node_modules/egg-bin/bin/egg-bin.js dev 代码如下

// egg-bin/bin/egg-bin.js
const Command = require('..');new Command().start();复制代码

require('..')引入模块egg-bin/index.js EggBin

'use strict';
const path = require('path');
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'));
  }
}
module.exports = exports = EggBin;
exports.Command = Command;
exports.CovCommand = require('./lib/cmd/cov');
exports.DevCommand = require('./lib/cmd/dev');
exports.TestCommand = require('./lib/cmd/test');
exports.DebugCommand = require('./lib/cmd/debug');
exports.PkgfilesCommand = require('./lib/cmd/pkgfiles');复制代码

查看代码 EggBin extends Command,   Command继承Common-bin


接下来创建对象new Command().start();,先初始化顶级父类CommonBin,获取命令行参数dev

创建Map集合

class CommonBin {
  constructor(rawArgv) { 
   this.rawArgv = rawArgv || process.argv.slice(2);
   this.yargs = yargs(this.rawArgv);
   this.parserOptions = {
      execArgv: false,
      removeAlias: false, 
     removeCamelCase: false,
    };    
  this[COMMANDS] = new Map();
  } 
...

}复制代码

接下来是初始化父类,初始化一些参数

class Command extends CommonBin { 
 constructor(rawArgv) { 
   super(rawArgv);
    this.parserOptions = {
      execArgv: true,
      removeAlias: true,
    };    
this.options = {
  typescript: {
       description: 'whether enable typescript support, will load `ts-node/register` etc',
        type: 'boolean',
        alias: 'ts', 
       default: undefined,
      },  
  };  
}
...

}复制代码

接下来是初始化EggBin,重点看下load方法,load方法继承顶级父类CommonBin

class EggBin extends Command { 
 constructor(rawArgv) { 
   super(rawArgv); 
   this.load(path.join(__dirname, 'lib/cmd'));
  }
}复制代码

查看CommonBin下的load方法,读取lib/cmd文件目录,把上面流程图相关类 AutodCommand,CovCommand,DebugCommand,DevCommand,PkgfilesCommand,TestCommand使用require加载并存储在Map集合中

  load(fullPath) {
    const files = fs.readdirSync(fullPath);
    const names = []; 
   for (const file of files) { 
     if (path.extname(file) === '.js') { 
       const name = path.basename(file).replace(/\.js$/, '');
        names.push(name);
        this.add(name, path.join(fullPath, file));
      }    
    }  
}  


add(name, target) { 
   if (!(target.prototype instanceof CommonBin)) {
        target = require(target);
    }    
    this[COMMANDS].set(name, target); 
  }复制代码

这就初始化完毕了,接着运行new Command().start(),中的start方法,继承顶级父类CommonBin
其中co是第三方模块是Generator 函数的自动执行器

 start() {    
    co(function* () { 
     yield this[DISPATCH]();
    }.bind(this)).catch(this.errorHandler.bind(this));
  }

  * [DISPATCH]() { 
   const parsed = yield this[PARSE](this.rawArgv); 
   const commandName = parsed._[0];
   if (this[COMMANDS].has(commandName)) {
         const Command = this[COMMANDS].get(commandName); 
         const rawArgv = this.rawArgv.slice();
         rawArgv.splice(rawArgv.indexOf(commandName), 1);
         const command = new Command(rawArgv); 
         yield command[DISPATCH]();
         return;
    }    
    const context = this.context;
    yield this.helper.callFn(this.run, [ context ], this);
  }复制代码

调用 DISPATCH方法,根据命令行传递的参数dev,判断之前存储在Map集合中有没有对应的模块
找到DevCommand模块,初始化DevCommand模块,配置默认端口7001,加载真正的启动文件serverBin

class DevCommand extends Command {
  constructor(rawArgv) { 
   super(rawArgv);
   this.defaultPort = 7001; 
   this.serverBin = path.join(__dirname, '../start-cluster');
   };
  } 
...
}复制代码

初始化完成DevCommand 又调用了自己的DISPATCH方法,这次commandName=undifind
直接走this.helper.callFn

* [DISPATCH]() {
    const parsed = yield this[PARSE](this.rawArgv);
    const commandName = parsed._[0];
    if (this[COMMANDS].has(commandName)) {
      const Command = this[COMMANDS].get(commandName); 
      const rawArgv = this.rawArgv.slice(); 
      rawArgv.splice(rawArgv.indexOf(commandName), 1); 
      const command = new Command(rawArgv);
      yield command[DISPATCH]();
      return;
    }    
    const context = this.context;
    yield this.helper.callFn(this.run, [ context ], this);
  }复制代码

callFn是顶级父类CommonBin下的helper中的方法,判断run方法是generatorFunction,运行run

callFn = function* (fn, args = [], thisArg) {
      if (!is.function(fn)) return;

      if (is.generatorFunction(fn)) { 
           return yield fn.apply(thisArg, args);
      }  
     const r = fn.apply(thisArg, args); 
     if (is.promise(r)) {
        return yield r;
      }  
    return r;
};复制代码


其中参数run 方法来自子类DevCommand,开始forkNode创建子进程

 * run(context) { 
   const devArgs = yield this.formatArgs(context);
    const env = {      
        NODE_ENV: 'development',
        EGG_MASTER_CLOSE_TIMEOUT: 1000,
    };    
const options = {
      execArgv: context.execArgv, 
     env: Object.assign(env, context.env),
    };    
    yield this.helper.forkNode(this.serverBin, devArgs, options); 
 }复制代码

运行CommonBin下的helper中的方法forkNode,创建子进程,./node_modules/egg-bin/lib/start-cluster"

forkNode = (modulePath, args = [], options = {}) => {
  const proc = cp.fork(modulePath, args, options);
  gracefull(proc);
};复制代码

fork子进程./node_modules/egg-bin/lib/start-cluster
require(options.framework) 加载egg ./node_modules/egg"


const options = JSON.parse(process.argv[2]);
require(options.framework).startCluster(options);复制代码

查看Egg

'use strict';
exports.startCluster = require('egg-cluster').startCluster;
exports.Application = require('./lib/application');
exports.Agent = require('./lib/agent');
exports.AppWorkerLoader = require('./lib/loader').AppWorkerLoader;
exports.AgentWorkerLoader = require('./lib/loader').AgentWorkerLoader;
exports.Controller = require('./lib/core/base_context_class');
exports.Service = require('./lib/core/base_context_class');
exports.Subscription = require('./lib/core/base_context_class');
exports.BaseContextClass = require('./lib/core/base_context_class');复制代码

startCluster(options) 方法来自于require('egg-cluster')模块,查看

exports.startCluster = function(options, callback) {
  new Master(options).ready(callback);
};复制代码



转载于:https://juejin.im/post/5c80cc9de51d4539171254ce

你可能感兴趣的:(egg 启动过程)