egg.js使用指南

官方教程有点跳跃,很多东西没讲清楚,不太适合小白理解,特此整理、归纳一下。
打开这篇博客的正确方式是:先读一遍官方教程,读不懂的可以略过,然后再带着问题来看这篇文章。然后再回去读官方文档,去理解。最终目的是理解官方文档,我的文章并不权威,只是起到帮助理解文档的作用。

零、egg.js是什么?

答案:功能更丰富、更规范的koa
使用koa时,你要写一个项目,要往里面加很多中间件,要写脚本加载routes文件夹下面的所有路由以及model文件夹下面的所有sequelize模型,koa仅仅是一个骨架,其他的都是你来完成,自由度高,但集成度低,每创建一个新项目都要做很多重复工作。egg.js是封装了一套koa,可以理解成大礼包版koa,集成度高,可以轻松创建一个项目而不用做很多繁琐的初期工作,解放生产力,更可贵的是有一套现成的规范提供给我们,不需要我们自己再去探索一套规范,比如router放哪里,controller放哪里,需不需要service,哪些放在service等等。

一、架构概览

根据egg.js目录结构先了解其项目规范,为了了解这些目录/文件是做什么,先从我们最熟悉的request讲起:

egg.js项目架构

绿色虚线框中的所有组件组成了一个Worker,这就是egg.js中实际执行代码逻辑的进程,是一个node服务器。

  • request进来后,先穿过中间件,自己定义的中间件都放在projectDir/app/middleware下,并在config中启用。
  • egg.js内置了egg-static中间件,将静态资源放在projectDir/app/public中,只会经过egg-static中间件之前的中间件,最后egg-static直接响应给客户端,不会到达其后的中间件以及Router。
  • 如果不是public中的资源,将会穿越所有中间件,到达路由。一般所有的路由都放在router.js中,这个文件没有任何逻辑,而是直接指向一个处理请求的controller,只起到目录和索引的作用:
    router.get('/users', controller.user.getAll);
    
  • Controller都放在projectDir/app/controller中,不含有具体的业务逻辑,业务逻辑都在Service中,Controller只负责调用并组合Service,最后将响应提交给客户端。
  • Service放在projectDir/app/service中,负责调用Model,进行具体业务。
  • 除此之外,Worker中还有定时任务,写在projectDir/app/schedule文件夹中。
  • 各个部件的所有可操控行为,都可以在projectDir/confg/中的配置文件中定义,配置文件可以同时有很多份,default会被具体环境的配置文件中的同名字段覆盖,具体使用哪份配置,是根据EGG_SERVER_ENV这个环境变量的值。

二、扩展内置对象

在进行下面的阅读前请保证已经理解了egg.js中的内置对象
内置对象可以被方便地获取到,不过功能有限,我们可以通过egg.js的扩展(Extend)功能去进一步加强、定制框架的能力。
egg.js中有非常多新鲜的特性:“扩展”、“插件”、“多环境配置”,这些特性名称虽然不一样,但本质都是一样的:有则覆盖,无则增加。类似于lodash中的defaults函数,也类似于继承。
因此,如果我们想扩展Application对象,根据egg.js规范,应该在projectDir/app/extend/下增加application.js:

// app/extend/application.js
module.exports = {
  specialName: 'hj's app'
}

以后就可以方便地调用app.specialName获取这个值。

三、 扩展应用:插件

Extend特性可以扩展上层框架的内置对象,而插件则可以扩展除router和controller之外的整个app。插件拥有自己的package.json,因此可以独立发布到npm,每个人都可以install,享用你的扩展。
如果我要为项目写一个管理微信公众号的功能,我会写一个WxService:

// app/service/wx.js
const Service = require('egg').Service;
const request = require('request-promise');

class WxService extends Service{
  async getUserInfo(openId){
      return request.post({
        uri: 'https://wx.user.com',
        body:{
          openId
        }
      });          
  }
}

很多项目都可以用到这个Service,因此我会提取为一个插件,然后通过引入插件的形式去引入,我在应用中同样可以调用这个Service,等于是把插件中的文件往应用中复制了一份,和写在应用中没什么两样。
关于如何提取插件,请参见:渐进式开发

四、定制自己的框架

定制自己的框架可以确定项目的技术选型、减少项目初期的工作,定制框架的思想其实和扩展内置对象、开插件是一样的,但是前置工作会比较多一些,参见:egg.js框架开发
这些前置工作比较重复、有固定格式,没有必要自己写,建议用骨架搭建。

$ npm install egg-init -g
$ egg-init --type=framework myegg
$ cd myegg

五、 Loader加载顺序

当我们基于自己定制的框架framework1,并且在应用中依赖了插件plugin2、plugin3,开发了一个应用:


app结构

其中framework1直接基于egg并且内置了plugin1,此时整个app的加载顺序是怎样的呢?
加载原则总结一句话是:从被依赖到依赖。


依赖顺序

先来分析一下,谁被依赖,谁依赖:

  • 插件可能被其他插件、框架和应用依赖
  • 框架可能被上层框架和应用依赖
  • 应用不被依赖
    插件处于被依赖队列的最前方,框架其次,应用最后,因此先加载顺序如图所示,那么我们刚刚的例子,加载顺序应该是:
    从上至下加载

    更多关于Loader的信息请见官方文档:Loader

六、多进程模型

为了最大程度利用多核、增强Node进程健壮性,一般我们会使用PM2一类的工具,如果使用egg.js,就完全不需要担心了,egg利用cluster模块(了解cluster原理请看这篇文章)已经创建了一个非常稳定的多进程模型。

多进程模型

使用egg-bin命令启动时,会启动一个Master进程,Master进程会先fork一个Agent,再根据核数启动对应数量的Worker。Master会监听端口,将请求转发给相应Worker处理,当Worker出错时Master重新fork一个Worker保证服务器健壮性。
Agent是一个特殊的Worker,不做具体的业务逻辑,只负责调度Worker,比如哪个Worker执行定时任务。
Agent与Worker的通行时通过Master转发,也就是每个人只知道Master,而不能互相通信,两个Worker之间也需要通过Master转发消息。
总结:

  • Master只负责Worker的初始化、重启、转发IPC消息等进程性工作。
  • Agent负责egg中的Worker调度。
  • Worker负责执行具体的业务逻辑。
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,332评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,508评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,812评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,607评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,728评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,919评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,071评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,802评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,256评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,576评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,712评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,389评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,032评论 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,798评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,026评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,473评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,606评论 2 350

推荐阅读更多精彩内容

  • 1、通过CocoaPods安装项目名称项目信息 AFNetworking网络请求组件 FMDB本地数据库组件 SD...
    阳明先生_X自主阅读 15,969评论 3 119
  • 用到的组件 1、通过CocoaPods安装 2、第三方类库安装 3、第三方服务 友盟社会化分享组件 友盟用户反馈 ...
    SunnyLeong阅读 14,608评论 1 180
  • 你以为你的世界是围着谁转动?你以为谁的世界在围着你转动? 你是你世界的主角,她是她世界的主角。你就...
    马禾阅读 1,476评论 3 3
  • 第一章 “成川,你给我站住。” “成川,你给我站住。”这个声音把成川吓了一跳,这时的成川压根就...
    淡淡的甜味阅读 854评论 0 17
  • 开发环境与项目创建请参考 Cordova webapp实战开发(一)- 环境布置项目环境已经布置好第一个项目也已经...
    travin阅读 1,176评论 0 0