1. 对 Node 的理解
Node 是一个基于 Chrome V8引擎的 JavaScript 运行环境。
-
多线程
我们常说的 Node 单线程实际是指 JavaScript 的执行是单线程的。而 Node 和浏览器都是多线程的,它们确实只有一个主线程,但有多个在底层工作的线程,比如负责垃圾回收的。
-
事件驱动
在 Node 中所做的大部分工作都是采用事件驱动的方式。只有当触发某个指定的事件时,才会执行相应的代码。
-
非阻塞 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 队列存有要执行的回调函数。当队列为空或达到系统上限时,事件循环就会进入下一阶段。
- timers 定时器阶段:执行
setTimeout、setInterval
的回调。 - I/O 阶段:处理上一轮循环中未执行的 I/O 回调。
- Idle, prepare 阶段:闲置阶段,仅在内部使用。
- Poll 轮询阶段:会处理大部分的事件,例如读文件、处理 http 请求等(“计时器”或 setImmediate 回调除外)。
- 如果 poll 队列不为空,那就依次执行队列里的回调,直到队列被清空或达到系统限制。
- 如果 poll 队列为空:
- 如果有
setImmediate()
回调,事件循环就会进入check
阶段; - 如果没有xxx,那就会等待新的回调进入 poll 队列中并立即执行。 另外,当 poll 队列为空且有计时器到期时,事件循环会回到 timer 阶段执行回调。
- 如果有
- Check 阶段:执行
setImmediate()
回调。 - 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
函数。
- koa 中的 API
koa 实践
- 项目相关
- 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())
- 工作相关
- 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..