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应用
创立serviceservice/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)