三步法解析Express源码,面试必问公司10大问题

先自我介绍一下,小编浙江大学毕业,去过华为、字节跳动等大厂,目前阿里P7

深知大多数程序员,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

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

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

由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新

如果你需要这些资料,可以添加V获取:vip1024c (备注前端)
img

正文

为了明确上述四个概念,先引入一段代码

const express = require(‘./express’);

const res = require(‘./response’);

const app = express();

app.get(‘/test1’, (req, res, next) => {

console.log(‘one’);

next();

}, (req, res) => {

console.log(‘two’);

res.end(‘two’);

})

app.get(‘/test2’, (req, res, next) => {

console.log(‘three’);

next();

}, (req, res) => {

console.log(‘four’);

res.end(‘four’);

})

app.listen(3000);

  1. Application

表示一个Express应用,通过express()即可进行创建。

  1. Router

路由系统,用于调度整个系统的运行,在上述代码中该路由系统包含app.get(‘/test1’,……)和app.get(‘/test2’,……)两大部分

  1. Layer

代表一层,对于上述代码中app.get(‘/test1’,……)和app.get(‘/test2’,……)都可以成为一个Layer

  1. Route

一个Layer中会有多个处理函数的情况,这多个处理函数构成了Route,而Route中的每一个函数又成为Route中的Layer。对于上述代码中,app.get(‘/test1’,……)中的两个函数构成一个Route,每个函数又是Route中的Layer。

了解完上述概念后,结合该幅图,就大概能对整个流程有了直观感受。首先启动服务,然后客户端发起了http://localhost:3000/test2的请求,该过程应该如何运行呢?

  1. 启动服务时会依次执行程序,将该路由系统中的路径、请求方法、处理函数进行存储(这些信息根据一定结构存储在Router、Layer和Route中)
  1. 对相应的地址进行监听,等待请求到达。
  1. 请求到达,首先根据请求的path去从上到下进行匹配,路径匹配正确则进入该Layer,否则跳出该Layer。
  1. 若匹配到该Layer,则进行请求方式的匹配,若匹配方式匹配正确,则执行该对应Route中的函数。

上述解释的比较简单,后续会在细节部分进一步阐述。

三、体会细节


通过上述对Express设计原理的分析,下面将从两个方面做进一步的源码解读,下面流程图是一个常见的Express项目的过程,首先会进行app实例初始化、然后调用一系列中间件,最后建立监听。对于整个工程的运行来说,主要分为两个阶段:初始化阶段、请求处理阶段,下面将以app.get()为例来阐述一下该核心细节。

3.1 初始化阶段

下面利用app.get()这个路由来了解一下工程的初始化阶段。

  1. 首先来看一下app.get()的内容(源代码中app.get()是通过遍历methods的方式产生)

app.get = function(path){

// ……

this.lazyrouter();

var route = this._router.route(path);

route.get.apply(route, slice.call(arguments, 1));

return this;

};

  1. 在app.lazyrouter()会完成router的实例化过程

app.lazyrouter = function lazyrouter() {

if (!this._router) {

this._router = new Router({

caseSensitive: this.enabled(‘case sensitive routing’),

strict: this.enabled(‘strict routing’)

});

// 此处会使用一些中间件

this._router.use(query(this.get(‘query parser fn’)));

this._router.use(middleware.init(this));

}

};

注意:该过程中其实是利用了单例模式,保证整个过程中获取router实例的唯一性。

  1. 调用router.route()方法完成layer的实例化、处理及保存,并返回实例化后的route。(注意源码中是proto.route)

router.prototype.route = function route(path) {

var route = new Route(path);

var layer = new Layer(path, {

sensitive: this.caseSensitive,

strict: this.strict,

end: true

}, route.dispatch.bind(route));

layer.route = route;// 把route放到layer上

this.stack.push(layer); // 把layer放到数组中

return route;

};

  1. 将该app.get()中的函数存储到route的stack中。(注意源码中也是通过遍历method的方式将get挂载到route的prototype上)

Route.prototype.get = function(){

var handles = flatten(slice.call(arguments));

for (var i = 0; i < handles.length; i++) {

var handle = handles[i];

// ……

// 给route添加layer,这个层中需要存放方法名和handler

var layer = Layer(‘/’, {}, handle);

layer.method = method;

this.methods[method] = true;

this.stack.push(layer);

}

return this;

};

注意:上述代码均删除了源码中一些异常判断逻辑,方便读者看清整体框架。

通过上述的分析,可以看出初始化阶段主要做了两件事情:

  1. 将路由处理方式(app.get()、app.post()……)、app.use()等划分为路由系统中的一个Layer。
  1. 对于每一个层中的处理函数全部存储至Route对象中,一个Route对象与一个Layer相互映射。

3.2 请求处理阶段

当服务启动后即进入监听状态,等待请求到达后进行处理。

  1. app.listen()使服务进入监听状态(实质上是调用了http模块)

app.listen = function listen() {

var server = http.createServer(this);

return server.listen.apply(server, arguments);

};

  1. 当连接建立会调用app实例,app实例中会立即执行app.handle()函数,app.handle()函数会立即调用路由系统的处理函数router.handle()

app.handle = function handle(req, res, callback) {

var router = this._router;

// 如果路由系统中处理不了这个请求,就调用done方法

var done = callback || finalhandler(req, res, {

env: this.get(‘env’),

onerror: logerror.bind(this)

});

//……

router.handle(req, res, done);

};

  1. router.handle()主要是根据路径获取是否有匹配的layer,当匹配到之后则调用layer.prototype.handle_request()去执行route中内容的处理

router.prototype.handle = function handle(req, res, out) {

// 这个地方参数out就是done,当所有都匹配不到,就从路由系统中出来,名字很形象

var self = this;

// ……

var stack = self.stack;

// ……

next();

function next(err) {

// ……

// get pathname of request

var path = getPathname(req);

// find next matching layer

var layer;

var match;

var route;

while (match !== true && idx < stack.length) {

layer = stack[idx++];

match = matchLayer(layer, path);

route = layer.route;

// ……

}

// no match

if (match !== true) {

return done(layerError);

}

// ……

// Capture one-time layer values

req.params = self.mergeParams

? mergeParams(layer.params, parentParams)
layer.params;

计算机网络

  • HTTP 缓存

  • 你知道 302 状态码是什么嘛?你平时浏览网页的过程中遇到过哪些 302 的场景?

  • HTTP 常用的请求方式,区别和用途?

  • HTTPS 是什么?具体流程

  • 三次握手和四次挥手

  • 你对 TCP 滑动窗口有了解嘛?

  • WebSocket与Ajax的区别

  • 了解 WebSocket 嘛?

  • HTTP 如何实现长连接?在什么时候会超时?

  • TCP 如何保证有效传输及拥塞控制原理。

  • TCP 协议怎么保证可靠的,UDP 为什么不可靠?

算法

  • 链表

  • 字符串

  • 数组问题

  • 二叉树

  • 排序算法

  • 二分查找

  • 动态规划

  • BFS

  • DFS

  • 回溯算法

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以添加V获取:vip1024c (备注前端)
img

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
/img-blog.csdnimg.cn/img_convert/614771dc66a0fec7a3e33c2c7e1fa878.png)

算法

  • 链表

  • 字符串

  • 数组问题

  • 二叉树

  • 排序算法

  • 二分查找

  • 动态规划

  • BFS

  • DFS

  • 回溯算法

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以添加V获取:vip1024c (备注前端)
[外链图片转存中…(img-KsCoeg8K-1713444488178)]

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

  • 13
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值