深入 Node.js 技术栈 学习笔记
date
Jul 9, 2023
slug
learn-nodejs
status
Published
tags
Node.js
summary
type
Post
exports 和 module.exportspath.join 和 path.resolvewebpack alias使用 npx 执行局部命令浏览器中的事件循环:事件队列、宏任务微任务Node 中的事件循环项目开发习惯路由与中间件错误处理密码处理登录凭证Cookie + Session (Deprecated)JWT Token 前后端分离
exports 和 module.exports
- 默认情况下,
exports
是module.exports
的引用
- 直接对 exports 赋值会断开两者的关联
- 下面两种用法的效果相同
exports.obj = { foo: 'bar' };
exports.myFunc = () => {};
module.exports = {
obj: { foo: 'bar' },
muFunc: () => {}
}
path.join 和 path.resolve
join 简单的路径拼接
resolve 以
/
为根目录,若传入相对路径,会自动在前面加上当前目录形成绝对路径// 当前路径为
/Users/xiao/work/test
path.join('a', 'b', '..', 'd');
// a/d
path.resolve('a', 'b', '..', 'd');
// /Users/xiao/work/test/a/d
webpack alias
为 import 和 export 创建别名
const resolve = dir => path.resolve(__dirname, dir);
module.exports = {
...
webpack: {
alias: {
"@": resolve("src"),
"components": resolve("src/components")
}
}
}
使用 npx 执行局部命令
全局安装webpack 5.1.3 项目安装webpack 3.6.0
webpack --version
// 5.1.3如何执行项目文件夹内的webpack?
npx webpack --version
原理:到当前目录的node_modules/.bin
查找对应命令浏览器中的事件循环:事件队列、宏任务微任务
做题前先画个图
宏任务:ajax、setTimeout、setInterval、DOM监听、UI Rendering
微任务:Promise.then、Mutation Observer API、queueMicrotask()
async 和 await 如何处理?
将 await 执行的代码,看作包裹在
(resolve, reject) => {}
中的代码
将 await 后的下一条语句,看作 then(res => {})
中的代码执行顺序:script 优先,执行任何一个宏任务之前,先执行完微任务队列中的


两道面试题讲解:8.Buffer和浏览器的时间循环 2:26:28
Node 中的事件循环
- Node 与浏览器的差异


宏任务:setTimeout、setInterval、IO事件、setImmediate、close事件
微任务:Promise.then、process.nextTick(单独的一个队列)、queueMicrotask
一次事件循环称为一次 tick

面试题讲解:1:00:13
项目开发习惯
路由与中间件
- 目录结构
src/
controller/
user.controller.js
router/
user.router.js
service/
user.service.js
middleware/
user.middleware.js
...
user.router.js
只负责注册接口
const Router = require('koa-router');
const {
create
} = require('../controller/user.controller.js'); // ===> 抽取到单独文件 controller
const {
verifyUser
} = require('../middleware/user.middleware.js'); // ===> 抽取到单独文件 middleware
const userRouter = new Router({ prefix: "/users" });
// userRouter.post('/', 具体的处理逻辑);
userRouter.post('/create', verifyUser, create);
module.exports = userRouter;
user.controller.js
编写具体的处理逻辑,可以编写类,new
一个来导出
const service = require('../service/user.service.js');
class UserController {
async create(ctx, next) {
// 获取用户请求传递的参数
...
// 查询数据 ===> 抽取到单独文件 service
const result = await service.create(user);
// 返回数据
ctx.body = result;
// ...
}
}
module.exports = new UserController();
user.service.js
中间操作太长,抽离出来
class UserService {
async create(user) {
// 将 user 存储到数据库中
return "创建成功";
}
}
module.exports = new UserService();
user.middleware.js
检查前置条件,类似 solidity 的 modifier
const verifyUser = async (ctx, next) => {
// 获取用户名和密码
const { name, password } = ctx.request.body;
// 判断非空,没被注册过,等等
await next();
}
module.exports = {
verifyUser,
};
错误处理
- 目录结构
src/
constants/
error-types.js
app/
index.js
error-handle.js
- 错误常量
error-types.js
const NAME_OR_PASSWORD_IS_REQUIRED = 'name_or_password_is_required';
module.exports = {
NAME_OR_PASSWORD_IS_REQUIRED
};
假如前面的
verifyUser
报错:const errorType = require('../constants/error-types');
const verifyUser = async (ctx, next) => {
// 获取用户名和密码
const { name, password } = ctx.request.body;
// 判断非空,没被注册过,等等
if(!name || !password || name === '' || password === '') {
const error = new Error(errorType.NAME_OR_PASSWORD_IS_REQUIRED);
return ctx.app.emit('error', error, ctx); // 返回错误
}
await next();
}
- error-handle.js
const errorType = require('../constants/error-types');
const errorHandler = (error, ctx) => {
let status, message;
switch(error.message) { // 根据错误类型进行处理
case errorType.NAME_OR_PASSWORD_IS_REQUIRED:
status = 400; // Bad Request
message = "用户名和密码不能为空";
break;
default: // 默认错误
status = 404;
message = "默认错误";
}
}
module.exports = errorHandler;
- index.js;
// const Koa = require('koa');
// const bodyParser = require('koa-bodyparser');
// const userRouter = require('../router/user.router');
const errorHandler = require('./error-handler.js');
const app = new Koa();
// app.use(bodyParser());
// app.use(userRouter.routes());
// app.use(userRouter.allowedMethods());
app.on('error', errorHandler);
密码处理
utils/password-handle.js
const crypto = require('crypto');
const md5password = (password) => {
const md5 = crypto.createHash('md5'); // 创建 Hash 类
const result = md5.update(password) // 计算生成的哈希
.digest('hex'); // 若留空,默认返回 Buffer
}
module.exports = md5password;
middleware/user.middleware.js
const handlePassword = async (ctx, next) => {
const { password } = ctx.request.body;
ctx.request.body.password = md5password(password);
await next();
}