egg 框架用koa实现一遍

193 阅读2分钟

1.约定大于配置(convention over configuration)

  1. 预设默认的配置
  2. 根据自定的约定生成配置
  3. 只有特殊的需要配置
  4. 目的就是为了简化配置,只做少量的约定生成代码逻辑

2.egg的mvc架构

  1. controller:与view,model,和serive交互的控制层,可以做数据库db操作
  2. service:通用业务逻辑层,短信/邮箱,图片上传等方法封装,或者访问数据库
  3. model:数据库持久化操作
  4. view:视图层,一般egg只提供服务多,不做html输出
  • router:通过路由统一分发不同的controller控制层。

3. 结构目录

image.png

3.自己实现架构图

image.png

  1. jgg统一管理程序入口总线,包括koa实例化和启动,jgg-loader 加载出来的所有模块功能
  2. jgg-loader实现通过文件夹遍历,加载文件夹下所有类,键值与方法映射
  3. 通过jgg实例$app传入 所有模块,实现数据共享

4.具体代码实现

//关键库 koa  koa-router node-schedule sequelize
//index.js
const jgg = require("./jgg")
const app = new jgg()
app.start(3000)

//jgg.js
const koa =  require('koa')
const {initRoute,initController,initService,initConfig,initSchedule} = require('./jgg-loader')
class jgg {
    constructor(conf){
        this.$app = new koa(conf)
        initConfig(this)
        initSchedule(this)
        this.$service = initService(this)
        this.$ctrl = initController(this)
        this.$app.use(initRoute(this).routes())
    }
    start(port){
        this.$app.listen(port, () => {
            console.log("服务器启动了,端口",port)
        })
    }
}
module.exports = jgg

//jgg-loader.js
const fs = require('fs')
const path = require('path')
const Router = require('koa-router')

function load(dir,cb) {
    console.log("dir",dir)
    const url = path.resolve(__dirname,dir)
    const files = fs.readdirSync(url)
    files.forEach( filename => {
        // 去掉后缀
        filename = filename.replace('.js', '')
        // 导入文件
        const file = require(url + '/' + filename)
        cb(filename, file)
    })
}
function initRoute(app) {
    const router = new Router()
    load("routes", (filename,fileObj) => {
        console.log(filename,fileObj)
        if(typeof fileObj === 'function') {
            fileObj = fileObj(app)
        }
        let prefix = filename === 'index' ? '': "/" + filename
        Object.keys(fileObj).forEach( key => {
            let [method,path] = key.split(" ") 
            console.log(`正在映射地址 ${method.toLocaleUpperCase()} ${prefix}${path}`)
            console.log("fileObj ",fileObj)
            let func = fileObj[key] 
            router[method](prefix + path,async ctx => {
                app.ctx = ctx
                await func(app)
            }) 
            
        })
    })
    return router;
}
function initController(app) {
    let controllers = []
    load("controllers", (filename,fileObj) => {
            controllers[filename] = fileObj(app)
    })
    return controllers;
}
function initService(app) {
    let services = []
    load("service", (filename,fileObj) => { 
            services[filename] = fileObj(app)
    })
    return services;
}

//定时器
const schedule = require("node-schedule")
function initSchedule(app) {
    load("schedule", (filename,fileObj) => {
            schedule.scheduleJob(fileObj.interval, fileObj.handler);
    })
}
const Sequelize = require('sequelize')
function initConfig(app) {
    load('conf', (filename, conf) => {
        if (conf.db) {
            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()
        }

        if (conf.middleware) {
            conf.middleware.forEach(mid => {
                const midPath = path.resolve(__dirname, 'middleware', mid)
                app.$app.use(require(midPath))
            })
        }
    })
}
module.exports = {
    initRoute,initController,initService,initConfig,initSchedule
}
//routes/index.js
module.exports = (app) => ({
    "get /" :  app.$ctrl.home.index,
    "get /detail" : app.$ctrl.home.detail,
})
//routes/user.js
module.exports = (app) => ({
    "get /aaa": async app => {
        const name = await app.$service.user.getName()
        app.ctx.body = "xxx111用户" + name;
      },
      
  // /user/info
  "get /info": app => {
    app.ctx.body = "xxx用户年龄" + app.$service.user.getAge();
  }
})

//controller/home.js
module.exports = (app) => ({
    index :  async app => {
        // const name = await app.$service.user.getName()
        // app.ctx.body = "con" + name;
        app.ctx.body = await app.$model.user.findAll()
      },

   
    detail : async app => {
        const name = await app.$service.user.getAge()
        app.ctx.body = "con" + name;
      },
})

//service/user.js
const delay = (data, tick) => new Promise(resolve => {
    setTimeout(() => {
        resolve(data)
    }, tick)
})

module.exports = (app) => ({
    getName() { 
       return   app.$model.user.findAll()
    },
    getAge() {
        return 20
    }
})

//model/user.js
const { STRING } = require("sequelize");
module.exports = {
  schema: {
    name: STRING(30)
  },
  options: {
    timestamps: false
  }
};
//conf/index.js 配置信息
module.exports = {
    db: {
        dialect: 'mysql',
        host: '81.71.xxx.xxx',
        database: 'xxxx',
        username: 'root',
        password: 'xxxx'
    },
    middleware:['logger']
}
//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"
    );
};
//schedule/log.js
module.exports = {
    interval:'*/3 * * * * *',
    handler(){
        console.log('定时任务 嘿嘿 三秒执行一次'+ new Date())
    }
}