组件 react read-code interview node server vue Blog Record
interview 面试经验之手写js基础版 React 面试相关汇总 手撕React Hooks Nodejs相关八股文 interview 综述 css css面试题 css common css error css loader的各种区别 tailwindcss用法累计 Postcss基础 实现 css-module(笔记) 实现 px 转rem(笔记) 如何实现自己的tailwindcss postcss和babel区别 guide 202307手写汇总 202308手写汇总(2) 202309手写汇总(3) 202311手写汇总(4)-HotWriting 202311手写汇总(5)-MiddleWriting 202311手写汇总(6)-hardWriting 手撕热门算法题1 手撕热门算法题2 连续这么多手写,需要复盘和回归 手撕热门算法题3 手撕热门算法题4 手撕算法面试题1 手写基础且重要的TS高级类型 积少成多 面试经验 TS TS总览 泛型 函数重载定义 条件类型condition infer推断 逆变和协变以及类型收窄和宽泛 handwritingTs.md 索引类型和映射类型 type 和 interface区别 UMD包如何导出ts类型 模式匹配 重新构造做转换 递归模式类型体操 unknown和any类型区别 数组相关以及其他类型体操 typeof以及类型守护 三种单独声明类型和三种模块语法 hardwritingTs.md 装饰器与反射元数据 handwritingTsMiddle.md 基础且相当重要的ts手写 TypeScript 5.0 beta 发布:新版 ES 装饰器、泛型参数的常量修饰、枚举增强等 Basic Basic综述 手写js 手写js2 四种异步编程 event loop js基础之继承 前端设计模式 四种异步之Promise 四种异步之Promise(2)-面试题 Promisify手写 四种异步之Promise(2)-面试题并发限制 代码片段和打印题 引擎/运行时和调用堆栈/浏览器 this/apply/call/bind 作用域链以及闭包 函数式编程FP 垃圾回收Garbage Collection和数据类型 async/defer/proload/prefetch 常考的打印题 weakMap/weakMap应用场景 前端设计模式IOC/DI Proxy/Reflecy应用场景 手写各类排序 Web Worker和service worker Promise和timeout实战 weixin小程序相关 babel/postcss/webpack/rollup/vite中的插件机制 装饰器和反射的基础知识 前端性能优化——首页资源压缩63%、白屏时间缩短86% 前端安全相关 csrf 和 xss js BigInt大数 axios 手写 H5 APM系统性能指标 通常前端三种方式埋点 基于web原生API:BarcodeDetector 解析二维码 正则表达式总结练习 JS各种数据流之间的格式 web-RTC 通用业务 common综述 主题换肤前端实现 如何实现优雅的骨架屏 国际化i8n 前端监控系统 长列表处理(虚拟滚动和分片渲染) 工具函数 网络 总览 Http1.x/Http2.x HTTPS HTTPS以及背后的加密原理 HTTP3.0 Cookie相关 TCP/UDP H5端如何唤起App DNS域名解析 HTTP/1.x 的 keep-alive 吗?它与 HTTP/2 多路复用的区别 websocket 原理 算法 数据结构和算法综述 广度优先遍历BFS 深度度优先遍历DFS 二叉树 二叉树中等 二叉树困难题 stack相关的积累 贪心算法相关 string相关的积累 链表相关 最长xx相关 LRU(least Recently used)最近最少使用 优先队列 动态规划1.0 回溯 动态规划Hard 二叉堆 hot100 hot100 browser browser总览 DOM总览 cookie/jwt/session web缓存 Cookie 的 SameSite 属性 深入调试原理 js Basic 设计模式 设计模式之访问者模式 设计模式之装饰者模式 /interview/experience 0310积累面试经验之base相关 面试经验之手写之历史汇总 面试经验之手写js中阶版 0214积累面试经验 0306积累面试经验之高阶函数 1230面试经验 0601积累面试经验 0310积累面试经验之Array相关 0703面试积累 面试经验之手写算法基础版 1130积累面试 1201积累面试 1207积累面试 1208面试小记 20240116积累面试 20240312积累面试 20240314积累面试 20240322积累面试 20240520积累面试 20240326积累面试 列表
1.Nodejs 事件循环 2.说说中间件,如何封装一个中间件,中间件的异常处理是怎么做的 3.说说你对洋葱模型的理解 4.手写 compose,或者说写一个 简易的 koa-compose 5.说说 commonjs 跟 esmodule 区别 6.说说 tree Shaking 原理 7.说说 nodejs 中的 EventEmitter 事件订阅机制,如何实现一个 eventEmitter 8.Nodejs 如何利用多核 CPU or 说说你对 Node.js 中多进程的理解 9.pm2 守护进程原理是什么 10.Nodejs 中的 cluster 和 fork 模式区别 11.node 中的回调函数变成 then 链式调用,手写 promisify 12.Nodejs 异步 IO 模型 13.Node 的日志和负载均衡怎么做的? 14.Node 开启子进程的方法有哪些? 15.在操作系统中,进程和线程如何进行通信?请列举几种常见的进程间通信方式。 1.Nodejs 事件循环
请移步事件循环
大体是这 6 个步骤:
timer -> I/O 回调 -> idle 闲置阶段 -> Poll -> check -> close Callback
2.说说中间件,如何封装一个中间件
NodeJS 中,中间件主要是指处理 http 请求和响应的函数,在 express、koa 等 web 框架中,中间件的本质为一个回调函数,参数包含请求对象、响应对象和执行下一个中间件的函数
Koa 中间件采用的是洋葱圈模型,每次执行下一个中间件传入两个参数:
ctx :封装了 request 和 response 的变量 next :进入下一个要执行的中间件的函数,下面则通过中间件封装 http 请求过程中几个常用的功能: 参考
express 中间件 token 校验
module.exports = function resolveToken(options) { return (ctx, next) => { try { // get Token const token = ctx.header.authorization if (token) { try { await verify(token) } catch(e) { console.log(err) } } } catch(e) { console.log(e) } } } 在这个示例中,resolveToken 函数返回一个函数,该函数接收请求和响应对象,并且调用 next() 函数来将控制权传递给下一个中间件函数。
日子模块中间件
module.exports = (option) => async (ctx, next) => {
const startTime = Date.now();
const requestTime = Date.now();
await next();
const ms = Date.now() - startTime;
let logout = ${ctx.request.ip} -- ${requestTime} -- ${ctx.method} -- ${ctx.url} -- ${ms}ms;
// 输出日志文件
fs.appendFileSync('./log.txt', logout + '\n');
};
3.说说你对洋葱模型的理解
Koa 框架是一个 Node.js 的 Web 应用程序框架,它通过中间件(Middleware)机制实现了业务逻辑的分层和复用。Koa 中使用的中间件机制被称为洋葱模型(Onion Model),其核心思想是将 HTTP 请求和响应对象依次传递给各个中间件函数,形成一条类似于洋葱的管道,最终返回响应结果。
具体来说,Koa 洋葱模型的处理流程可以大致分为四个阶段:
请求阶段:从外到内依次执行请求相关的中间件,例如解析请求体、设置响应头等操作。
业务阶段:执行业务逻辑相关的中间件,例如处理授权、验证身份、路由分发等操作。
响应阶段:从内到外依次执行响应相关的中间件,例如格式化响应数据、设置响应头等操作。
错误处理阶段:如果在前面的中间件过程中出现了错误,则会跳过后续中间件并交给错误处理中间件来处理异常情况。
在这个过程中,每个中间件都可以根据需要对请求和响应对象进行修改、扩展、封装等操作,并将控制权传递给下一个中间件,形成了一条流水线式的处理模式。这种设计可以大大提高代码的复用和可读性,同时也方便了对程序行为进行监控、调试和优化。
4.手写 compose
function compose(middlewares) { return (ctx, next) => { return dispatch(0); function dispatch(i) { var fn = middlewares[i]; if (i === middlewares.length) fn = next; if (!fn) return Promise.resolve(''); try { return Promise.resolve(fn.apply(ctx, dispatch(i + 1))); } catch (e) { return Promise.reject(e); } } }; } 5.说说 commonjs 跟 esmodule 区别
6.说说 tree Shaking 原理
7.说说 nodejs 中的 EventEmitter 事件订阅机制,如何实现一个 eventEmitter
8.Nodejs 如何利用多核 CPU?
在 Node.js 中,JS 也是单线程的,只有一个主线程用于执行任务。但是,在 Node.js 中可以使用多进程来利用多核机器,以充分利用 CPU 系统资源。
Node.js 提供了 cluster/child_process 模块,可以轻松创建子进程来处理任务。通过将任务分配给不同的子进程,每个子进程可以在自己的线程上执行任务,从而实现多核机器的利用。 Node.js 也提供了 worker_threads 模块,可以创建真正的多线程应用程序。这个模块允许开发者创建和管理多个工作线程,每个线程都可以独立地执行任务。 Node.js 中的 worker_threads[4] 模块是用于创建多线程应用程序的官方模块。它允许在 Node.js 程序中创建和管理真正的操作系统线程,以实现并行处理和利用多核 CPU 的能力。
利用的是 Node.js 的事件循环机制和异步非阻塞的 I/O操作。Node.js 使用事件驱动的模型来处理请求,当有请求到达时,Node.js 将其放入事件队列中,然后通过事件循环来处理这些请求。在等待 I/O 操作的过程中,Node.js 不会阻塞其他请求的处理,而是继续处理其他请求。这样,即使 JavaScript 是单线程的,但在实际运行中,多个请求可以同时处理,充分利用了多核系统的能力。 9.pm2 守护进程原理是什么
守护进程是什么:守护进程是一个在后台运行并且不受任何终端控制的进程,并且能够因为某个异常导致进程退出的时候重启子进程。守护进程一般在系统启动时开始运行,除非强行终止,否则直到系统关机都保持运行.使用命令 ps axj 查看当前守护进程。
实现监听子进程退出
利用 NodeJS 的事件机制,监听 exit 事件
在 master.js 中使用 fork 创建子进程监听 exit 事件,1s 之后创建一个新的子进程
// master.js const { fork } = require('child_process');
const forkChild = () => { const child = fork('log.js'); child.on('exit', () => { setTimeout(() => { forkChild(); }, 1000); }); }; forkChild();
// log.js const fs = require('fs'); const path = require('path');
fs.open(path.resolve(__dirname, 'log.txt'), 'w', function (err, fd) { setInterval(() => { fs.write(fd, process.pid + '\n', function () {}); }, 2000); }); 使用 node master 启动项目之后,使用 kill 命令杀掉对应的子进程,能够成功重启子进程,守护进程生效~
如何退出终端运行?
使用 setsid
在 Node 中如何调用 setsid 方法呢? Node 尚未对 setsid 进程封装,但是我们可以通过 child_process.spawn 来调用该方法。 spawn 中有一个配置项 detached,当其 detached: true 时,会调用 setsid 方法
在非 Windows 平台上,如果 options.detached 设置为 true,则子进程将成为新进程组和会话的领导者。 子进程可以在父进程退出后继续运行。
开始实现
在 command 中使用 child_process.spawn(master) 创建子进程 对进程 master 执行 setsid 方法 进程 command 退出,进程 master 由 init 进程接管,此时进程 master 为守护进程 创建 command,使用 spawn 方法衍生子进程
//command.js const { spawn } = require('child_process');
spawn('node', ['./master.js'], { detached: true, });
process.exit(0); 当我们执行 node command 之后,我们的主进程就会关闭,但是我们的子进程还在继续运行,且不受终端控制,该进程就是我们创建的守护进程
10.Nodejs 中的 cluster 模式和 fork 模式区别
fork 分叉模式(child_process.fork(modulePath[, args][, options])),单实例多进程
常用于多语言混编,比如 php、python 等,不支持端口复用,需要自己做应用的端口分配和负载均衡的子进程业务代码。缺点就是单服务器实例容易由于异常会导致服务器实例崩溃。
for Example: 默认情况下,pm2 将使用 node 这样 pm2 start server.js
cluster 集群模式,多实例多进程,
但是只支持 node,端口可以复用,不需要额外的端口配置,0 代码实现负载均衡。优点就是由于多实例机制,可以保证服务器的容错性,就算出现异常也不会使多个服务器实例同时崩溃。 for Example: pm2 start -i 4 server.js 将启动 4 个 server.js 实例并让集群模块处理负载平衡
共同点:都是多进程,都需要消息机制或数据持久化来实现数据共享