手写Express核心原理,再也不怕面试官问我Express原理,8个优秀的CSS实践

return app;

}

module.exports = createApplication;

如代码所示,我们将app改成一个对象,也是没有问题的。

.

实现app.get()方法


app.get方法接受两个参数,路径和回调函数。

// myExpress.js

let http = require(‘http’);

function createApplication() {

let app = {};

app.routes = []

app.get = function (path, handler) {

let layer = {

method: ‘get’,

path,

handler

}

app.routes.push(layer)

}

app.listen = function () {

let server = http.createServer(function (req, res) {

res.end(‘hahha’)

})

server.listen(…arguments);

}

return app;

}

module.exports = createApplication;

如上面代码所示,给app添加了route对象,然后get方法执行的时候,将接收到的两个参数:路径和方法,包装成一个对象push到routes里了。

可想而知,当我们在浏览器输入路径的时候,肯定会执行http.createServer里的回调函数。

所以,我们需要在这里 获得浏览器的请求路径。解析得到路径.

然后遍历循环routes,寻找对应的路由,执行回调方法。如下面代码所示。

// myExpress.js

let http = require(‘http’);

const url  = require(‘url’);

function createApplication() {

let app = {};

app.routes = []

app.get = function (path, handler) {

let layer = {

method: ‘get’,

path,

handler

}

app.routes.push(layer)

}

app.listen = function () {

let server = http.createServer(function (req, res) {

// 取出layer

// 1. 获取请求的方法

let m = req.method.toLocaleLowerCase();

let { pathname } = url.parse(req.url, true);

// 2.找到对应的路由,执行回调方法

for (let i = 0 ; i< app.routes.length; i++){

let {method,path,handler} = app.routes[i]

if (method === m && path === pathname ) {

handler(req,res);

}

}

res.end(‘hahha’)

})

server.listen(…arguments);

}

return app;

}

module.exports = createApplication;

运行一下代码。可见运行成功:

实现post等其他方法。


很简单,我们可以直接复制app.get方法,然后将method的值改成post就好了。

// myExpress.js

let http = require(‘http’);

const url  = require(‘url’);

function createApplication() {

。。。

app.get = function (path, handler) {

let layer = {

method: ‘get’,

path,

handler

}

app.routes.push(layer)

}

app.post = function (path, handler) {

let layer = {

method: ‘post’,

path,

handler

}

app.routes.push(layer)

}

。。。

return app;

}

module.exports = createApplication;

这样是可以实现,但是除了post和get,还有其他方法啊,难道每一个我们都要这样写嘛?,当然不是,有个很简单的方法。

// myExpress.js

function createApplication() {

http.METHODS.forEach(method => {

method = method.toLocaleLowerCase()

app[method] = function (path, handler) {

let layer = {

method,

path,

handler

}

app.routes.push(layer)

}

});

}

module.exports = createApplication;

如代码所示,http.METHODS是一个方法数组。如下面所示的数组

[“GET”,“POST”,“DELETE”,“PUT”]。

遍历方法数组,就可以实现所有方法了。

测试跑了一下,确实成功。

实现app.all方法


all表示的是匹配所有的方法,

app.all('/user')表示匹配所有路径是/user的路由

app.all('*')表示匹配任何路径 任何方法 的 路由

实现all方法也非常简单,如下代码所示

app.all = function (path, handler){

let layer = {

method: “all”,

path,

handler

}

app.routes.push(layer)

}

然后只需要续改下路由器匹配的逻辑,如下代码所示,只需要修改下判断。

app.listen = function () {

let server = http.createServer(function (req, res) {

// 取出layer

// 1. 获取请求的方法

let m = req.method.toLocaleLowerCase();

let { pathname } = url.parse(req.url, true);

// 2.找到对应的路由,执行回调方法

for (let i = 0 ; i< app.routes.length; i++){

let {method,path,handler} = app.routes[i]

if ((method === m || method === ‘all’) && (path === pathname || path === “*”)) {

handler(req,res);

}

}

console.log(app.routes);

res.end(‘hahha’)

})

server.listen(…arguments);

}

可见成功。

中间件app.use的实现


这个方法的实现,跟其他方法差不多,如代码所示。

app.use = function (path, handler) {

let layer = {

method: “middle”,

path,

handler

}

app.routes.push(layer)

}

但问题来了,使用中间件的时候,我们会使用next方法,来让程序继续往下执行,那它是怎么执行的。

app.use(function (req, res, next) {

console.log(‘Time:’, Date.now());

next();

});

所以我们必须实现next这个方法。

其实可以猜想,next应该就是一个疯狂调用自己的方法。也就是递归

而且每递归一次,就把被push到routes里的handler拿出来执行。

实际上,不管是app.use还说app.all还是app.get。其实都是把layer放进routes里,然后再统一遍历routes来判断该不该执行layer里的handler方法。可以看下next方法的实现。

function next() {

// 已经迭代完整个数组,还是没有找到匹配的路径

if (index === app.routes.length) return res.end('Cannot find ')

let { method, path, handler } = app.routes[index++] // 每次调用next就去下一个layer

if (method === ‘middle’) { // 处理中间件

if (path === ‘/’ || path === pathname || pathname.starWidth(path + ‘/’)) {

handler(req, res, next)

} else { // 继续遍历

next();

}

} else { // 处理路由

if ((method === m || method === ‘all’) && (path === pathname || path === “*”)) {

handler(req, res);

} else {

next();

}

}

}

可以看到是递归方法的遍历routes数组。

而且我们可以发现,如果是使用中间件的话,那么只要path是“/”或者前缀匹配,这个中间件就会执行。由于handler会用到参数req和res。所以这个next方法要在 listen里面定义。

如下代码所示:

// myExpress.js

let http = require(‘http’);

const url = require(‘url’);

function createApplication() {

let app = {};

app.routes = [];

let index = 0;

app.use = function (path, handler) {

let layer = {

method: “middle”,

path,

handler

}

app.routes.push(layer)

}

app.all = function (path, handler) {

let layer = {

method: “all”,

path,

handler

}

app.routes.push(layer)

}

http.METHODS.forEach(method => {

method = method.toLocaleLowerCase()

app[method] = function (path, handler) {

let layer = {

method,

path,

handler

}

app.routes.push(layer)

}

});

app.listen = function () {

let server = http.createServer(function (req, res) {

// 取出layer

// 1. 获取请求的方法

let m = req.method.toLocaleLowerCase();

let { pathname } = url.parse(req.url, true);

// 2.找到对应的路由,执行回调方法

function next() {

// 已经迭代完整个数组,还是没有找到匹配的路径

if (index === app.routes.length) return res.end('Cannot find ')

let { method, path, handler } = app.routes[index++] // 每次调用next就去下一个layer

if (method === ‘middle’) { // 处理中间件

if (path === ‘/’ || path === pathname || pathname.starWidth(path + ‘/’)) {

handler(req, res, next)

} else { // 继续遍历

next();

}

} else { // 处理路由

if ((method === m || method === ‘all’) && (path === pathname || path === “*”)) {

handler(req, res);

} else {

next();

}

}

}

next()

res.end(‘hahha’)

})

server.listen(…arguments);

}

return app;

}

module.exports = createApplication;

当我们请求路径就会发现中间件确实执行成功。

不过,这里的中间价实现还不够完美。

因为,我们使用中间件的时候,是可以不用传递路由的。例如:

app.use((req,res) => {

console.log(“我是没有路由的中间价”);

})

这也是可以使用的,那该怎么实现呢,其实非常简单,判断一下有没有传递路径就好了,没有的话,就给个默认路径“/”,实现代码如下:

app.use = function (path, handler) {

if(typeof path !== “string”) { // 第一个参数不是字符串,说明不是路径,而是方法

handler = path;

path = “/”

}

let layer = {

method: “middle”,

path,

handler

}

app.routes.push(layer)

}

看,是不是很巧妙,很容易。

我们试着访问路径“/middle”

咦?第一个中间件没有执行,为什么呢?

对了,使用中间件的时候,最后要执行next(),才能交给下一个中间件或者路由执行。

当我们请求“/middle”路径的时候,可以看到确实请求成功,中间件也成功执行。说明我们的逻辑没有问题。

实际上,中间件已经完成了,但是别忘了,还有个错误中间件?

什么是错误中间件?


错误处理中间件函数的定义方式与其他中间件函数基本相同,差别在于错误处理函数有四个自变量而不是三个,专门具有特征符 (err, req, res, next):

app.use(function(err, req, res, next) {

console.error(err.stack);

res.status(500).send(‘Something broke!’);

});
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数前端工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Web前端开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。

img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上前端开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注:前端)

最后

javascript是前端必要掌握的真正算得上是编程语言的语言,学会灵活运用javascript,将对以后学习工作有非常大的帮助。掌握它最重要的首先是学习好基础知识,而后通过不断的实战来提升我们的编程技巧和逻辑思维。这一块学习是持续的,直到我们真正掌握它并且能够灵活运用它。如果最开始学习一两遍之后,发现暂时没有提升的空间,我们可以暂时放一放。继续下面的学习,javascript贯穿我们前端工作中,在之后的学习实现里也会遇到和锻炼到。真正学习起来并不难理解,关键是灵活运用。

资料领取方式:点击这里免费领取前端全套学习资料

css源码pdf

JavaScript知识点
份《2024年Web前端开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。**

[外链图片转存中…(img-XWpnCBE1-1712364200352)]

[外链图片转存中…(img-CfeHslLW-1712364200353)]

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上前端开发知识点,真正体系化!

[外链图片转存中…(img-DIGAeGDn-1712364200353)]

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注:前端)

最后

javascript是前端必要掌握的真正算得上是编程语言的语言,学会灵活运用javascript,将对以后学习工作有非常大的帮助。掌握它最重要的首先是学习好基础知识,而后通过不断的实战来提升我们的编程技巧和逻辑思维。这一块学习是持续的,直到我们真正掌握它并且能够灵活运用它。如果最开始学习一两遍之后,发现暂时没有提升的空间,我们可以暂时放一放。继续下面的学习,javascript贯穿我们前端工作中,在之后的学习实现里也会遇到和锻炼到。真正学习起来并不难理解,关键是灵活运用。

资料领取方式:点击这里免费领取前端全套学习资料

[外链图片转存中…(img-5hAB7vgU-1712364200353)]

[外链图片转存中…(img-cu5gjw1p-1712364200353)]

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值