egg 啓動過程

前言:最近在寫node後臺,使用了egg,不瞭解框架的運行過程,更多的是在搬磚配置,內心沒底!javascript

今天翻出源代碼,在此記錄一下java

啓動命令行:egg-bin devnode

爲了看清運行過程,使用node調試:typescript

進入項目運行 node --inspect-brk=6666  ./node_modules/egg-bin/bin/egg-bin.js dev 代碼以下
promise

// egg-bin/bin/egg-bin.js
const Command = require('..');new Command().start();複製代碼

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

'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
app


接下來建立對象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();
  } 
...

}複製代碼

接下來是初始化父類,初始化一些參數ui

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);
};複製代碼
相關文章
相關標籤/搜索