本文大量引用了eggjs官方文档指引,并且根据文档创建了eggjs项目,进行了简单的分析,这仅是我个人的总结,轻拍。
根据公开的文献,eggjs的前身属于蚂蚁金服,于2016年9月亮相JSConf China并正式开源。它有一个很显著的特点:【微内核+插件机制】,专门为团队架构师提供框架定制的能力,可以基于eggjs来定制业务框架。
快速入门
$ npm i egg-init -g
$ egg-init egg-simple --type=simple
$ cd egg-simple
eggjs提供了一个命令行工具,来初始化工程,根据官网提供的信息,你可以看见整个工程的分布,每一个目录都是有明确意义的。
egg-project
├── package.json // 定义了项目所需要的各种模块,以及配置信息(名称、版本等元数据)
├── app.js (可选) // 用于自定义初始化工作,这个文件只能返回一个函数beforeStart
├── agent.js (可选) // 跟app.js类似。
├── app
| ├── router.js // 用于配置URL路由规则。
│ ├── controller // 对用户的请求参数进行处理,调用对应的service方法处理业务,返回结果
│ | └── home.js
│ ├── service (可选) // 复杂数据的处理,第三方服务的调用。
│ | └── user.js
│ ├── middleware (可选) // 用于编写中间件
│ | └── response_time.js
│ ├── schedule (可选) // 用于编写定时任务
│ | └── my_task.js
│ ├── public (可选) // 存放静态资源
│ | └── reset.css
│ ├── view (可选) // 用于放置模板文件
│ | └── home.tpl
│ └── extend (可选) // 用于框架的扩展
│ ├── helper.js (可选)
│ ├── request.js (可选)
│ ├── response.js (可选)
│ ├── context.js (可选)
│ ├── application.js (可选)
│ └── agent.js (可选)
├── config
| ├── plugin.js // 用于配置需要加载的插件
| ├── config.default.js // 默认配置文件
| ├── config.prod.js // 生产环境配置文件
| ├── config.test.js (可选)
| ├── config.local.js (可选)
| └── config.unittest.js (可选)
└── test // 单元测试
├── middleware
| └── response_time.test.js
└── controller
└── home.test.js
工程分析
正常情况下,我们几乎最常用的内置对象或者配置,一个内置对象描述了一个请求的生命周期,它在一个controller对象的上下文中可访问。
-
Application,一个应用只会实例化一个,在它的上面可以挂载一些全局方法或属性
-
Context,请求级别对象,一个请求的生命周期内,框架会实例化一个Context,这个对象封装了这次请求的所有信息
-
Request,提供了一系列的方法来获取HTTP的信息
-
Response,提供了一系列的方法来设置HTTP的信息
由于eggjs也是基于Koa来进行的封装,所以洋葱模型能支持的特性,在eggjs中同样支持。Koa的设计思路是为中间件提供一系列的语法糖,来增强其互用性和健壮性,但有时候中间件并不能提供所有的能力。eggjs提供的插件机制,就很好的辅助解决了这个问题。再这些跟请求相关的对象之外,eggjs对开发模型也进行了封装。
-
Controller,框架提供了一个Controller基类,并且约定放置在app/controller目录下,这一层主要是对用户请求参数进行处理,Controller的上下文中挂载了很多有用的对象,因此推荐所有Controller都继承该基类。
-
Service,框架提供了一个Service基类,并且约定放置在app/service目录下,这一层主要解决在复杂业务场景下对逻辑的封装,比如查询数据库,调用第三方服务(Github登录),因此推荐所有Service都继承该基类。
-
Helper: 用来提供一些使用的实用函数。可以将一些常用的动作抽象为helper.js里面的一个独立函数。Helper跟Controller基类有一样的属性,也会在每次请求时实例化。
-
Config: 所有框架、插件和应用级别的配置都可以通过Config对象获取到。
-
Logger: 日志
框架提供了强大可扩展的配置功能,可以自动Merge插件,应用,框架的配置,并且按顺序覆盖,这是一个很有用的功能,可以很容易的配置你的Web应用。
在此之外,我们可以全面窥视一下eggjs能支持的其他能力
-
Middleware,我们约定一个中间件放置在app/middleware目录下,如果你想它的能力可以应用在整个Web应用中,那么就需要配置在config.xxx.js文件中。如果仅仅是应用在请求级别上,那么需要在具体的路由上进行配置
-
Router,它主要是来约定描述请求URL和具体承担执行动作的controller,框架约定了app/router.js来描述统一的路由贵族。
-
定时任务,它主要是来处理一些需要定时的任务处理,框架约定放置在app/schedule目录下
-
单元测试,这一层的目的是对逻辑的测试检验,提供健壮性的验证,框架约定放置在app/test目录下
-
框架的扩展能力,它指明了Application,Context,Request,Response,Helper是可以进行扩展的,我们可以增加这些对象的api,来进一步加强框架的能力
在整套框架中我很看重安全性,eggjs总结阿里巴巴的很多经验,并且默认提供了这样的支持,这一点很帮,节省了一些考虑这方面的时间,框架本身针对 Web 端常见的安全风险内置了丰富的解决方案。
-
利用 extend 机制扩展了 Helper API, 提供了各种模板过滤函数,防止钓鱼或 XSS 攻击
-
常见 Web 安全头的支持。可以针对某一路径开启或忽略安全校验
-
CSRF 的防御方案
-
灵活的安全配置,可以匹配不同的请求 url
-
可定制的白名单,用于安全跳转和 url 过滤
-
各种模板相关的工具函数做预处理
-
在框架中内置了安全插件 egg-security, 提供了默认的安全实践
框架简单分析
阅读到这里,其实大家基本上对eggjs有了一个大概的应用,这里的描述主要是简单分析一下eggjs的运行机制,它有助于我们理解eggjs框架,独创的Loader机制实现了加载的约定,并且抽象了很多底层api可以进一步的扩展,这样的加载机制,几乎做到了无感知。
-
按插件 => 框架 => 应用依次加载
-
插件之间的顺序由依赖关系决定,被依赖方先加载,无依赖按 object key 配置顺序加载,具体可以查看插件章节
-
框架按继承顺序加载,越底层越先加载
如果你要真正的部署Web应用,由于JavaScript的单线程特点,注定无法享有多核运算的好处,但是Node.js提供了Cluster模块,它要解决的问题,主要就是来享用多核运算的好处,我们可以简单的看一下Cluster模块的功能:
-
在服务器上同时启动多个进程
-
每个进程里都跑的是同一份源代码(好比把以前一个进程的工作分给多个进程去做)
-
更神奇的是,这些进程可以同时监听一个端口
这里有一个很有意思的特点,启动一个Master进程来负责启动其他进程,其他进程类比Worker,Worker进程来接收请求,对外提供服务,每一个Worker之间的通信,都需要借助Master,由Master来转发。(额外话题,这里是不是和React的兄弟组件通信很像,都需要一个Master来做中转)
虽然看起来,很容易,其实实现起来很多细节问题,要处理。
最后,如果你开发Node.js Web应用,我强烈推荐使用eggjs,这是一个很值得学习的Web基础框架。
长按二维码关注公众号:
你也可以关注我的新浪微博,搜索i_icepy,很期待和大家交流 。