vlambda博客
学习文章列表

实现超简版koa框架和egg框架

学习node框架,接触了koa和egg,通过平时使用,我了解了她们的特性,就想实现一下简单核心的功能。希望能够更加熟练掌握它们。

阅读需要10分钟

step1:简单手写超简版的koa小框架(Pok)✨

实现的简单功能🔋

  • 搭建http服务

  • 简易路由的搭建

  • 中间件的合成

  • request和response的封装

  • 上下文context.js

文件目录内容📌

  • app.js

  • pok.js

  • context.js

  • response.js

  • router.js

测试文件(app.js)📜

 
const Pok = require('./pok')const app = new Pok()// ==============Router start================const Router = require('./router')const router = new Router()
router.get('/index', async ctx => { ctx.body = 'index page'; });router.get('/post', async ctx => { ctx.body = 'post page'; });router.get('/list', async ctx => { ctx.body = 'list page'; });router.post('/index', async ctx => { ctx.body = 'post page'; });app.use(router.routes());// ==============Router end================app.listen(3001,()=>{ console.log('服务已启动');})

封装一个类似koa的框架pok📜

 
//引入基本库const http = require('http')const context = require('./context')const request = require('./request')const response = require('./response')//Pokclass Pok{ constructor(){ this.middlewares = [] } listen(...args){ const server = http.createServer(async (req,res)=>{ //创建content上下文 const ctx = this.createContext(req,res); //合并中间件 const fn = this.compose(this.middlewares) await fn(ctx) res.end(ctx.body) }) server.listen(...args) } use(middleware){ this.middlewares.push(middleware) } //创建content上下文 createContext(req,res){ const ctx = Object.create(context) ctx.request = Object.create(request) ctx.response = Object.create(response) ctx.request.req = req ctx.response.res = res return ctx } // 中间件 compose(middlewares){ return function(ctx){ return ds(0) function ds(i){ let fn = middlewares[i] if(!fn){ return Promise.resolve() }else{ return Promise.resolve( fn(ctx,function next(){ return ds(i+1) }) ) } } } }}module.exports = Pok

路由的封装(router.js)📜

 
class Router { constructor() { this.stack = [] } resigter(method, path, middleware) { this.stack.push({method, path, middleware}) } get(path, middleware) { this.resigter('get', path, middleware) } post(path, middleware) { this.resigter('post', path, middleware) } routes() { let stock = this.stack; return async function(ctx, next) { let currentPath = ctx.url; console.log(currentPath); let route;
for (let i = 0; i < stock.length; i++) { let item = stock[i]; console.log(item); if (currentPath === item.path && item.method.indexOf(ctx.method) >= 0) { // 判断path和method route = item.middleware; break; } }
if (typeof route === 'function') { route(ctx, next); return; }
await next(); }; }}module.exports = Router

request和response的封装 (request.js)📜

module.exports = {get url(){return this.req.url },get method(){return this.req.method.toLowerCase() }};response.jsmodule.exports = {get body(){return this._body },set body(val){this._body = val }};

上下文的封装(context.js)📜

 
module.exports = { get url() { return this.request.url }, get body() { return this.response.body }, set body(val){ this.response.body = val }, get method() { return this.request.method }}

step2:简单手写Egg框架实现mvc分层(基于koa)

实现的简单功能🔋

  • 对koa进行封装,实现mvc分层

主要内容📌

  • initRouter

  • initController

  • initService

  • LoadConfig

  • 更新中。。。

测试文件(app.js)📜

 
const pgg = require('./pgg')const app = new pgg()app.start(3000)#封装一个类似Egg的框架pggconst koa = require('koa');const {initRouter, initController, initService,LoadConfig} = require('./pgg-loader')class Pgg{ constructor(conf){ this.$app = new koa(conf) LoadConfig(this) this.$service = initService(this) this.$ctrl = initController(this) this.$router = initRouter(this) //挂载路由 console.dir(this.$ctrl); // 调用routes函数 this.$app.use(this.$router.routes()) } start(port){ this.$app.listen(port,()=>{ console.log(`服务器${port}启动成功`); }) }}module.exports = Pgg

读取不同文件(pgg-loader.js)📜

 
const fs = require('fs')const path = require('path')const Router = require('koa-router')const Sequelize = require('sequelize')// 读取文件,将方法 路径 和函数转换成对象function load(dir,cb) { //转换成绝对路径 const url = path.resolve(__dirname,dir) //读指定目录下所有文件名称”的数组对象。 const files = fs.readdirSync(url) // 遍历文件,读取内容 files.forEach(filename=>{ filename = filename.replace('.js','') const file= require(url+'/'+filename) console.log(filename,'文件名'); // 文件名称 cb(filename,file) })}//路由页面的初始化function initRouter(app) { const router = new Router() // 初始化route文件路径下的路由文件 load("routes",(filename,routes)=>{ // index 前缀处理 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}\n,${routes[key]}`); router[method](prefix + path,async ctx=>{ app.ctx = ctx await routes[key](ctx) }) }) }) return router}//控制层的初始化function initController(app) { const controllers = {} load('controller',(filename,controller)=>{ controllers[filename] = controller(app) }) return controllers}//服务层的初始化function initService() { const services = {} load('service',(filename,service)=>{ services[filename] = service }) return services}//配置持久化function LoadConfig(app) {  load('config',(filename,config)=>{ if(config.db){ app.$db = new Sequelize(config.db) } app.$model = {} load('model',(filename,{schema, options})=>{ app.$model[filename] = app.$db.define(filename, schema, options) }); app.$db.sync(); })}//持久层的初始化module.exports = {initRouter,initController,initService,LoadConfig}
欢迎关注作者,感谢支持❗☘