嘿,我又造了个轮子

in Node.js with 1 comment

前言

其实研究Node也有一段时间了,但是由于受到PHP后端框架MVC模式根深蒂固的影响,刚开始用一些诸如expressKoa框架时,显得有些许不太适应,没有明显的分层,也没有ORM,当然,这也是这些框架的轻巧和简约之处。
我曾经在express的基础上适当的把一些MVC层给勾勒出来了,但是写的太过于潦草,所以这次我以一个更简约的框架--Koa作为基础进行了封装,完成了一些基本的MVCORM.
GayHub.

功能

MVC分层

Koa以精简出众,完成了基本的RequestResponseMiddleware等功能。所以正如我在上文所说,我在项目的app目录下增加了一个MVC的分层,我封装了一个routeServe方法,来自动的调用routes目录下的文件,并根据其文件名来设置对应的路由前缀,所以,如果在web.js中这么写:

const routers = router
    .get('/', 'Index@index');

那么就会自动的调用controllers/Index.jsindex方法,对应的路由就是/

如果在api.js中这么写:

const routers = router
    .get('/data', 'Index@index');

那么就会自动的调用controllers/Index.jsindex方法,对应的路由就是/api/data

当然,在这里我也对原生的Middleware进行了封装,如果我们在路由中使用:

const routers = router
    .get('/', 'Init+Index@index');

时,那么就会自动的先执行middlewares/Init.jshandle方法,再去执行controllers/Index.jsindex方法。

/**
     * 遍历路由文件 web用'/' 其他用对应文件名
     * @returns {*}
     */
    routeServe() {
        helper.walkDir(pathObj.join(base.rootPath , '/routes')).map((item) => {
            let root = item.split('/').pop().split('.').shift();
            route.use(root === 'web' ? '/' : '/' + root, require(item).routes());
        });
        return route;
    },

    /**
     * 给路由包裹上方法
     * @param router
     * @returns {*}
     */
    wrapRoute(router) {
        let that = this;
        router.methods.map((method) => {
            router[method.toLowerCase()] = function (name, path, middleware) {
                let args = Object.values(arguments).map((value) => {
                    return that.parseMiddleware(value);
                });

                if (typeof path === 'string' || path instanceof RegExp) {
                    if (path.indexOf('@') > -1) {
                        middleware = args.pop();
                        path = name;
                        name = null;
                    } else {
                        middleware = args.splice(2);
                    }
                } else {
                    middleware = args.splice(1);
                    path = name;
                    name = null;
                }

                this.register(path, [method], middleware, {
                    name: name
                });

                return this;
            };
        });
        return router;
    },

    /**
     *
     * @param middleware
     * @returns {*}
     */
    parseMiddleware(middleware) {
        if (typeof middleware === 'string' && middleware.indexOf('@') > -1) {
            let parts = middleware.split('@'), root = pathObj.join(base.rootPath, 'app'),
                controllerPath = pathObj.join(root, 'controllers'), controller = parts.shift(), method = parts.pop(),
                middlewarePath = null;
            if (middleware.indexOf('#') > -1) {
                let middlewareParts = middleware.split('#');
                controller = middlewareParts.pop().split('@').shift();
                // 中间件
                middlewarePath = require(pathObj.join(root, 'middlewares', middlewareParts.shift()));
                middleware = [];
            }

            let currController = helper.withObj(pathObj.join(controllerPath, controller));

            //用于判断是否有middleware
            Array.isArray(middleware) ? middleware = [new middlewarePath().handle.bind(currController), currController[method].bind(currController)] : middleware = currController[method].bind(currController);

        }
        return middleware;
    }

以上代码是对路由封装的主要代码,在成功载入路由文件之后对路由结构进行解析时,会判断是否存在middleware,然后把middlewarecontroller同时置于一个数组中予以返回,然后重写了koa-routegetpost等方法,再去register这个数组。

ORM

虽然在Node中用MySql的情况不太多,但是如果要写的时候,以官方库的形式去写就会不是很优雅,所以,我在这里借鉴了我们团队ORM的思想对官方库进行了封装,完成了一个初版的ORM

目录:

.
├── Connector.js -> 连接器
├── Sql -> sql语句层
│   ├── Add.js
│   ├── Base.js
│   ├── Delete.js
│   ├── Select.js
│   └── Update.js
└── Table.js -> table控制

其中,Connector.js主要用于链接数据库,进行sql操作,并针对CRUD来进行分发;Sql层主要是拼接不同的sql语句,其中Base.js是其基类,用于解析sql之类的操作;Table.js主要用于一些常用方法的封装。
至于具体的执行方法,可以在我在GitHub上面写的md查看。

form

这里写的比较自动化,我在middlewares中写了一个Data.js的中间件,当我们使用这个中间件时,会自动对我们输入的参数进行验证.
比方我们请求了一个GET:/api/user/search/nine,我们写的路由是/user/search/:first_name,那么需要我们在forms目录下新建一个User/Search.js的文件,里面需要有checkFirstName这个方法,在这个里面可以调用我们已经写好的validate方法,如下所示:

    checkFirstName(name) {
        return this.validate(name, 1 === 2, 'url格式有误')
            .validate(name, 'email', 'email格式有误');
    }

当然,我们在这里验证的时候,也可以使用自己的验证,如第一个验证方法所示。

env

为了区分不同的开发环境,我借鉴Laravel的方式,封装了env的一些配置,载入配置可以让我们同时在开发和生产环境执行。你也可以通过调用lib/Env.jsgetEnvsetEnv去设置一些全局参数。

后记

这个框架写的有很多需要完善和不太合理的地方,我也没有指望这个框架会有多少人去使用。为什么世上有这么多的轮子了还是有人要去新建轮子呢?我想,能体验自己的成长也是一种乐趣所在吧。我想,以后我也会继续去写一些我自己想要写的东西。

Responses
  1. awd

    Reply