Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Koa 之路由处理 #27

Open
zenglinan opened this issue Oct 1, 2019 · 0 comments
Open

Koa 之路由处理 #27

zenglinan opened this issue Oct 1, 2019 · 0 comments

Comments

@zenglinan
Copy link
Owner

zenglinan commented Oct 1, 2019

koa 中的路由

Koa 中可以使用 app.use 注册中间件, 中间件接收一个 ctx 参数, 这个参数挂载了一些请求信息, 同时返回给前端的信息也可以挂载在这个参数上

那么我们就可以这样处理路由:

app.use(async (ctx, next) => {
  if(ctx.path === 'user' && ctx.method.toUpperCase() === 'GET'){
    ctx.body = {
      'name': "john"
    }
  }
})

如果我们希望路由这块能抽离出来呢? 这样会比较好维护一些, 这时可以使用 koa-router, 因为 koa 默认没有集成, 所以需要单独安装

在 router.js 中编写路由中间件

const KoaRouter = require('koa-router')
const router = new KoaRouter()

router.get('/user', async (ctx, next) => {
  // 这里的路由处理函数实际上也是一个中间件
  // ... 路由处理
  await next()
})

router.get('/user', async (ctx, next) => {
  next()
})

router.get('/msg', async (ctx, next) => {
  next()
})

可以看到, koa-router 对路由的处理, 实际上也遵循洋葱圈模型, 这样的好处是: 可以将路由抽离成一个中间件

只需要引用这个路由中间件即可

import router from 'router.js'
app.use(router.routes())

接下来我们应该思考的是, 当项目越来越大的时候, 我们总不能把所有的路由处理都放在一个 router.js 里面吧, 对不同主题的路由进行进一步的拆分是很有必要的

比如一个博客系统, 就可以拆分成 user 部分, blog 部分, love 点赞部分等。
除此之外, 我们还要考虑新版本旧版本路由的兼容问题, 这时候又要将进行进一步划分。如果将所有路由都写在一个文件里面的话, 简直是灾难!

这时候我们的目录拆分大概是这样的, 用 v 表示版本号:

  • api
    • v1
      • user
      • blog
      • love
    • v2
      • user
      • blog
      • love

拆分好了, 新的问题又来了, 如果我们拆分了这么多块, 那岂不是要在 app.js 这个入口文件也引入这么多

像这样:

const User_v1 = require('api/v1/user')
const Blog_v1 = require('api/v1/blog')
const Love_v1 = require('api/v1/love')
const User_v2 = require('api/v2/user')
const Blog_v2 = require('api/v2/blog')
const Love_v2 = require('api/v2/love')

app.use(User_v1)
app.use(Blog_v1)
// ... 写不下去了

这时候我们可以借助一个第三方的包 require-directory , 他可以帮我们自动获取指定目录下所有文件导出的 module, 值得一提的是, 即使是嵌套的目录, 他也可以检索出来。

const KoaRouter = require('koa-router')
const requireDirectory = require('require-directory')

// 检索 /api 下的每一个文件导出的 module, 每导入一个 module, 都会执行 whenLoadModule 函数, 并且将导出的 module.exports 作为参数传给这个函数 
requireDirectory(module, '/api', {
  visit: whenLoadModule
})

function whenLoadModule(exports){  // 导出的 module.exports
  // 这里统一规定 modulex.exports 导出的形式为对象形式 { xxx, yyy }, 方便统一处理
  Object.keys(exports).forEach(k => {
    // 只要是路由对象就进行注册
    if(exports[k] instanceof KoaRouter){
      app.use(exports[k].routes())
    }
  })
}

现在我们注册路由已经很方便了, 但是还是觉得 app.js 的代码有点多, 对于这些初始化的代码, 我们可以用一个类抽象一下:

// core/init.js
const KoaRouter = require('koa-router')
const requireDirectory = require('require-directory')

class InitManager {
  static init(app){
    InitManager.app = app
    InitManager.initRouters()
  }

  static initRouters(){
    // 这里我们路径用拼接的绝对路径, 硬编码的话改动很麻烦
    const apiDirectory = `${process.cwd()}/api`
    requireDirectory(module, apiDirectory, {
      visit: whenLoadModule
    })

    function whenLoadModule(exports){
      Object.keys(exports).forEach(k => {
        if(exports[k] instanceof KoaRouter){
          InitManager.app.use(exports[k].routes())
        }
      })
    }
  }
}
module.exports = InitManager

这时候我们的 app.js 就比较清爽了

const Koa = require('koa')
const InitManager = require('core/init')

const app = new Koa()

InitManager.init(app)

到这里, 我们的路由处理的大体框架就基本完善了

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant