Node(Koa2)面试题

327 阅读5分钟

1. 对 Node 的理解

Node 是一个基于 Chrome V8引擎的 JavaScript 运行环境

  1. 多线程

    我们常说的 Node 单线程实际是指 JavaScript 的执行是单线程的。而 Node 和浏览器都是多线程的,它们确实只有一个主线程,但有多个在底层工作的线程,比如负责垃圾回收的。

  2. 事件驱动

    在 Node 中所做的大部分工作都是采用事件驱动的方式。只有当触发某个指定的事件时,才会执行相应的代码。

  3. 非阻塞 I/O 机制

    通过非阻塞 I/O 机制(异步回调)执行异步请求时,会将请求放到回调函数中处理,然后继续执行请求后面的代码,不用等待结果返回,从而实现高并发问题。事件循环是 Node 非阻塞 I/O 操作的原理。

2. NodeJS 环境和浏览器环境的区别

  • 全局环境下,Node 中的 this 默认指向global,而浏览器中默认指向window
  • Node 环境中没有 html 节点,也不会操作DOM、BOM,而浏览器可以操作DOM、BOM
  • Node 使用CommonJS模块系统、require引入;浏览器则遵循ES6 Module标准、import引入。

3. Node 的事件循环机制

浏览器中的事件循环

libuv引擎中的事件循环分为6个阶段,每个阶段都有一个 FIFO 队列存有要执行的回调函数。当队列为空或达到系统上限时,事件循环就会进入下一阶段。

image.png

  1. timers 定时器阶段:执行setTimeout、setInterval的回调。
  2. I/O 阶段:处理上一轮循环中未执行的 I/O 回调。
  3. Idle, prepare 阶段:闲置阶段,仅在内部使用。
  4. Poll 轮询阶段:会处理大部分的事件,例如读文件、处理 http 请求等(“计时器”或 setImmediate 回调除外)。
  • 如果 poll 队列不为空,那就依次执行队列里的回调,直到队列被清空或达到系统限制。
  • 如果 poll 队列为空:
    • 如果有setImmediate()回调,事件循环就会进入check阶段;
    • 如果没有xxx,那就会等待新的回调进入 poll 队列中并立即执行。 另外,当 poll 队列为空且有计时器到期时,事件循环会回到 timer 阶段执行回调。
  1. Check 阶段:执行setImmediate()回调。
  2. Close callback 阶段:执行一些关闭的回调函数。

问题:Node 的事件循环和浏览器的事件循环的区别?

两者对宏任务、微任务的定义是一样的。区别在于:

  • 忽略:微任务执行时机(v10之后已经和浏览器顺序一致了)
    • 浏览器环境下,每执行完一个宏任务会去执行微任务队列。
    • Node 环境下,微任务会在事件循环的各个阶段执行。也就是说,当事件循环的一个阶段执行完毕(执行一队 setTimeout),就会去执行微任务队列。
  • process.nextTick()优先级
    • 浏览器环境下,process.nextTick和其他微任务的优先级是一样的。
    • Node 环境下,process.nextTick的优先级高于其他微任务。

对 koa 的理解

koa 的中间件是通过async/await实现的

Koa 提供一个Context对象表示一次会话的上下文(包括 request 和 response),通过加工这个对象就可以控制返回给用户的内容。

基本上,Koa 所有的功能都是通过中间件实现的,中间件会接受两个参数:Context 对象和 next 函数。只要调用 next 函数就可以把执行权转交给下一个中间件。

const Koa = require('koa');
const app = new Koa();

app.use((ctx, next) => { // 同步中间件
    console.log(1);
    next();
    console.log(2);
});

app.use(async (ctx, next) => { // 异步中间件
    console.log(3);
    next();
    console.log(4);
});

app.listen(8000) // 开启一个 HTTP 服务

koa 中间件运行流程:洋葱模型

多个中间件会形成一个栈结构,以“先进后出”的顺序执行。

  • 首先执行最外层的中间件,然后调用next函数把执行权交给下一个中间件;
  • 依此执行直到最内层的中间件执行结束后,再把执行权交回上一个中间件执行,直到执行到最外层中间件。

koa 中间件实现原理

koa 中间件是通过compose方法实现的。

  • use方法用来加载中间件,它将中间件函数 push 到内部的中间件栈中;
  • listen方法实际上是 Node.js 的http模块的createServer方法的语法糖,用来创建一个服务。内部会通过callback方法去执行compose函数。
  • compose函数会返回一个Promise函数。
  1. koa 中的 API

koa 实践

  1. 项目相关
  • koa-bodyparser:用于解析请求体Request Body,支持不同格式的请求体。
  • koa-logger:打印日志。
  • koa-router:koa 的路由库。
// 添加路由
router.get('/', async (ctx) => {
    ctx.body = '<h1>欢迎光临 index page 页面</h1>';
});

// 加载路由中间件
router.use('/');
app.use(router.routes())
  1. 工作相关
  • buvid 中间件:生成 buvid 并写入 cookie,供下游链路使用。
  • error 中间件:
  • ipChecker 中间件:检查 ip 所对应的地区是否在黑名单内

koa1 和 koa2 的区别

实现异步的方式

  • koa1:generator/yield
  • koa2:async/await
  • express:回调函数

koa2 和 express 的区别

koa的思想是先经过业务路由,然后再处理中间件,express的思想是先经过中间件,比如鉴权,如果中间件验证不通过,就不会处理业务了。koa通过await next()来统计中间件耗时,express可以在第一个中间件的时候把时间挂在到req上,等流转到业务代码的时候就可以统计中间件耗时了。

用过 koa 的哪些组件?koa-router、koa-bodyparser..