21天筑基期--Node.js+工程化系列
Node.js
如何实现jwt鉴权机制
JWT(JSON Web Token),本质就是一个字符串书写规范,如下图,作用是用来在用户和服务器之间传递安全可靠的信息
Token
的使用分成了两部分:
- 生成token:登录成功的时候,颁发token
- 验证token:访问某些资源或者接口时,验证token
对中间件概念
中间件(Middleware)是介于应用系统和系统软件之间的一类软件,它使用系统软件所提供的基础服务(功能),衔接网络上应用系统的各个部分或不同的应用,能够达到资源共享、功能共享的目的
例如在express
、koa
等web
框架中,中间件的本质为一个回调函数,参数包含请求对象、响应对象和执行下一个中间件的函数
中间件模型不同:express 的中间件模型为线型,而 koa 的为U型(洋葱模型)。
Nodejs中的事件循环机制
- timers阶段:这个阶段执行timer(setTimeout、setInterval)的回调
- 定时器检测阶段(timers):本阶段执行 timer 的回调,即 setTimeout、setInterval 里面的回调函数
- I/O事件回调阶段(I/O callbacks):执行延迟到下一个循环迭代的 I/O 回调,即上一轮循环中未被执行的一些I/O回调
- 闲置阶段(idle, prepare):仅系统内部使用
- 轮询阶段(poll):检索新的 I/O 事件;执行与 I/O 相关的回调(几乎所有情况下,除了关闭的回调函数,那些由计时器和 setImmediate() 调度的之外),其余情况 node 将在适当的时候在此阻塞
- 检查阶段(check):setImmediate() 回调函数在这里执行
- 关闭事件回调阶段(close callback):一些关闭的回调函数,如:socket.on('close', ...)
说说什么是进程?什么是线程?
进程
进程是一种抽象的概念,从来没有统一的标准定义看,一般由程序、数据集合和进程控制块三部分组成:
- 程序用于描述进程要完成的功能,是控制进程执行的指令集
- 数据集合是程序在执行时所需要的数据和工作区
- 程序控制块,包含进程的描述信息和控制信息,是进程存在的唯一标志
线程
线程(thread)是操作系统能够进行运算调度的最小单位,其是进程中的一个执行任务(控制单元),负责当前进程中程序的执行
一个进程至少有一个线程,一个进程可以运行多个线程,这些线程共享同一块内存,线程之间可以共享对象、资源,如果有冲突或需要协同,还可以随时沟通以解决冲突或保持同步
区别
- 本质区别:进程是操作系统资源分配的基本单位,而线程是任务调度和执行的基本单位
- 在开销方面:每个进程都有独立的代码和数据空间(程序上下文),程序之间的切换会有较大的开销;线程可以看做轻量级的进程,同一类线程共享代码和数据空间,每个线程都有自己独立的运行栈和程序计数器(PC),线程之间切换的开销小
- 所处环境:在操作系统中能同时运行多个进程(程序);而在同一个进程(程序)中有多个线程同时执行(通过CPU调度,在每个时间片中只有一个线程执行)
- 内存分配方面:系统在运行的时候会为每个进程分配不同的内存空间;而对线程而言,除了CPU外,系统不会为线程分配内存(线程所使用的资源来自其所属进程的资源),线程组之间只能共享资源
- 包含关系:没有线程的进程可以看做是单线程的,如果一个进程内有多个线程,则执行过程不是一条线的,而是多条线(线程)共同完成的;线程是进程的一部分,所以线程也被称为轻权进程或者轻量级进程
举个例子:进程=火车,线程=车厢
- 线程在进程下行进(单纯的车厢无法运行)
- 一个进程可以包含多个线程(一辆火车可以有多个车厢)
- 不同进程间数据很难共享(一辆火车上的乘客很难换到另外一辆火车,比如站点换乘)
- 同一进程下不同线程间数据很易共享(A车厢换到B车厢很容易)
- 进程要比线程消耗更多的计算机资源(采用多列火车相比多个车厢更耗资源)
- 进程间不会相互影响,一个线程挂掉将导致整个进程挂掉(一列火车不会影响到另外一列火车,但是如果一列火车上中间的一节车厢着火了,将影响到所有车厢)
Node 中的 Buffer 的理解
在Node
应用中,需要处理网络协议、操作数据库、处理图片、接收上传文件等,在网络流和文件的操作中,要处理大量二进制数据,而Buffer
就是在内存中开辟一片区域(初次初始化为8KB),用来存放二进制数据
- 网络通信:可以使用Buffer.from()方法将字符串转换为二进制数据,然后使用net模块进行网络通信:
const net = require('net');
const client = net.createConnection({ port: 8080 }, () => {
// 将字符串转换为二进制数据
const data = Buffer.from('Hello, world!', 'utf8');
// 发送数据
client.write(data);
});
- 文件操作,用Buffer来存储文件数据:
const fs = require('fs');
// 读取文件,并将数据存储到 Buffer 对象中
const data = fs.readFileSync('/path/to/file');
// 处理数据
// ...
- 加密解密,例如,可以使用 crypto 模块创建加密解密算法需要的二进制数据:
const crypto = require('crypto');
// 创建加密解密算法需要的二进制数据
const key = Buffer.from('mysecretkey', 'utf8');
const iv = Buffer.alloc(16);
// 创建加密解密算法对象
const cipher = crypto.createCipheriv('aes-256-cbc', key, iv);
// 加密数据
const encrypted = Buffer.concat([cipher.update(data), cipher.final()]);
- 图像处理:
Node 中的 Stream 的理解
Stream是一种处理流式数据的抽象接口,用于读取、写入、转换和操作数据流。它是一个基于事件的 API,可以让我们以高效、低延迟的方式处理大型数据集。
说直白点就是基于Stream封装的API,性能更好。
比如读取文件,使用流我们可以一点一点来读取文件,每次只读取或写入文件的一小部分数据块,而不是一次性将整个文件读取或写入到内存中或磁盘中,这样做能够降低内存占用。
express.js 和 koa.js 的区别是什么?
- 中间件模型不同:express 的中间件模型为线型,而 koa 的为U型(洋葱模型)。
- 对异步的处理不同:express 通过回调函数处理异步,而 koa 通过generator 和 async/await 使用同步的写法来处理异步,后者更易维护,但彼时 Node.js 对 async 的兼容性和优化并不够好,所以没有流行起来。
- 功能不同:express 包含路由、渲染等特性,而 koa 只有 http 模块。
总得来说,express 功能多一点,写法烂一点,兼容性好一点,所以当时更流行。虽然现在 Node.js 已经对 await 支持得很好了,但是 koa 已经错过了风口。
不过 express 和 koa 的作者都是 TJ 大神。
工程化
说一说前端性能优化手段
webpack的构建流程
常见 loader 和 plugin 有哪些?二者的区别是什么?
常见 loader
在 webpack 文档里写了:
Loaders | webpack 你可以记住:
babel-loader
把 JS/TS 变成 JS
ts-loader
把 TS 变成 JS,并提示类型错误
markdown-loader
把 markdown 变成 html
html-loader
把 html 变成 JS 字符串
sass-loader
把 SASS/SCSS 变成 CSS
css-loader
把 CSS 变成 JS 字符串
style-loader
把 JS 字符串变成 style 标签
postcss-loader
把 CSS 变成更优化的 CSS
vue-loader
把单文件组件(SFC)变成 JS 模块
thread-loader
用于多进程打包
常见 plugin
也在 webpack 文档里写了:
Plugins | webpack 你可以记住这些:
html-webpack-plugin
用于创建 HTML 页面并自动引入 JS 和 CSS
clean-webpack-plugin
用于清理之前打包的残余文件
mini-css-extract-plugin
用于将 JS 中的 CSS 抽离成单独的 CSS 文件
SplitChunksPlugin
用于代码分包(Code Split)
-
DllPlugin
+DllReferencePlugin
用于避免大依赖被频繁重新打包,大幅降低打包时间webpack使用-详解DllPlugin 3.
eslint-webpack-plugin
用于检查代码中的错误
DefinePlugin
用于在 webpack config 里添加全局变量
copy-webpack-plugin
用于拷贝静态文件到 dist
二者的区别
-
loader 是文件加载器(这句废话很重要)
- 功能:能够对文件进行编译、优化、混淆(压缩)等,比如 babel-loader / vue-loader
- 运行时机:在创建最终产物之前运行
-
plugin 是 webpack 插件(这句废话也很重要)
- 功能:能实现更多功能,比如定义全局变量、Code Split、加速编译等
- 运行时机:在整个打包过程(以及前后)都能运行
webpack proxy工作原理?为什么能解决跨域
webpack proxy
,即webpack
提供的代理服务
基本行为就是接收客户端发送的请求后转发给其他服务器
其目的是为了便于开发者在开发模式下解决跨域问题(浏览器安全策略限制)
想要实现代理首先需要一个中间服务器,webpack
中提供服务器的工具为webpack-dev-server
跨域问题
关于配置方面,在webpack
配置对象属性中通过devServer
属性提供,如下:
module.exports = {
//...
devServer: {
proxy: {
'/api': {
target: 'http://xiedaimala.com',
changeOrigin: true,
},
},
},
};
devServetr
里面proxy
则是关于代理的配置,该属性为对象的形式,对象中每一个属性就是一个代理的规则匹配
属性的名称是需要被代理的请求路径前缀,一般为了辨别都会设置前缀为 /api
,值为对应的代理匹配规则,对应如下:
- target:表示的是代理到的目标地址
- pathRewrite:默认情况下,我们的 /api-hy 也会被写入到URL中,如果希望删除,可以使用pathRewrite
- secure:默认情况下不接收转发到https的服务器上,如果希望支持,可以设置为false
- changeOrigin:它表示是否更新代理后请求的 headers 中host地址
如何提高 webpack 构建速度?
- 使用 DllPlugin 将不常变化的代码提前打包,并复用,如 vue、react
- 使用 thread-loader 或 HappyPack(过时)进行多线程打包
- 处于开发环境时,在 webpack config 中将 cache 设为 true,也可用 cache-loader(过时)
- 处于生产环境时,关闭不必要的环节,比如可以关闭 source map
- 网传的 HardSourceWebpackPlugin 已经一年多没更新了,谨慎使用
vite
构建工具的高阶封装, 内部使用的其它的构建工具, 最核心的是 Rollup
, 与框架无关
- 开发时效率极高
- 开箱即用, 功能完备
- 社区丰富, 兼容 rollup
- 超高速热重载
- 预设应用和类库打包模式
- 前端类库无关
webpack 与 vite 的区别是什么?
vite 相比webpack的优缺点。 - 掘金 (juejin.cn)
-
开发环境区别
-
vite 自己实现 server,不对代码打包,充分利用浏览器对
<script type=module>
的支持- 假设 main.js 引入了 vue
- 该 server 会把
import { createApp } from 'vue'
改为import { createApp } from "/node_modules/.vite/vue.js"
这样浏览器就知道去哪里找 vue.js 了
-
webpack-dev-server 常使用 babel-loader 基于内存打包,比 vite 慢很多很多很多
- 该 server 会把 vue.js 的代码(递归地)打包进 main.js
-
-
生产环境区别
- vite 使用 *rollup *+ *esbuild *来打包 JS 代码
-
*webpack *使用 *babel *来打包 JS 代码,比 esbuild 慢很多很多很多
- webpack 能使用 esbuild 吗?可以,你要自己配置(很麻烦)。
-
文件处理时机
- vite 只会在你请求某个文件的时候处理该文件
- webpack 会提前打包好 main.js,等你请求的时候直接输出打包好的 JS 给你
目前已知 vite 的缺点有:
- 热更新常常失败,原因不清楚
- 有些功能 rollup 不支持,需要自己写 rollup 插件
- 不支持非现代浏览器
转载自:https://juejin.cn/post/7256393626681966629