Egg.js 介绍

基于Koa的企业级三层构造框架

Egg.js 的构造

三层构造

  • 信息资源层
裸露给里面的接口,后端对其余服务的裸露,蕴含 视图、接口;
  • 业务逻辑层
反复的业务逻辑解决放在外面,实现外围的业务管制也是在这一层实现。
  • 数据拜访层
重点负责数据库拜访,实现长久化性能

我的项目构造

Egg-Dome├──app ├────controller #路由对应的加载文件├────public #寄存动态资源├────service #反复的业务逻辑解决├────router.js #门路匹配├──config #配置文件

根本应用

app/controller/product.js
const {Controller} = require('egg')class ProductController extends Controller { async index(){   const {ctx} = this   const res = await ctx.service.product.getAll()   ctx.body=res }
app/service/product.js
const {Service} = require('egg');class ProductService extends Service{  async getAll(){    return {      id: 1,      name: '测试数据'    }  }}module.exports = ProductService

app/router.js

'use strict';/** * @param {Egg.Application} app - egg application */module.exports = app => {  const { router, controller } = app;  router.get('/product',controller.product.index)};

创立模型层

egg.js 加载任何性能都是基于插件
以mysql + sequelize为例例演示数据长久化
装置:

npm install --save egg->sequelize mysql2

环境配置

config/plugin.js
'use strict';module.exports={  sequelize= {    enable: true,    package: 'egg-sequelize',  }}
config/config.default.js
  // add your user config here  const userConfig = {    sequelize: {      dialect: "mysql",      host: "127.0.0.1",      port: 3306,      username: "root",      password: "example",      database: "ohh"    }  };

实现分层框架

创立一个ohh-egg-dome的我的项目
先 npm init一下,创立下我的项目
目录构造:

ohh-egg-dome├── routers├──── index.js├──── user.js├── index.js

主动加载路由性能

首先创立两个门路的文件

routes/index.js
module.exports = {  'get /': async ctx =>{    ctx.body = '首页'  },  'get /detail': async ctx=>{    ctx.body = '具体页面'  }}
routes/user.js
module.exports={  // user/  'get /': async ctx=>{    ctx.body= '用户首页'  },  // user/info  'get /info': async ctx=>{    ctx.body = '用户详情页'  }}

配置路由页面
让下面写的两个路由,index和user主动加载
ohh-loader.js

const fs = require('fs')const path= require('path')const Router = require('koa-router')function load(dir, cb){  // 加工成绝对路径  const url = path.resolve(__dirname, dir)  // 列表  const files = fs.readdirSync(url)  // 遍历  files.forEach(filename=>{    // 将 user.js 中的js去掉    filename = filename.replace('.js','')    const file = require(url+'/'+filename)    cb(filename,file)  })}function initRouter(){  const router =new Router()  load('routes',(filename, routes)=>{    // 是index 门路间接是/ 如果是别的前缀,那么就须要拼接: /user    const prefix =filename ==='index'?'':`/${filename}`    Object.keys(routes).forEach(key=>{      const [method,path] = key.split(' ')      console.log(`正在映射地址: ${method.toLocaleUpperCase()} ${prefix}${path}`)      router[method](prefix + path, routes[key])    })  })  return router}module.exports = {initRouter}

index.js

const app = new(require('koa'))()const {initRouter} = require('./ohh-loader')app.use(initRouter().routes())app.listen(7001)

运行

nodemon index.js

页面展现

如何封装

封装

const {initRouter} = require('./ohh-loader')app.use(initRouter().routes())

这属于架构的一部分,不单是加载路由而是加载多层操作,所以把实现细节须要封装起来。

ohh.js

const Koa = require('koa')const {initRouter} = require('./ohh-loader')class ohh{  constructor(conf){    this.$app = new Koa(conf)    this.$router =initRouter()    this.$app.use(this.$router.routes())  }  start(port){    this.$app.listen(port, ()=>{      console.log('服务启动 at '+ port);    })  }}module.exports = ohh
index.js
// const app = new(require('koa'))()// const {initRouter} = require('./ohh-loader')// app.use(initRouter().routes())// app.listen(7001)const ohh =require('./ohh')const app =new ohh()app.start(7001)

controller性能实现

增加文件
ohh-egg-dome
├── controller
├──── home.js
controller/home.js
module.exports = {  index: async ctx=>{    ctx.body = "柯里化-首页"  },  detail:ctx=>{    ctx.body="柯里化-详请页面"  }}

routes/index.js

module.exports= app => ({ 'get /': app.$ctrl.home.index,  'get /detail': app.$ctrl.home.detail})// 将对象转化成=> 对象工厂
ohh-loader.js
const fs = require('fs')const path= require('path')const Router = require('koa-router')function load(dir, cb){  // 加工成绝对路径  const url = path.resolve(__dirname, dir)  // 列表  const files = fs.readdirSync(url)  // 遍历  files.forEach(filename=>{    // 将 user.js 中的js去掉    filename = filename.replace('.js','')    const file = require(url+'/'+filename)    cb(filename,file)  })}function initRouter(app){  const router =new Router()  load('routes',(filename, routes)=>{    // 是index 门路间接是/ 如果是别的前缀,那么就须要拼接: /user    const prefix =filename ==='index'?'':`/${filename}`    // 判断传进来的是柯里化函数     routes = typeof routes === 'function' ? routes(app): routes    Object.keys(routes).forEach(key=>{      const [method,path] = key.split(' ')      console.log(`正在映射地址: ${method.toLocaleUpperCase()} ${prefix}${path}`)      router[method](prefix + path, routes[key])    })  })  return router}// controller 加载进来function initController(){  const controllers={}  load('controller', (filename, controller)=>{    controllers[filename] = controller  })  return controllers}module.exports = {initRouter, initController}
ohh.js
const Koa = require('koa')const {initRouter, initController} = require('./ohh-loader')class ohh{  constructor(conf){    this.$app = new Koa(conf)    this.$ctrl =initController() //新退出进来的controller    this.$router =initRouter(this) // 把this传进来,在    this.$app.use(this.$router.routes())  }  start(port){    this.$app.listen(port, ()=>{      console.log('服务启动 at '+ port);    })  }}module.exports = ohh

service应用

创立service
service/user.js
//  模仿异步const delay = (data, tick)=> new Promise (resolve => {  setTimeout(()=>{    resolve(data)  },tick)})module.exports = {  getName(){    return delay('ohh', 1000)  },  getAge(){      return 18  }}
routes/user.js
module.exports={  // user/  // 'get /': async ctx=>{  //   ctx.body= '用户首页'  // },  'get /':async app=>{    const name= await app.$service.user.getName()    app.ctx.body= '用户:'+ name  },  // user/info  // 'get /info': async ctx=>{  //   ctx.body = '用户详情页'  // }  'get /info': async app=>{    app.ctx.body="年龄:"+  app.$service.user.getAge()  }}
controller/home.js
module.exports = app=>({  // index: async ctx=>{  //   ctx.body = "柯里化-首页"  // },  // 这里须要用上app, 所以须要整个对象进行升阶  index: async ctx=>{    const name= await app.$service.user.getName()    app.ctx.body = 'ctrl usr '+ name  },  detail:ctx=>{    ctx.body="柯里化-详请页面"  }})
service 文件加载
ohh-loader.js
const fs = require('fs')const path= require('path')const Router = require('koa-router')function load(dir, cb){  // 加工成绝对路径  const url = path.resolve(__dirname, dir)  // 列表  const files = fs.readdirSync(url)  // 遍历  files.forEach(filename=>{    // 将 user.js 中的js去掉    filename = filename.replace('.js','')    const file = require(url+'/'+filename)    cb(filename,file)  })}function initRouter(app){  const router =new Router()  load('routes',(filename, routes)=>{    // 是index 门路间接是/ 如果是别的前缀,那么就须要拼接: /user    const prefix =filename ==='index'?'':`/${filename}`    // 判断传进来的是柯里化函数    routes = typeof routes === 'function' ? routes(app): routes    Object.keys(routes).forEach(key=>{      const [method,path] = key.split(' ')      console.log(`正在映射地址: ${method.toLocaleUpperCase()} ${prefix}${path}`)      // 这里进行了; 一次调整。router 中的文件须要用ctx 。所以包装了一层      router[method](prefix + path, async ctx=>{      // app.ctx, 须要从新复制,因为app中的ctx是ohh.loader的;      // 因为在router中,咱们应用的是 `app.ctx.body= '用户:'+ name`,这个ctx就是Koa的        app.ctx =ctx        await routes[key](app)      })    })  })  // console.log('router', router);  return router}// controller 加载进来function initController(app){  const controllers={}  load('controller', (filename, controller)=>{    // console.log('controller-filename', filename, controller);    controllers[filename] = controller(app)  })  console.log(controllers,'controllers')  return controllers}// 加载service文件function initService(){  const services={}  // filename 在service 中的文件名称;   // service  在文件中默认导出对象的内容  load('service',(filename,service)=>{    services[filename]= service  })  return services}module.exports = {initRouter, initController, initService}
ohh.js
const Koa = require('koa')const {initRouter, initController, initService} = require('./ohh-loader')class ohh{  constructor(conf){    this.$app = new Koa(conf)    this.$service = initService()     this.$ctrl =initController(this)    this.$router =initRouter(this) // 把this传进来,在    this.$app.use(this.$router.routes())  }  start(port){    this.$app.listen(port, ()=>{      console.log('服务启动 at '+ port);    })  }}module.exports = ohh
在controller中应用service,须要将controller进行升阶,柯理化
router中应用service,改为函数,须要外部用的app

加载数据层

首先装置sequelize、mysql2

npm install sequelize mysql2 --save
config/index.js
// 数据库配置module.exports = {  db:{    dialect:'mysql',    host:'localhost',    database:'ohh',    username:'root',    password:'example'  } }
ohh-loader.js
// 次要性能是主动加载config函数,判断外面是有数据库相应的配置,主动初始化数据库const Sequelize = require('sequelize')function loadConfig(app){  load('config', (filename,conf)=>{     if(conf.db){       console.log('加载数据库');        app.$db = new Sequelize(conf.db)     }  })}module.exports = {initRouter, initController, initService, loadConfig}
const Koa = require('koa')const {initRouter, initController, initService, loadConfig} = require('./ohh-loader')class ohh{  constructor(conf){    loadConfig(this)  }  start(port){    this.$app.listen(port, ()=>{      console.log('服务启动 at '+ port);    })  }}module.exports = ohh

加载数据模型

model/user.js
const {STRING} = require("sequelize");// 新建数据库模型module.exprots={  schema:{    name: STRING(30)  },  options:{    timestamps: false  }}
ohh-loader.js
// 次要性能是主动加载config函数,判断外面是有数据库相应的配置,主动初始化数据库const Sequelize = require('sequelize')function loadConfig(app){  load('config', (filename,conf)=>{     if(conf.db){       console.log('加载数据库');        app.$db = new Sequelize(conf.db)        // 加载模型        app.$model = {}         load('model',(filename, {schema, options})=>{          // 创立模型          app.$model[filename] = app.$db.define(filename,schema,options)        })        app.$db.sync()     }  })}
ohh.js
class ohh{  this.$service = initService(this)   }
ohh-loader.js
//service  应用model// 加载service文件function initService(app){  const services={}  // filename 在service 中的文件名称;   // service  在文件中默认导出对象的内容  load('service',(filename,service)=>{    services[filename]= service(app)  })  return services}
service/user.js
module.exports = app=>({  getName(){    // return delay('ohh', 1000)    return app.$model.user.findAll()  },  getAge(){      return 18  }})
controller/home.js
  index: async ctx=>{    const name= await app.$service.user.getName()    app.ctx.body = name  },

主动加载中间件

middleware/logger.js
module.exports= async (ctx,next)=>{  console.log(ctx.method+" "+ctx.path);  const start =new Date()  await next()  const duration = new Date() - start;  console.log(ctx.method+" "+ctx.path+" "+ctx.status+" "+duration+"ms");}
config/index.js
// 数据库配置module.exports = {  db:{    dialect:'mysql',    host:'localhost',    database:'ohh',    username:'root',    password:'example'  },  // 中间件配置,定义为数组  middleware:[    // 中间件的名字    'logger'  ]}
ohh-loader.js
function loadConfig(app){  load('config', (filename,conf)=>{     // 如果有中间件配置     if(conf.middlerware){      // 不须要load,不必全副加载。所以顺次加载      // 首先解决绝对路径      // 三段体, /xxx+ '/middleware/'+ 'logger'      const midPath =path.resolve(_dirname,'middleware', mid)      app.$app.use(require(midPath))     }    })### 定时工作使⽤Node-schedule来管理定时工作

npm install node-schedule --save

> `schedule/log.js`

module.exports= {
// 工夫距离 crontab 工夫距离的字符串
// */ 示意先执行一次,3秒后,在执行。
interval:'/3 ', // 3秒执行一次
handler(){

console.log('定时工作,每三秒执行一次'+ new Date());

}
}

> `schedule/user.js`

module.exports={
// 30秒之后执行
interval:"30 *",
handler(){

console.log('定时工作 每分钟30秒执行一次'+ new Date())

}
}

> `ohh-loader.js`

const schedule = require('node-schedule')
function initschecule(){
load('schedule',(filename, scheduleConfig)=>{

schedule.scheduleJob(scheduleConfig.interval,  scheduleConfig.handler)

})
}

> `ohh.js`

class ohh {

constructor(conf){    initschecule()}

}

> linux的crobtab 的介绍阐明> [https://www.runoob.com/w3cnote/linux-crontab-tasks.html](https://www.runoob.com/w3cnote/linux-crontab-tasks.html)