From 8819a81f04d07c86f61ef0733d08883335197b4f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=92=8B=E7=BB=8D=E6=B4=8B?= <15001270+jiang_060105@user.noreply.gitee.com> Date: Mon, 30 Mar 2026 02:52:03 +0000 Subject: [PATCH] =?UTF-8?q?=E4=BA=A4=E4=BD=9C=E4=B8=9A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: 蒋绍洋 <15001270+jiang_060105@user.noreply.gitee.com> --- ...am\346\265\201\347\254\224\350\256\260.md" | 132 ++++++++++++++++++ ...13\344\273\266\345\276\252\347\216\257.md" | 121 ++++++++++++++++ ...04\347\220\206\347\254\224\350\256\260.md" | 118 ++++++++++++++++ ...17\351\241\271\347\254\224\350\256\260.md" | 119 ++++++++++++++++ 4 files changed, 490 insertions(+) create mode 100644 "\350\222\213\347\273\215\346\264\213/20260323Crypto\346\250\241\345\235\227\347\254\224\350\256\260\345\222\214stream\346\265\201\347\254\224\350\256\260.md" create mode 100644 "\350\222\213\347\273\215\346\264\213/20260325\344\272\213\344\273\266\345\276\252\347\216\257.md" create mode 100644 "\350\222\213\347\273\215\346\264\213/20260326\345\210\235\350\257\206Express\347\254\224\350\256\260\345\222\214\350\267\257\347\224\261\350\257\267\346\261\202\344\270\216\345\244\204\347\220\206\347\254\224\350\256\260.md" create mode 100644 "\350\222\213\347\273\215\346\264\213/20260327\345\205\263\344\272\216express\346\250\241\345\235\227\345\214\226\345\206\231CRUD\351\241\271\347\233\256\347\232\204\346\263\250\346\204\217\351\241\271\347\254\224\350\256\260.md" diff --git "a/\350\222\213\347\273\215\346\264\213/20260323Crypto\346\250\241\345\235\227\347\254\224\350\256\260\345\222\214stream\346\265\201\347\254\224\350\256\260.md" "b/\350\222\213\347\273\215\346\264\213/20260323Crypto\346\250\241\345\235\227\347\254\224\350\256\260\345\222\214stream\346\265\201\347\254\224\350\256\260.md" new file mode 100644 index 00000000..6348d9b7 --- /dev/null +++ "b/\350\222\213\347\273\215\346\264\213/20260323Crypto\346\250\241\345\235\227\347\254\224\350\256\260\345\222\214stream\346\265\201\347\254\224\350\256\260.md" @@ -0,0 +1,132 @@ +# Node.js Crypto 模块笔记 + +`crypto` 模块提供了加密功能,包括对 OpenSSL 的哈希、HMAC、加密、解密、签名和验证函数的封装。 + +### 1. 哈希算法 (Hash) +* **特点**:不可逆(只能从明文到密文,不能反向推导)。 +* **用途**:密码存储(通常加盐)、文件完整性校验。 +* **常用算法**:`sha256`, `md5`。 + +```javascript +const crypto = require('crypto'); + +const data = "hello world"; +const hash = crypto.createHash('sha256') // 选择算法 + .update(data) // 输入数据 + .digest('hex'); // 输出格式 (常用 hex 或 base64) + +console.log('SHA256 Hash:', hash); +``` + +### 2. HMAC (密钥哈希) +* **特点**:类似 Hash,但需要一个“密钥”。 +* **用途**:身份验证(如 JWT 的签名部分)。 + +```javascript +const secret = 'my-secret-key'; +const hmac = crypto.createHmac('sha256', secret) + .update('hello') + .digest('hex'); +console.log('HMAC:', hmac); +``` + +### 3. 对称加密 (AES) +* **特点**:加密和解密使用**同一个密钥**。 +* **核心步骤**:需要算法名、密钥(Key)、初始化向量(IV)。 + +```javascript +const algorithm = 'aes-256-cbc'; +const key = crypto.randomBytes(32); // 256位密钥 +const iv = crypto.randomBytes(16); // 128位初始化向量 + +// --- 加密 --- +const cipher = crypto.createCipheriv(algorithm, key, iv); +let encrypted = cipher.update('敏感数据', 'utf8', 'hex'); +encrypted += cipher.final('hex'); + +// --- 解密 --- +const decipher = crypto.createDecipheriv(algorithm, key, iv); +let decrypted = decipher.update(encrypted, 'hex', 'utf8'); +decrypted += decipher.final('utf8'); + +console.log('解密后:', decrypted); +``` + +--- + +# Node.js Stream (流) 笔记 + +`stream` 是处理大数据的核心工具。它将数据分成一小块一小块(chunks)处理,而不是一次性全部读入内存。 + +### 1. 为什么用流? +* **内存效率**:不需要为了读一个 10GB 的文件而占用 10GB 内存。 +* **时间效率**:接收到第一块数据就可以开始处理,不用等全部下载完。 + +### 2. 四种基本流类型 +1. **Readable (可读流)**:数据源(如 `fs.createReadStream`)。 +2. **Writable (可写流)**:数据目的地(如 `fs.createWriteStream`)。 +3. **Duplex (双工流)**:既可读又可写(如 TCP net.Socket)。 +4. **Transform (转换流)**:读入数据,修改后输出(如 `zlib` 压缩)。 + +### 3. 核心 API:`.pipe()` (管道) +管道是连接流最简单的方式,把上游的输出直接导向下游的输入。 + +```javascript +const fs = require('fs'); + +const src = fs.createReadStream('source.txt'); +const dest = fs.createWriteStream('dest.txt'); + +// 把读取的内容直接写入目标文件 +src.pipe(dest); +``` + +### 4. 事件监听 (手动控制) +如果你想在流的过程中做点别的,可以监听事件: + +```javascript +const rs = fs.createReadStream('bigfile.txt'); + +rs.on('data', (chunk) => { + console.log(`读取到 ${chunk.length} 字节数据`); +}); + +rs.on('end', () => { + console.log('读取完成'); +}); + +rs.on('error', (err) => { + console.error('发生错误:', err); +}); +``` + +--- + +# 进阶:Crypto + Stream 结合实战 + +**场景:给一个超大文件计算哈希值(或加密)** +如果文件很大,不能使用 `fs.readFileSync`,必须结合流。 + +```javascript +const crypto = require('crypto'); +const fs = require('fs'); + +const hash = crypto.createHash('sha256'); +const input = fs.createReadStream('very-large-video.mp4'); + +// 像水流一样流过哈希处理器 +input.on('data', (chunk) => { + hash.update(chunk); +}); + +input.on('end', () => { + console.log('文件哈希值为:', hash.digest('hex')); +}); + +// 或者更优雅的写法 (使用 Transform 流特性) +// input.pipe(hash).pipe(process.stdout); +``` + +### 总结口诀 +* **Crypto**: 摘要(Hash)不可逆,加解密找AES,签名验证保安全。 +* **Stream**: 大数据分块走,`pipe`连接真方便,省内存全靠它。 \ No newline at end of file diff --git "a/\350\222\213\347\273\215\346\264\213/20260325\344\272\213\344\273\266\345\276\252\347\216\257.md" "b/\350\222\213\347\273\215\346\264\213/20260325\344\272\213\344\273\266\345\276\252\347\216\257.md" new file mode 100644 index 00000000..87fc1d75 --- /dev/null +++ "b/\350\222\213\347\273\215\346\264\213/20260325\344\272\213\344\273\266\345\276\252\347\216\257.md" @@ -0,0 +1,121 @@ +# 简答题 +## 第一题:什么是事件循环?它在Node.js中起什么作用? +1. Node.js 是单线程,一次只能干一件事。事件循环就是一个死循环,不停检查:有没有要执行的异步任务?有就按顺序执行,没有就等着。 +2. 让 JS 不用干等网络请求、读文件这些慢操作 +一边等 I/O,一边能处理别的请求 +保证代码不卡死、能并发 +## 第二题:宏任务和微任务有什么区别?请举例说明。 +微任务:插队,全部跑完再走 + +宏任务:排队,一次只走一个 + +顺序:同步 > 微任务 > 宏任务 +## 第三题:请解释以下代码的执行顺序: +``` +console.log('1'); +setTimeout(() => console.log('2'), 0); +Promise.resolve().then(() => console.log('3')); +process.nextTick(() => console.log('4')); +console.log('5'); +``` + +15432 +## 第四题:什么是阻塞事件循环?它会对应用造成什么影响? +阻塞事件循环 = 主线程被同步耗时任务霸占,事件循环停摆 + +后果 = 服务不响应、异步任务全卡住、应用卡死 +## 第五题:如何避免阻塞事件循环? +不让主线程长时间被同步任务霸占, +CPU 密集用子线程,I/O 密集用异步 API。 + +# 操作题 +## 第一题:编写代码验证以下代码的执行顺序,并说明原因 +``` +console.log('A') + +setTimeout(() => { + console.log('B') +}, 0) + +Promise.resolve().then(() => { + console.log('C') +}) + +process.nextTick(() => { + console.log('D') +}) + +console.log('E') +``` +AEDCB +先跑同步代码:A、E + +再跑 nextTick(微任务优先级最高):D + +再跑 Promise.then(普通微任务):C + +最后跑 setTimeout(宏任务):B +## 第二题:创建一个程序,模拟阻塞事件循环的情况。 +``` +setInterval(() => { + console.log('定时器:', new Date().toLocaleTimeString()) +}, 1000) + +console.log('开始大计算,阻塞 3~5 秒...') + +// 同步大循环,阻塞事件循环 +for (let i = 0; i < 1000000000; i++) {} + +console.log('耗时同步任务结束,事件循环恢复') +``` +## 第三题:非阻塞优化:将阻塞代码改写为非阻塞形式 +``` +/*阻塞代码*/ +setInterval(() => { + console.log('定时器运行') +}, 1000) + +// 同步大循环,阻塞事件循环 +for (let i = 0; i < 1000000000; i++) {} +``` +``` +/*非阻塞代码*/ +setInterval(() => { + console.log('定时器运行') +}, 1000) + +let i = 0 +function run() { + i++ + if (i < 1000000000) { + setImmediate(run) + } +} + +run() +``` +## 第四题:综合测试:创建一个程序,综合运用setTimeout、Promise、nextTick,验证执行顺序 +``` +console.log('1: 同步开始'); + +setTimeout(() => { + console.log('2: setTimeout'); +}, 0); + +Promise.resolve().then(() => { + console.log('3: Promise.then'); +}); + +process.nextTick(() => { + console.log('4: nextTick'); +}); + +console.log('5: 同步结束'); +``` +顺序 + +1: 同步开始 +5: 同步结束 +4: nextTick +3: Promise.then +2: setTimeout \ No newline at end of file diff --git "a/\350\222\213\347\273\215\346\264\213/20260326\345\210\235\350\257\206Express\347\254\224\350\256\260\345\222\214\350\267\257\347\224\261\350\257\267\346\261\202\344\270\216\345\244\204\347\220\206\347\254\224\350\256\260.md" "b/\350\222\213\347\273\215\346\264\213/20260326\345\210\235\350\257\206Express\347\254\224\350\256\260\345\222\214\350\267\257\347\224\261\350\257\267\346\261\202\344\270\216\345\244\204\347\220\206\347\254\224\350\256\260.md" new file mode 100644 index 00000000..9e989454 --- /dev/null +++ "b/\350\222\213\347\273\215\346\264\213/20260326\345\210\235\350\257\206Express\347\254\224\350\256\260\345\222\214\350\267\257\347\224\261\350\257\267\346\261\202\344\270\216\345\244\204\347\220\206\347\254\224\350\256\260.md" @@ -0,0 +1,118 @@ +## 笔记 +``` +安装 +npm install express +导入 express +const express = require('express') + +restful 路由设计 +``` +| HTTP 方法 | 路径 | 对应操作 | 代码中的功能 | +| :--- | :--- | :--- | :--- | +| GET | `/users` | 查 (List) | 获取用户列表(包含分页逻辑) | +| POST | `/users` | 增 (Create) | 新增用户 | +| PUT | `/users/:id` | 改 (Update) | 修改指定 ID 的用户 | +| GET | `/users/:id` | 查 (Detail) | 获取指定 ID 的用户详情 | +| DELETE | `/users/:id` | 删 (Delete) | 删除指定 ID 的用户 | + + +``` +``` + +## 项目 +一、 +1.什么是RESTful API?它的设计原则是什么? +RESTful API 是一种基于 HTTP 协议的 API 设计规范或架构风格,而非强制标准。它的核心思想是将服务器上的所有数据都视为“资源”,客户端通过统一的接口对这些资源进行操作。 +设计原则:资源,统一接口,无状态,客户端-服务器分离,可缓存。 + +2.req对象和res对象分别代表什么?它们有哪些常用属性和方法? +req 和 res 是两个核心对象,分别代表 HTTP 请求和 HTTP 响应。 +req对象:代表客户端的HTTP请求,包含请求信息。 +常用属性: + req.params:获取路径参数(如/users/:id中的id)。 + req.query:获取查询参数(如?q=keyword中的q)。 + req.body:获取请求体数据(需配合express.json()中间件)。 + req.method:获取HTTP方法(如GET、POST)。 +res对象:代表服务器的HTTP响应,用于返回数据。 +常用方法: + res.status(code):设置HTTP状态码(如200、404)。 + res.json(data):发送JSON格式响应。 + res.send(data):发送文本、对象等响应。 + res.setHeader(name, value):设置响应头。 + +3.Express中使用哪个方法启动服务器?它接收哪些参数? +使用app.listen()方法启动服务器。 +端口号:数字类型,如3000。 +主机名(可选):字符串类型,如'localhost'。 +回调函数(可选):服务器启动后执行的函数,常用于打印日志。 + +4.前后端分离的开发模式有什么优点? +职责分离,技术独立,易于扩展,部署灵活 + +5.为什么学习Express而不是直接使用原生Node.js? +简洁高效的路由,强大的中间件机制,简化请求和响应处理,生态丰富 + +二、 +1.个人主页:创建一个Express应用,包含首页、个人简介、作品集三个页面。 +入口文件 (app.js) +``` +const express = require('express'); +const path = require('path'); +const app = express(); +const PORT = 3000; + +app.set('view engine', 'ejs'); +app.set('views', path.join(__dirname, 'views')); + +app.use('/', require('./routes/index')); +app.use('/about', require('./routes/about')); +app.use('/portfolio', require('./routes/portfolio')); + +app.listen(PORT, () => { + console.log(`服务器正在运行: http://localhost:${PORT}`); +}); +``` +首页路由 (routes/index.js) +``` +const express = require('express'); +const router = express.Router(); + +router.get('/', (req, res) => { + res.render('index', { title: '首页' }); +}); + +module.exports = router; +``` +个人简介路由 (routes/about.js) +``` +const express = require('express'); +const router = express.Router(); + +router.get('/', (req, res) => { + + const userData = { + name: '张三', + role: '全栈开发工程师', + bio: '热爱编程,专注于 Node.js 和前端技术。' + }; + res.render('about', { title: '个人简介', user: userData }); +}); + +module.exports = router; +``` +作品集路由 (routes/portfolio.js) +``` +const express = require('express'); +const router = express.Router(); + +router.get('/', (req, res) => { + + const projects = [ + { name: '电商后台管理系统', tech: 'Vue + Express' }, + { name: '个人博客', tech: 'Hexo' }, + { name: '即时聊天应用', tech: 'Socket.io' } + ]; + res.render('portfolio', { title: '作品集', projects: projects }); +}); + +module.exports = router; \ No newline at end of file diff --git "a/\350\222\213\347\273\215\346\264\213/20260327\345\205\263\344\272\216express\346\250\241\345\235\227\345\214\226\345\206\231CRUD\351\241\271\347\233\256\347\232\204\346\263\250\346\204\217\351\241\271\347\254\224\350\256\260.md" "b/\350\222\213\347\273\215\346\264\213/20260327\345\205\263\344\272\216express\346\250\241\345\235\227\345\214\226\345\206\231CRUD\351\241\271\347\233\256\347\232\204\346\263\250\346\204\217\351\241\271\347\254\224\350\256\260.md" new file mode 100644 index 00000000..a0b58281 --- /dev/null +++ "b/\350\222\213\347\273\215\346\264\213/20260327\345\205\263\344\272\216express\346\250\241\345\235\227\345\214\226\345\206\231CRUD\351\241\271\347\233\256\347\232\204\346\263\250\346\204\217\351\241\271\347\254\224\350\256\260.md" @@ -0,0 +1,119 @@ +# 笔记 + +### 1. 推荐的目录结构 +不要把所有代码都写在 `app.js` 里。建议采用以下结构: + +```text +my-app/ +├── node_modules/ +├── routes/ # 路由定义 (URL 映射) +│ └── users.js +├── controllers/ # 业务逻辑 (处理具体的 CRUD 操作) +│ └── userController.js +├── models/ # 数据模型 (数据库操作) +│ └── userModel.js +├── app.js # 入口文件 (配置中间件、监听端口) +└── package.json +``` + +--- + +### 2. 使用 `express.Router()` 实现路由模块化 +这是模块化的核心。 + +**步骤 A:在 `routes/users.js` 中定义路由** +```javascript +const express = require('express'); +const router = express.Router(); +// 导入控制器逻辑 (见下文) +const userController = require('../controllers/userController'); + +// 这里的路径是相对路径,相对于挂载点 +router.get('/', userController.getAllUsers); // 查询所有 +router.get('/:id', userController.getUserById); // 查询单个 +router.post('/', userController.createUser); // 新增 +router.put('/:id', userController.updateUser); // 修改 +router.delete('/:id', userController.deleteUser); // 删除 + +module.exports = router; +``` + +**步骤 B:在 `app.js` 中挂载路由** +```javascript +const express = require('express'); +const userRouter = require('./routes/users'); +const app = express(); + +app.use(express.json()); // 必须:解析 POST 的 JSON 数据 + +// 挂载路由:为所有用户相关的路由添加前缀 /api/users +app.use('/api/users', userRouter); + +app.listen(3000); +``` + +--- + +### 3. CRUD 开发注意项 (重点) + +#### **C - Create (新增)** +* **注意项**:必须配置 `app.use(express.json())` 否则 `req.body` 为 `undefined`。 +* **状态码**:创建成功建议返回 `201 Created`。 + +#### **R - Read (查询)** +* **查询全部**:通常返回数组。 +* **查询单个**:通过 `req.params.id` 获取 ID。如果找不到数据,务必返回 `404`,不要返回空对象。 + +#### **U - Update (修改)** +* **注意项**:修改通常需要 `id` (在 `params` 中) 和新数据 (在 `body` 中)。 +* **方法选择**:`PUT` 通常用于替换整个资源,`PATCH` 用于局部更新。 + +#### **D - Delete (删除)** +* **注意项**:删除是危险操作。 +* **状态码**:删除成功可返回 `204 No Content`(无返回内容)或 `200`(返回被删除的对象)。 + +--- + +### 4. 统一响应格式与状态码 +在模块化开发中,建议统一返回的 JSON 格式,方便前端处理。 + +* **常用状态码表**: + * `200`: OK (成功) + * `201`: Created (新增成功) + * `400`: Bad Request (客户端参数错误) + * `401`: Unauthorized (未登录/权限不足) + * `404`: Not Found (资源不存在) + * `500`: Internal Server Error (服务器内部崩溃) + +--- + +### 5. 错误处理中间件 +在模块化项目中,不要在每个控制器的 `catch` 块里写 `res.send('error')`,应统一定义错误处理器。 + +**在 `app.js` 最后面添加:** +```javascript +// 全局错误处理器 +app.use((err, req, res, next) => { + console.error(err.stack); + res.status(500).json({ + status: 'fail', + message: err.message || '服务器内部错误' + }); +}); +``` + +--- + +### 6. 参数校验 (Validation) +在数据进入 `Controller` 之前,应该进行校验。 +* **基础版**:手动 `if (!req.body.name) { ... }`。 +* **进阶版**:使用第三方库如 `joi` 或 `express-validator` 专门编写校验中间件。 + +--- + +### 总结口诀 +> **Router** 只管路怎么走, +> **Controller** 只管事怎么做, +> **Model** 只管数据怎么拿。 +> `app.use` 挂前缀,`express.json` 别忘掉。 +> 状态码要给对,错误处理要统一。 \ No newline at end of file -- Gitee