深入 Node.js 技术栈 学习笔记

date
Jul 9, 2023
slug
learn-nodejs
status
Published
tags
Node.js
summary
type
Post

exports 和 module.exports

  • 默认情况下,exportsmodule.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 优先,执行任何一个宏任务之前,先执行完微任务队列中的
notion image
notion image
两道面试题讲解:8.Buffer和浏览器的时间循环 2:26:28

Node 中的事件循环

  • Node 与浏览器的差异
notion image
notion image
宏任务:setTimeout、setInterval、IO事件、setImmediate、close事件 微任务:Promise.then、process.nextTick(单独的一个队列)、queueMicrotask
一次事件循环称为一次 tick
notion image
面试题讲解: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();
}

登录凭证

Cookie + Session (Deprecated)

JWT Token 前后端分离

 

© e 2021 - 2023 site logo