使用Node.js实现一个express框架
小职 2020-09-16 来源 : 阅读 724 评论 0

摘要:web前端学习,如何使用Node.js来实现一个express框架,希望你能通过这些文章真的学到框架的原理,进而自己能写出一些框架,走向更高的层级。

web前端学习,如何使用Node.js来实现一个express框架,希望你能通过这些文章真的学到框架的原理,进而自己能写出一些框架,走向更高的层级。

使用Node.js实现一个express框架

手写一个express系列

 

express的基本用法

 

const express = require("express");  

const app = express();  

app.get("/test", (req, res, next) => {  

  console.log("*1");  

//   res.end("2");  

  next();  

});  

app.get("/test", (req, res, next) => {  

  console.log("*2");  

  res.end("2");  

});  

app.listen(8888, (err) => {  

  !err && console.log("监听成功");  

});

 当我访问localhost:8888/test时候,返回了:2,服务端打印了

*1  

*2

 从上面可以看到什么?

 express默认引入调用后返回一个app对象

 app.listen 会启动进程监听端口

 每次收到请求,对应的url和method会触发相应挂载在app上对应的回调函数

 调用 next 方法,会触发下一个

一起来实现一个简单的express框架

 

 定义属于我们的express文件入口,这里使用class来实现

class express {  

}  

module.exports = express;

 需要的原生模块http,创建进程监听端口

const { createServer } = require("http");

 给 class 定义 listen 方法,监听端口

class express {  

  listen(...args) {  

    createServer(cb).listen(...args);  

  }  

}

 这样就可以通过调用 class 的 listen 去调用 http 模块的 listen 了,这里的cb我们可以先不管,你要知道每次接受到请求,必然会调用 cb 函数,这个是 createServer 原生模块帮我们封装好的

实现接收到请求触发

 

 实现app.get app.post等方法

 目前我们接受到响应,就会触发 cb 这个回调函数,那我们打印下,看看是什么参数?

class express {  

  cb() {  

    return (req, res) => {  

      console.log(res, res, "开始行动");  

    };  

  }  

  listen(...args) {  

    createServer(this.cb()).listen(...args);  

  }  

}

 发现此时的 req 和 res 正是我们想要的可读流和可写流.

 开始编写 get 和 post 方法

 这里注意,有路由是'/'的,这种是不管任何路由都会触发一次

constructor() {  

    this.routers = {  

      get: [],  

      post: [],  

    };  

  }  

  get(path, handle) {  

    this.routers.get.push({  

      path,  

      handle,  

    });  

  }  

  post(path, handle) {  

    this.routers.post.push({  

      path,  

      handle,  

    });  

  }

 初始化时候定义 get、post 的数组储存对应的 path 和 handle.

 需要触发路由回调的时候,首先要找到对应的请求方式下对应的 url 的 handle 方法,然后触发回调.

 如何找到对应请求方式下的 url 对应的 handle 方法? 在接到请求时候就要遍历一次

 这里要考虑匹配多个路由,意味着,我们可能遇到像最开始一样,有两个 get 方式的 test 路由

cb() {  

  return (req, res) => {  

    const method = req.method.toLowerCase();  

    console.log(this.routers[method], ",method");  

    const url = req.url;  

    this.routers[method].forEach((item) => {  

      item.path === url && item.handle(req, res);  

    });  

  };  

}  

listen(...args) {  

  createServer(this.cb()).listen(...args);  

}

 上面根据 method 找到对应的数组,遍历找到请求的路由,触发回调,此时已经能正常返回数据了

[ { method: 'get', path: '/test', handle: [Function] } ] ,method

 此时最简单的express已经完成了,但是我们好像忘了最重要的中间件

完成最重要的中间件功能

 

 首先要知道,express中间件分两种,一种带路由的,那就是根据路由决定是否触发

 另外一种就是不带路由的,像静态资源这种. 是用户访问任何路由都要触发一次的

 那我们需要一个 all 数组储存这种任意路由都需要匹配触发的

constructor() {  

   this.routers = {  

     get: [],  

     post: [],  

     all: [],  

   };  

 }

 之前的直接通过 push 方式是太粗暴.如果用户需要中间件功能,不传路由,那就要做特殊处理,这里通过一个中间函数处理下

 改造get、post方法,定义handleAddRouter方法

handleAddRouter(path, handle) {  

   let router = {};

    if (typeof path === "string") {  

     router = {  

       path,  

       handle,  

     };  

   } else {  

     router = {  

       path: "/",  

       handle: path,

     };  

   }  

   return router;  

 }  

 get(path, handle) {  

   const router = this.handleAddRouter(path, handle);  

   this.routers.get.push(router);  

 }  

 post(path, handle) {  

   const router = this.handleAddRouter(path, handle);  

   this.routers.post.push(router);

  }  

 use(path, handle) {  

   const router = this.handleAddRouter(path, handle);  

   this.routers.all.push(router);  

 }

 每次添加之前,先触发一次handleAddRouter,如果是 path 为空的中间件,直接传入函数的,那么 path 帮它设置成'/'

 我们还遗留了一个点,next的实现,因为我们现在加了all这个数组后,意味着可能有多个中间件,那么可能一次请求打过来,就要触发多个路由

这里要注意,promise.then 源码实现和 express 的 next、以及 koa 的洋葱圈、redux 的中间件实现,有着一丁点相似,当你能真的领悟前后端框架源码时候,发现大都相似

 

 阅读我的文章,足以击破所有前后端源码.而且可以手写出来, 我们只学最核心的,抓重点学习,野蛮生长!

实现next

 

 思路:

 首先要找到所有匹配的路由

 然后逐个执行(看 next 的调用)

 定义search方法,找到所有匹配的路由

search(method, url) {  

    const matchedList = [];  

    [...this.routers[method], ...this.routers.all].forEach((item) => {  

      item.path === url && matchedList.push(item.handle);  

    });  

    return matchedList;  

  }  

  cb() {  

    return (req, res) => {  

      const method = req.method.toLowerCase();  

      const url = req.url;  

      const matchedList = this.search(method, url);  

    };  

  }

 matchedList就是我们想要找到的所有路由

 为了完成next,我们要将req ,res , matchedList存入闭包中,定义handle方法

handle(req, res, matchedList) {  

   const next = () => {  

     const midlleware = matchedList.shift();  

     if (midlleware) {  

       midlleware(req, res, next);  

     }  

   };  

   next();  

 }  

 cb() {  

   return (req, res) => {  

     const method = req.method.toLowerCase();  

     const url = req.url;  

     const matchedList = this.search(method, url);  

     this.handle(req, res, matchedList);  

   };  

 }

 这样我们就完成了next方法,只要手动调用 next 就会调用下一个匹配到的路由回调函数

 不到一百行代码,就完成了这个简单的express框架



公众号(Zhizuobiao_Online),找我免费领取最新干货资源哦


本文由 @小职 发布于职坐标。未经许可,禁止转载。
喜欢 | 0 不喜欢 | 0
看完这篇文章有何感觉?已经有0人表态,0%的人喜欢 快给朋友分享吧~
评论(0)
后参与评论

您输入的评论内容中包含违禁敏感词

我知道了

助您圆梦职场 匹配合适岗位
验证码手机号,获得海同独家IT培训资料
选择就业方向:
人工智能物联网
大数据开发/分析
人工智能Python
Java全栈开发
WEB前端+H5

请输入正确的手机号码

请输入正确的验证码

获取验证码

您今天的短信下发次数太多了,明天再试试吧!

提交

我们会在第一时间安排职业规划师联系您!

您也可以联系我们的职业规划师咨询:

小职老师的微信号:z_zhizuobiao
小职老师的微信号:z_zhizuobiao

版权所有 职坐标-一站式IT培训就业服务领导者 沪ICP备13042190号-4
上海海同信息科技有限公司 Copyright ©2015 www.zhizuobiao.com,All Rights Reserved.
 沪公网安备 31011502005948号    

©2015 www.zhizuobiao.com All Rights Reserved

208小时内训课程