网站首页 > 开源技术 正文
作者:Peter酱
转发链接:https://mp.weixin.qq.com/s/9r3_uLCx1IYdyr5BRzjQgA
express的基本用法
const express = require("express");
const app = express();
app.get("/test", (req, res, next) => {
console.log("会所技师到位*1");
// res.end("会所技师开始服务1");
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
- 从上面可以看到什么?
- app.listen 会启动进程监听端口
- 每次收到请求,对应的url和method会触发相应挂载在app上对应的回调函数
- 调用 next 方法,会触发下一个
- express默认引入调用后返回一个app对象
一起来实现一个简单的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框架
写在最后
- 只要你根据我这些文章去认真自己实现一次,扎实学习一年拿个 P6应该没什么问题
- 大道至简,希望你能通过这些文章真的学到框架的原理,进而自己能写出一些框架,走向更高的层级,我的所有源码仓库都在https://github.com/JinJieTan/Peter-,记得给个star
作者:Peter酱
转发链接:https://mp.weixin.qq.com/s/9r3_uLCx1IYdyr5BRzjQgA
猜你喜欢
- 2024-10-22 调用 Express API时出现奇怪的CORS错误怎么办?
- 2024-10-22 详解如何从零开始搭建Express+Vue开发环境
- 2024-10-22 从零开始学习nodejs+express--交互环境
- 2024-10-22 express开发(一)简介与搭建(express创建项目)
- 2024-10-22 express中间件原理connect(express和koa中间件原理的区别)
- 2024-10-22 蓝易云 - centos系统直接部署express教程。
- 2024-10-22 Node + Express + Mysql: Todo List项目让你成全栈
- 2024-10-22 我为 Express 开了外挂(cad中的express怎么显示为中文)
- 2024-10-22 七爪源码:让我们 Dockerize 一个 Node.js Express 应用程序
- 2024-10-22 Node实战篇:Express--jade模板引擎(七)
你 发表评论:
欢迎- 03-19基于layui+springcloud的企业级微服务框架
- 03-19开箱即用的前端开发模板,扩展Layui原生UI样式,集成第三方组件
- 03-19SpringMVC +Spring +Mybatis + Layui通用后台管理系统OneManageV2.1
- 03-19SpringBoot+LayUI后台管理系统开发脚手架
- 03-19layui下拉菜单form.render局部刷新方法亲测有效
- 03-19Layui 遇到的坑(记录贴)(layui chm)
- 03-19基于ASP.NET MVC + Layui的通用后台开发框架
- 03-19LayUi自定义模块的定义与使用(layui自定义表格)
- 最近发表
-
- 基于layui+springcloud的企业级微服务框架
- 开箱即用的前端开发模板,扩展Layui原生UI样式,集成第三方组件
- SpringMVC +Spring +Mybatis + Layui通用后台管理系统OneManageV2.1
- SpringBoot+LayUI后台管理系统开发脚手架
- layui下拉菜单form.render局部刷新方法亲测有效
- Layui 遇到的坑(记录贴)(layui chm)
- 基于ASP.NET MVC + Layui的通用后台开发框架
- LayUi自定义模块的定义与使用(layui自定义表格)
- Layui 2.9.11正式发布(layui2.6)
- Layui 2.9.13正式发布(layui2.6)
- 标签列表
-
- jdk (81)
- putty (66)
- rufus (78)
- 内网穿透 (89)
- okhttp (70)
- powertoys (74)
- windowsterminal (81)
- netcat (65)
- ghostscript (65)
- veracrypt (65)
- asp.netcore (70)
- wrk (67)
- aspose.words (80)
- itk (80)
- ajaxfileupload.js (66)
- sqlhelper (67)
- express.js (67)
- phpmailer (67)
- xjar (70)
- redisclient (78)
- wakeonlan (66)
- tinygo (85)
- startbbs (72)
- webftp (82)
- vsvim (79)
本文暂时没有评论,来添加一个吧(●'◡'●)