去年年初对 Node.js 比较感兴趣,也用了很多 Node.js 的框架,但是开发体验不是特别好,我之前也是后端转前端,然后再接触 Node.js ,所以用过挺多的服务端框架,相对 js 而言,设计一款服务端框架并不容易,本人也不太愿意使用 typescript (为什么不用 java,请勿吐槽)编写并且基于 ES6 对入门的小伙伴会更友好一些,然后自己动手开发了一个 Node.js 的 Web 框架,快过年了才有时间写文章(手动狗头),在这里给大家分享一下开发经历。


注:目录只是为了好看,想到什么写什么,没有文笔可言,小白文。


选型

对于框架底层,想过自己开发一套(成本太高,并且考虑到生态问题)被我否决了,然后比较了 Koa2 与 Express 最终选择 Koa2 作为默认底层(最后由于框架的架构设计,koa2 服务或者 express 都可以作为底层库 *),不过最后还是选择了 koa2 默认集成。

既然选择了 koa2 那自然也是兼容 koa2 的生态圈


架构设计

刚开始开发的时候其实是顺着 koa 的路子走的, 以 koa 作为底层,对 koa 的 ctx 进行扩展,后来觉得这样子封装一个 koa 的全家桶貌似没什么意义(晚上一大堆,造轮子没什么意义,和别的框架有啥区别,请原谅我这老土的想法 *),然后开始思考 *:做这个框架的初衷和意义。

作为一个后端过来的,自然就想到了用 IOC 容器作为底层更优雅,但是 js 并没有类型约束,接口等特性,也看过很多 typescript 的实现(和其他后端框架并无明显区别,不是我吐槽,其他语言的更完善更安全性能更好),我下定决心要写一个 js(ES6)版的出来(学习成本低,更好入门 * 就是这么自以为是),然后就这样开启了我的 Node.js 之旅。

写偏了。。下面介绍一下这个框架的架构:

以容器作为底层,应用类集成容器基类并绑定在容器中,是应用程序对象也是容器的保姆。

其他所有的服务(包括 koa 、router、logger、validate、request、response 等等)都是以提供者的形式在应用程序中注册(实际绑定到了容器)。

容器开发

前期没想那么多,开发容器也很顺利,在设计依赖注入模式的时候(由于没有接口),想躲过很多方案,最后决定使用装饰器(真香),不是 ts,使用 ECMA 草案中装饰器(使用 babel转码),最后1.0定稿以后,会成为可选方案,装饰器可以增加开发体验,但不是必须的,并且强烈推荐的模式进行设计。

例如我们开放一个端点(路由),装饰器例子,不是注入:

@Router('users')
class UserController {
    @Get()
    index() {
        // ...
    }
}复制代码

这种模式进行开发,上述例子开放了一个 GET /users 的访问端点

那如果进行注入呢:

class UserController {
    @Config() config;
@Request()
index(request) {
    <span class="hljs-keyword">this</span>.request.param()
    <span class="hljs-keyword">this</span>.config.get(<span class="hljs-string">'app.port'</span>)
}

}复制代码

我们可以对属性,方法,构造函数进行注入

原理是使用装饰器标记控制器属性方法等需要注入的参数,然后调用函数的时候从容器中取出(这里碰到个坑),由于http服务请求的上下文在回调函数中,所以我绑定了一个回调函数到容器中,需要获取实例的时候将上下文传入函数中,生成例如request对象的实例。


提供者

这时候开发的框架,各种服务默认绑定在容器中,与应用类耦合,虽然是框架自带的服务,但是还是不够完美,所以借鉴了提供者的设计模式,将所有服务抽离并设计注册服务的 api,在框架启动时,自动注册默认服务。


这样子,我们的所有服务与框架底层核心完全解耦,保证了底层核心的精简,并具有强大的可伸缩性。


模块化

既然是一个 web 框架,使用的时候肯定会承载不同的业务,所以我们需要使用模块化功能拆分业务,提升可维护性,比如可以设定这个模块包含了哪几个控制器(支持通配符),这个模块需要加载哪些中间件,甚至子模块功能

所以我就设计了这么一套方案,使用模块描述类来定义模块

module.exports = class ExampleModule {
    // 标示子模块
    modules = [];
<span class="hljs-comment">// 标示需要装载的控制器</span>
controllers = [];

<span class="hljs-comment">// 标示需要加载的中间件</span>
middlewares = [];

}复制代码

* good

请求

作为 web 框架,肯定需要解析请求啊什么的,既然不是扩展 ctx 属性,那么我的方案就是使用 Request 类来解析 ctx, 这样的好处就是,我可以解析koactxexpressreqres或者其他框架的上下文对象,并且这个类是注册在容器中的,如果你有其他的解析方案,当然也可以自己注册一个,然后为所欲为(没错,IOC容器就是可以为所欲为 *)。

响应

生产响应的时候肯定也要越方便越好,方便到你只需要在控制器中return就好,可以return各种类型,除了koa2中的支持的数据类型,还支持直接返回 框架的 View(视图,即模板)实例或者Response(响应类)实例等等,框架都会自动判断。

class UserController {
    index() {
        return [{ id: 1 }]
    }
}复制代码

就是这么方便,当然不仅仅这样,还有更多强大的功能。

验证器

我在使用很多框架的时候,验证请求数据不是很方便,所以也重新设计了一套方案(狗头),

当然还是使用装饰器模式

// 定义一个验证类,放在指定目录
class UserPostValidate extends Validate {
    @MaxLength(10) username;
    @Length(1, 20) password:
}复制代码

然后在控制器中

class UserController extends Controller {
    store() {
        this.request.validate('UserPostValidate')
    }
}
复制代码

狗头

写了好久,先去吃饭了,其实还有很多功能模块,例如日志服务、多进程服务、进程间通信服务、安全相关服务、cookiesession服务等等,有兴趣的可以留言继续解答,或者出第二篇,下次出点技术类的~~~*

仓库地址: [Github]

当然,框架核心代码在 framework 这个包,目前还在开发和测试中,优化一些功能和文档,工具比较 low 逼,也需要完善,希望有大牛可以一起开发。

目前已经到 0.8.x 版本,已经历时半年(第一个 npm 包提交开始),离第一个稳定版不远了!!!!正在快车道中,希望明年尽快开放 1.0 版本服务大众。

也希望大家可以尝试下提供各种意见反馈,由于目前就一个人开发,bug肯定不少,cli工具方面已经很久没更新了,准备下一步完善工具,如果需要体验的(工具万一有bug),直接clone daze 仓库就可以。

然后,希望大家不要吝啬自己的 star ******** 给个鼓励~~~~

最后,祝大家新年快乐,新年新气象 *******


  • Node.js

    Node.js 是一个基于 Chrome V8 引擎的 JavaScript 运行时。 Node.js 使用高效、轻量级的事件驱动、非阻塞 I/O 模型。Node.js 的生态系统是目前最大的开源包管理系统。 …

    58 引用
感谢    赞同    分享    收藏    关注    反对    举报    ...