深海 深海
首页
  • 随手笔录
  • 电影
  • 音乐
  • 书籍
汇总
面试
  • 开发工具
  • VScode插件
  • Git
  • Mac时代
  • 前端工具
  • Chrome
  • HTML
  • CSS
  • Javascript
  • Typescript
  • Axios
  • 框架

    • Vue
    • uni-app
  • Nginx
  • Linuk
事例
关于

深海

人生如逆旅,我亦是行人
首页
  • 随手笔录
  • 电影
  • 音乐
  • 书籍
汇总
面试
  • 开发工具
  • VScode插件
  • Git
  • Mac时代
  • 前端工具
  • Chrome
  • HTML
  • CSS
  • Javascript
  • Typescript
  • Axios
  • 框架

    • Vue
    • uni-app
  • Nginx
  • Linuk
事例
关于
  • 开发工具
  • Vscode插件
  • Git
  • Mac时代
  • 前端工具

    • Node.js
      • Node 版本管理工具
      • 1.安装
      • 2.模块
        • fs 文件模块
        • path 路径模块
        • http 模块
        • 模块化
        • module 对象
        • module.exports 对象
        • export 对象
        • 模块的加载机制
        • 内置模块
        • 自定义模块
        • 第三方模块
        • 以目录作为模块
      • 3.npm
        • 常用命令
        • 包的语义化版本规范
        • 包管理配置文件
        • nrm 管理 npm 镜像源
        • 实用的 npm 包
        • 构建 npm 包
      • 4.Express
        • 托管静态文件
        • 路由
        • JSONP 请求
        • 中间件
      • 5.身份认证
        • Session 认证
        • 工作流程
        • 缺点
        • 使用 session
        • JWT 认证
        • JWT 的组成
        • 工作流程
        • 优点
        • 使用 JWT
    • webpack
  • 效率工具

  • Chrome
  • 工具
  • 前端工具
深海
2023-07-10
目录

Node.js

Node.js

Node.js 是一个开源的、跨平台的 JavaScript 运行时环境。

# Node 版本管理工具

工具 适用场景 跨平台 速度 自动切换 管理其他语言
nvm 通用方案 ✅ (macOS/Linux) 中等 ❌ ❌
fnm 更快替代 nvm ✅ (macOS/Linux) ⚡ 快 ✅ (支持 .nvmrc) ❌
n 极简版 ❌ (仅 macOS/Linux) 快 ❌ ❌
Volta 自动切换 ✅ 中等 ✅ ❌
asdf 多语言管理 ✅ 中等 ✅ ✅
  • nvm 安装
brew install nvm
1
  • 常用命令
    命令 功能 命令 功能
    nvm install <version> 安装指定版本 nvm uninstall <version> 卸载指定版本
    nvm use <version> 切换到指定版本 nvm alias default <version> 设置默认版本
    nvm use system 使用系统安装的 Node nvm ls 已安装的所有版本
    nvm ls-remote 列出所有可安装的远程版本 nvm ls-remote --lts 列出所有可安装的 LTS 版本
    nvm current 当前使用的 Node.js 版本 nvm which <version> 显示某个版本的 Node.js 安装路径

# 1.安装

Node.js (opens new window) LTS 版本和 Current 版本

  • LTS 为长期版本,有稳定性
  • Current 为新特性尝鲜版
node -v  # 安装后查看node版本
1

# 2.模块

# fs 文件模块

const fs = require("fs");
1
  • 读取文件中的内容
/**
 * 参数1(必选)  文件的路径
 * 参数2(可选)  编码格式 默认指定 utf-8
 * 参数3(必选)  回调参数接受两个参数 (err,dataStr)
*/
fs.readFile(file[,options],callback)
1
2
3
4
5
6
  • 向指定文件写入内容
/**
 * 参数1(必选)  文件的路径
 * 参数2(必选)  表示要写入的内容
 * 参数3(可选)  编码格式 默认指定 utf-8
 * 参数3(必选)  回调参数 err
*/
fs.writeFile(file,data[,options],callback)
1
2
3
4
5
6
7

# path 路径模块

path 模块是 Node.js 官方提供的,用来处理路径模块的处理

const path = require('path')
path.join([...paths])     // 将多个路径片段拼成一个完整的路径字符串
path.basename(path[,ext]) // 获取路径中的最后一部分
/**
 * path 路径名(必选)
 * ext  文件扩展名(可选)
 */
1
2
3
4
5
6
7
  • html 文件分离
const fs = require("fs");
const path = require("path");
const regStyle = /<style>[\s\S]*<\/style>/;
const regScript = /<script>[\s\S]*<\/script>/;

fs.readFile(path.join(__dirname, "/index.html"), "utf-8", (err, dataStr) => {
  if (err) return console.log("读取文件失败" + err.message);
  resolveCSS(dataStr);
  resolveJS(dataStr);
  resolveHTML(dataStr);
});

function resolveCSS(htmlStr) {
  const r1 = regStyle.exec(htmlStr);
  const newStr = r1[0].replace("<style>", " ").replace("</style>", " ");

  fs.writeFile(path.join(__dirname, "/clock/index.css"), newStr, (err) => {
    if (err) return console.log("css写入失败" + err.message);
  });
  console.log("css写入成功!!!");
}
function resolveJS(htmlStr) {
  const r1 = regScript.exec(htmlStr);
  const newStr = r1[0].replace("<script>", " ").replace("</script>", " ");

  fs.writeFile(path.join(__dirname, "/clock/index.js"), newStr, (err) => {
    if (err) return console.log("js写入失败" + err.message);
  });
  console.log("js写入成功!!!");
}
function resolveHTML(htmlStr) {
  console.log(regStyle);
  const newHtml = htmlStr
    .replace(regStyle, '<link rel="stylesheet" href="./index.css"></link>')
    .replace(regScript, '<script src="./index.js"></script>');
  console.log(newHtml);
  fs.writeFile(path.join(__dirname, "/clock/index.html"), newHtml, (err) => {
    if (err) return console.log("html写入失败" + err.message);
  });
  console.log("html写入成功!!!");
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41

# http 模块

// 导入http模块
const http = require("http");
const path = require("path");
const fs = require("fs");
// 创建web服务器示例
const server = http.createServer();
server.on("request", (req, res) => {
  /**
   * req 请求对象
   * res 响应对象
   */
  const url = req.url;
  const fpath = path.join(__dirname + url);
  fs.readFile(fpath, "utf-8", (err, dataStr) => {
    if (err) return res.end("404 not find");
    // 向客户端发送指定内容,并结束这次请求
    res.end(dataStr);
  });
});
server.listen(8089, () => {
  console.log("server runing at http://127.0.0.1:8089");
});
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

# 模块化

模块化是指解决一个复杂问题时,自顶向下逐层把系统分成若干模块的过程。

  • 内置模块(是由 httNode.js 提供的模块,如 path、fs、http 等)
  • 自定义模块(用户自己写的模块)
  • 第三方模块(第三方开发出来的模块)

# module 对象

每一个自定义的 js 中都有一个 module 对象,里面存储了和当前模式有关的信息。

# module.exports 对象

  • 使用 require()方法导入模块的时候,就是导入 module.exports 所指向的对象。
  • 使用 module.exports 对象将莫模块内的成员共享出去,供外界使用。
module.exports = {
  userName: "jack",
};
1
2
3

# export 对象

默认情况下,exports 和 module.exports 指向同一个对象。require()模块时,得到的永远是 module.exports 指向的对象。

exports.userName = "jack";
1

# 模块的加载机制

# 内置模块

内置模块是由 Node.js 提供的,他的加载优先级最高。如果 node_modules 中有 fs 模块,require('fs')引用的还是 Node.js 中内置的 fs 模块。

# 自定义模块

加载自定义模块时,必须指定以./或../开头的路径标识符

Node 加载规则

  1. 按照确切的文件名进行加载
  2. 补全.js扩展名进行加载
  3. 补全.json扩展名进行加载
  4. 补全.node扩展名进行加载
  5. 加载失败,终端报错 :::

# 第三方模块

从当前父目录开始,从node_modules文件中加载第三方模块,如果没有找到对应的第三方模块,则移动到上一层父目录中,进行加载,直到文件的根目录。

# 以目录作为模块

  1. 被加载的目录下查找package.json文件,寻找 main 属性,作为 require()的入口。
  2. 如果目录中没有package.json文件或者 main 入口不存在或无法解析,则 Node.js 会加载目录下 index.js 文件。
  3. 若上面都查找失败,则终端报错Error: Cannot find module './test'

# 3.npm

Node.js 中第三方模块就是包

# 常用命令

npm -v                 # npm 版本
npm i 包名 -g           # 安装全局依赖包
npm uninstall 包名 -g   # 卸载全局依赖包
npm root -g            # 查看全局安装npm包的路径
npm view 包名 versions  # 查看该包所有的版本
npm i 包名@2.2.0        # 安装指定版本的包
npm install 包名        # 安装依赖
npm uninstall 包名       # 卸载依赖
npm i 包名 --save-dev   # 安装依赖到开发环境 简写 npm i 包名 -D
1
2
3
4
5
6
7
8
9

# 包的语义化版本规范

Static Badge

包的版本号以"点分十进制"形式来定义的。版本号提升规则(前面的版本号增加了,后面的版本号归零)

  1. 第 1 位数字 大版本
  2. 第 2 位数字 功能版本
  3. 第 3 位数字 Bug 修复版本

# 包管理配置文件

  1. 创建 package.json

    npm init -y

  2. 文件配置

{
  "name": "init", // 名称
  "version": "1.0.0", // 版本
  "description": "", // 项目描述
  "main": "index.js", // npm包项目的入口文件
  "keywords": [], // 搜索的关键词
  "scripts": {
    // 启动脚本
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "", // 作者
  "license": "ISC", // 开源协议
  "dependencies": {}, // 项目依赖(生产)
  "devDependencies": {} // 项目依赖(开发)
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

# nrm 管理 npm 镜像源

npm i nrm -g    # 全局安装nrm
nrm ls          # 查看所有的镜像源
nrm use taobao  # 切换到淘宝镜像源
1
2
3
  • 直接修改为淘宝镜像源(建议使用 nrm)
# 查看当前的下包镜像源
npm config get registry
# 将npm镜像源切换到淘宝镜像源
npm config set registry https://registry.npm.taobao.org/
1
2
3
4

# 实用的 npm 包

  • i5ting_toc

    把 md 文档转换成 html 页面的小工具

# 构建 npm 包

  1. 包结构
  • 包必须以单独的目录而存在
  • 包的顶级目录必须包含package.json文件
  • package.json中必须包含 name(包名)、version(版本号)、main(入口文件)三个属性
  1. 包文件
  • 使用 npm init -y 创建package.json文件
  • 新建入口 index.js,package.json中配置"main": "index.js"
  • 新建README.md文件,用于包描述
  1. 发布包
  • 注册 npm 账户,在终端执行npm login命令,输入用户名、密码、邮箱、邮箱验证吗

    运行npm login之前,需要先把下载包的服务器切换为 npm 官方服务器,否则会导致发布包失败。

  • 将终端切到根目录上,运行npm publish命令,即可将包发布到 npm 上。
  1. 删除包
  • npm unpublish 包名 --fore 删除 npm 上已发布的包

注意

  1. npm unpublish命令只能删除 72 小时以内发布的包
  2. npm unpublish删除的包,在 24 小时内不允许重复发布

# 4.Express

高度包容、快速而极简的 Node.js Web 框架 Express (opens new window)

// 导入express
const express = require("express");
const app = express();
// get请求
/**
 * req.params 匹配url的动态参数  /user/:id/:name  可以跟多个,但必须设置几个后面写几个
 * req.query  匹配url的查询参数  /user?age=18&name=jack
 * req.body   post请求的主体参数
 */
app.get("/user/:id/:name", function (req, res) {
  console.log(req.params);
  res.send("hello world");
});
// post 请求
app.post("/submit", function (req, res) {
  res.send("hello world");
});
app.listen(88, () => {
  console.log("http://127.0.0.1:88");
});
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

# 托管静态文件

app.use(express.static('public'));
app.use(express.static('files'));
// 访问位于 public 目录中的文件 (也可以托管多个静态资源)
http://localhost:3000/images/kitten.jpg
http://localhost:3000/css/style.css
http://localhost:3000/js/app.js

// 添加访问路径前缀
app.use('/public',express.static('../clock'))
1
2
3
4
5
6
7
8
9

# 路由

路由表示应用程序端点 (URI) 的定义以及端点响应客户机请求的方式。

  • router.js
const express = require("express");
// 创建路由对象
const router = express.Router();
router.get("/user", (req, res) => {
  res.send("user");
});
module.exports = router;
1
2
3
4
5
6
7
  • app.js
const express = require("express");
const router = require("./router");
const app = express();
// 挂载路由(添加前缀)
app.use("/api", router);
app.listen(88, () => {
  console.log("http://127.0.0.1:88");
});
1
2
3
4
5
6
7
8

# JSONP 请求

浏览其通过<script>标签的 src 属性,请求服务器上的数据,服务器返回一个函数的调用

特点

  • JSONP 不是一个 Ajax 请求,因为他没有 XMLHttpRequest 这个对象。
  • JSONP 仅支持 GET 请求,不支持 POST、DELETE、PUT 请求。
  • app.js
const express = require("express");
const cors = require("cors");
const app = express();
app.get("/api/jsonp", (req, res) => {
  // 获取客户端发送过来的回调函数名
  const funName = req.query.callback;
  const data = { age: 17, name: "jack" };
  // 拼接函数调用字符串
  const str = `${funName}(${JSON.stringify(data)})`;
  res.send(str);
});
// 如果已经配置cors了,jsonp请求必须在配置cors中间件之前声明
app.use(cors());
app.listen(8089, () => {
  console.log("server http://127.0.0.1:8089");
});
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
  • 页面调用
$("#btnJSON").on("click", function () {
  $.ajax({
    type: "delete",
    url: "http://127.0.0.1:8089/api/jsonp",
    dataType: "jsonp",
    success: (res) => {
      console.log(res);
    },
  });
});
1
2
3
4
5
6
7
8
9
10

# 中间件

中间件函数能够访问请求对象 (req)、响应对象 (res) 以及应用程序的请求/响应循环中的下一个中间件函数。下一个中间件函数通常由名为 next 的变量来表示。

注意事项

  • 在路由之前注册中间件

  • 客户端发送过来的请求,可以连续调用多个中间件进行处理

  • 最后需要调用next()函数

  • 调用多个中间件,多个中间件共享req和res :::

  • 全局中间件(应用级别中间件)

    通过app.use()或app.get()或app.post()绑定到 app 实例上的中间件

// 全局中间件
app.use((req, res, next) => {
  next();
});
const vm = function (req, res, next) {
  next();
};
app.get("/user", vm, (req, res) => {});
// 调用多个中间件写法
app.get("/user", vm1, vm2, (req, res) => {});
app.get("/user", [vm1, vm2], (req, res) => {});
1
2
3
4
5
6
7
8
9
10
11
  • 路由级别中间件

    绑定到express.Router()实例上的中间件

const express = require("express");
const router = express.Router();
router.use(function (req, res, next) {
  next();
});
app.use("/api", router);
1
2
3
4
5
6
  • 错误级别的中间件

    捕获项目中发生的异常错误

    警告

    错误中间件必须注册在路由之后,否则不会生效

const express = require("express");
const app = express();
app.get("/user", (req, res) => {
  throw new Error("发生错误");
  res.send("user");
});
app.use((err, req, res, next) => {
  // 捕获token失效
  if ((err.name = "UnauthorizedError")) {
    return res.send("token失效");
  }
  res.send("错误" + err.message);
});
app.listen(88, () => {
  console.log("http://127.0.0.1:88");
});
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
  • 内置中间件
  1. express.static() 托管静态资源中间件
  2. express.json() 4.16.0+ 解析 JSON 格式的请求体数据
  3. express.urlencoded()4.16.0+ 解析 URL-encdoded 格式的请求体数据
const express = require("express");
const app = express();
// 如果不配置express.json() req.body则默认undefined
app.use(express.json());
// 传urlencoded参数的话,如果已配置express.json()则 req.body 为 {}
app.use(express.urlencoded({ extended: false }));
1
2
3
4
5
6
  • 第三方中间件

    第三方开发出来的中间件如body-parser

# 5.身份认证

身份认证是一种通过验证请求发送者身份的过程。它通常被用来确保只有授权用户可以访问受限资源或执行操作。

# Session 认证

通过在服务器端存储用户的会话信息来记录和验证用户的身份。在 Session 认证中,用户在登录时提供用户名和密码,然后服务器会验证这些凭证,如果验证成功,就为该用户创建一个唯一的 Session ID,并将其存储到服务器端的内存或数据库中。该 Session ID 通常会在每个请求中被包含在一个称为 Cookie 的 HTTP 请求头中。

  • Cookie 是什么呢?

    Cookie是指浏览器端(客户端)存储在用户电脑上的小数据文件,通常是由网站服务器发送给浏览器,用于存储和检索用户的信息,如网站的登录状态、购物车信息等。下面是Cookie的几大特征

  1. 自动发送
  2. 域名独立
  3. 过期时限
  4. 4KB 大小限制

# 工作流程

  1. 在浏览器发送 HTTP 请求访问网站之前,网站服务器会在 HTTP 响应头中添加 Set-Cookie 字段,其中包含了要发送到浏览器中的 Cookie 内容。
  2. 浏览器收到响应头中的 Set-Cookie 字段后,将该 Cookie 保存到本地文件中(路径在浏览器的特定目录中),以备下次访问该网站时使用。
  3. 当浏览器下一次访问该网站时,将会在请求头的 Cookie 字段中带上保存的 Cookie 信息。
  4. 服务器收到 Cookie 后,会解析其中的内容并处理相关业务逻辑,然后将响应返回给浏览器。

# 缺点

Cookie 是明文传输的,因此敏感信息(如用户名和密码)不应该保存在 Cookie 中。此外,如果一个网站的 Cookie 过多或者太大,将会占用用户过多的磁盘空间和内存,因此最好定期清除过期或不必要的 Cookie

# 使用 session

  • 安装
npm i express-session
1
  • 配置
const express = require("express");
const session = require("express-session");
const app = express();
app.use(
  session({
    secret: "dong", // 任意字符串
    resave: false, // 固定写法
    saveUninitialized: true, // 固定写法
  })
);
app.listen(8089, () => {
  console.log("server http://127.0.0.1:8089");
});
1
2
3
4
5
6
7
8
9
10
11
12
13

说明

当配置成功后,可通过req.session来访问和使用 session 对象。

  • 注册中间件
const express = require("express");
const session = require("express-session");
const app = express();
app.use(
  session({
    secret: "dongkj", // 任意字符串
    resave: false, // 固定写法
    saveUninitialized: true, // 固定写法
    cookie: {
      maxAge: 24 * 60 * 60 * 1000, // 24 hours
    },
  })
);
app.listen(8089, () => {
  console.log("server http://127.0.0.1:8089");
});
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
  • 使用
// 存取数据
req.session.user = {};
// 清空session
req.session.destroy();
1
2
3
4

# JWT 认证

JWT(JSON Web Token)是一种开放标准(RFC 7519),用于在各方之间作为 JSON 对象安全地传输信息。JWT 通常用于身份验证和授权,是一种无状态的认证机制。

# JWT 的组成

  1. Header(头部):包含了 token 的元数据,例如加密算法等。
  2. Payload(有效荷载):包含了对于身份验证和授权所需的具体信息,例如用户 ID、角色等。
  3. Signature(签名):将第一部分和第二部分组合在一起并加密得到的结果,用于校验 token 的完整性以及认证 token 是否有效。

# 工作流程

  1. 用户提供用户名和密码进行身份验证。
  2. 服务器验证成功后,生成一个 JSON Web Token 并将其返回给客户端。
  3. 客户端存储该 token,并在每次请求中将其添加到 Authorization Header 中进行访问。
  4. 服务器验证 token 的有效性并返回响应或者拒绝响应。

# 优点

使用 JWT 的好处是可以实现无状态和分布式的系统,由于 token 存储在客户端,服务器端可以无需存储用户的信息,从而降低了服务器端的存储压力。此外,由于 JWT 使用了加密技术,可以保证 token 的安全性,防止 token 被篡改或伪造

# 使用 JWT

  • 安装
# jsonwebtoken  用于生成JWT字符串
# express-jwt   用于将JWT字符串还原成json对象
npm i express-jwt jsonwebtoken
1
2
3

注意

一定要在路由之前配置解析 Token 中间件
配置成功后可以调用req.user获取到用户信息,最新版本改为 req.auth

  • 使用
const express = require("express");
const session = require("express-session");
const cors = require("cors");
const app = express();
// 1.导入
const jwt = require("jsonwebtoken");
const expressJWT = require("express-jwt");
// 2.设置密钥
const secretKey = "dong";
// 3.配置哪些接口不需要访问权限 案例中以/api开头的接口为不需要权限
app.use(expressJWT({ secret: secretKey }).unless({ path: [/^\/api\//] }));
// 解决跨域问题
app.use(cors());
app.use(express.urlencoded({ extended: false }));
app.use(express.json());
// 登录
app.post("/api/login", (req, res) => {
  const { username, password } = req.body;
  if (username != "张三" || password != "12345") {
    return res.send({
      success: false,
      message: "用户名或密码错误",
    });
  }
  /**
   * 4. 生成JWT字符串(3个参数)
   * 参数1:用户的信息对象
   * 参数2:加密的密钥
   * 参数3:配置对象
   */
  const token = jwt.sign({ username: username }, secretKey, {
    expiresIn: "55s",
  });
  res.send({
    success: true,
    data: {
      token,
    },
    message: "登录成功",
  });
});
app.post("/admin/getName", (req, res) => {
  // 5. req.user 可以获取到用户信息(token)## 最新版本改为req.auth
  res.send({
    success: true,
    data: req.user,
  });
});
// 错误中间件用来捕获错误
app.use((err, req, res, next) => {
  // 捕获token失效的错误
  if ((err.name = "UnauthorizedError")) {
    return res.send("token失效");
  }
  res.send("错误" + err.message);
});
app.listen(8089, () => {
  console.log("server http://127.0.0.1:8089");
});
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
  • 接口调用 在请求头(Headers)中添加Authorization : Bearer Token
上次更新: 2025/04/30, 10:02:00
Mac时代
webpack

← Mac时代 webpack→

最近更新
01
TypeScript是什么
06-15
02
项目搭建
05-21
03
Prettier
02-27
更多文章>
Theme by Vdoing | Copyright © 2022-2025 京ICP备2020044002号-4 京公网安备11010502056618号
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式