优雅的处理硬编码
====

在我们的项目中，有很多东西是需要我们使用硬编码去书写的，例如，启动ip地址，端口，数据链接端口，数据库名字，密码，跨域的一些http请求等等。

我曾经看过一些非常不规范的开发，把各种硬编码写入逻辑中，有时候，线上和线下的配置是完全不一样的，维护起来那叫一个要命。

按照之前我们的思路，我们可以将配置，写入一个config文件夹中，用名字的限制来区分我们线下，线上的配置

同样我们可以使用前面类似的方法进行对config文件夹中的东西进行扫描，然后自动加载到项目中去。

在考虑如何实现config自动挂载之前，我们得思考一下一个问题：挂去哪里？

- router ：很小几率会用上
- controller：业务控制流程，但是还有是有一定几率会用上.
- service: 业务逻辑上，用到的是最多的，也是最频繁的
- 我们的配置是不可在业务逻辑中修改的，因此我们的硬编码配置无需新建一个对象，只要在app启动时挂载一次即可

根据上述的思考，我们考虑到，koa的实例，是全局唯一的。

所以，我们决定将配置，统一挂到koa的实例上。


第一步
====
src下创建一个config文件夹，里面放我们的配置

- config.default.ts 用于存放公共配置
- config.dev.ts 用于存放开发时的配置
- config.pro.ts 用于存放线上配置

随便写一点配置
```ts
//config.default.ts 
module.exports = {
    middleware: ['parse', 'mid'],
}
```


给Loader类添加一个loadConfig的函数
```js
loadConfig() {
        const configDef = __dirname + '/config/config.default.js';
        const configEnv = __dirname + (process.env.NODE_ENV === 'production' ? '/config/config.pro.js' : '/config/config.dev.js');
        const conf = require(configEnv);
        const confDef = require(configDef);
        const merge = Object.assign({}, conf, confDef);
        Object.defineProperty(this.app, 'config', {
            get: () => {
                return merge
            }
        })
    }
```
代码很简单，就是将目录下的config都读出来，进行合并。然后绑定在this.app下

第二步
====
给Controller基类和Serivce基类传入app实例。

```ts
//base.ts
import { BaseContext } from "koa";
import * as Koa from "koa";

export class Controller {
    ctx: BaseContext;
    app:Koa
    constructor(ctx: BaseContext,app:Koa) {
        this.ctx = ctx;
        this.app = app;
    }
}

import * as Koa from "koa";

class Service {
    ctx: BaseContext;
    app: Koa
    constructor(ctx: BaseContext, app: Koa) {
        this.ctx = ctx;
        this.app = app;
    }
}


```

第三步 
===
构造Controller基类和Serivce基类时传入app实例。

```ts

loadRouter() {
        this.loadController();
        this.loadService();
        this.loadConfig();

        const mod = require(__dirname + '/router.js');
        const routers = mod(this.controller);
        console.log(routers);
        Object.keys(routers).forEach((key) => {
            const [method, path] = key.split(' ');

            (<any>this.router)[method](path, async (ctx: BaseContext) => {
                const _class = routers[key].type;
                const handler = routers[key].methodName;
                const instance = new _class(ctx, this.app);  //注意这里传入
                instance[handler]();
            })
        })
        return this.router.routes();
    }

loadService() {
        const service = fs.readdirSync(__dirname + '/service');
        var that = this;
        Object.defineProperty(this.app.context, 'service', {
            get() {
                if (!(<any>this)['cache']) {
                    (<any>this)['cache'] = {};
                }
                const loaded = (<any>this)['cache'];
                if (!loaded['service']) {
                    loaded['service'] = {};
                    service.forEach((d) => {
                        const name = d.split('.')[0];
                        const mod = require(__dirname + '/service/' + d);

                        loaded['service'][name] = new mod(this, that.app);  //注意这里传入
                    });
                    return loaded.service;
                }
                return loaded.service;
            }
        });

    }
```

最后
====
启动框架，我们可以愉快的在service或者controller中使用了。
使用的方法是this.app.config.....

```ts
import { Controller } from "./base";

//user.ts

export default class User extends Controller {
    async user() {
        this.ctx.body = this.ctx.service.check.index();

    }

    getConfig() {
        return (<any>this.app)['config']
    }

    async userInfo() {
        this.ctx.body = this.getConfig().middleware[0];//注意这里
    }
}
```
