乐趣区

关于node.js:eggjs-原理解析

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)
退出移动版