node1
约 4908 字大约 16 分钟
2025-05-02
什么是Nods.js
是一个基于Chrome V8引擎的JavaScript 运行环境
1. fs 文件系统模块
操作文件的模块
const fs = require('fs')
读文件
fs.readFile()
fs.readFile('./1.text', 'utf-8', (err, dataStr) => { // console.log(err)// null 成功 if (err) { return console.log('读取失败' + err.message) } console.log('读取成功' + dataStr) })
写文件
fs.writeFile()
fs.writeFile('F:./.text', '123', 'utf-8', (err) => { if (err) { return console.log('写入失败') } })
相对路径的的问题

解决方案:在使用 fs 模块操作文件时,直接提供完整的路径,不要提供 ./ 或 ../ 开头的相对路径,从而防止路径动态拼接的问题
__dirname 当前文件所处的目录
const fs = require('fs')
console.log(__dirname)
fs.readFile(__dirname + '/1.text', 'utf-8', (err, dataStr) => {
console.log(dataStr)
})
2. path 路径模块
用来处理路径模块
const path = require('path')
path.join()
方法,用来将多个路径片段拼接成一个完整的路径字符串
const fs = require('fs')
const path = require('path')
fs.readFile(path.join(__dirname, '../文件系统fs/1.text'), 'utf-8', (err, dataStr) => {
if (err) {
console.log('读取文件失败', err.message)
}
console.log(dataStr)
})
path.basename()
方法,用来从路径字符串中,将文件名解析出来

path.extname**()** 的代码示例
3. 拆分 css js html
const fs = require('fs')
const path = require('path')
const regStyle = /<style>[\s\S]*<\/style>/
const regJs = /<script>[\s\S]*<\/script>/
fs.readFile(path.join(__dirname, './index.html'), 'utf-8', (err, dataStr) => {
if (err) return console.log('读取失败' + err.message)
// console.log(dataStr)
resolveCss(dataStr)
resolveJs(dataStr)
resolveHtml(dataStr)
})
// 拆除css
const resolveCss = (htmlStr) => {
const cssStr = regStyle.exec(htmlStr)
let strCss = cssStr[0].replace('<style>', '').replace('</style>', '')
fs.writeFile(path.join(__dirname, './index.css'), strCss, 'utf-8', err => {
if (err) return console.log('失败' + err.message)
console.log('成功')
})
}
// 拆除js
const resolveJs = (htmlStr) => {
const cssStr1 = regJs.exec(htmlStr)
let strCss = cssStr1[0].replace('<script>', '').replace('</script>', '')
fs.writeFile(path.join(__dirname, './index.js'), strCss, 'utf-8', err => {
if (err) return console.log('失败' + err.message)
console.log('成功')
})
}
// 拆分html
const resolveHtml = (htmlStr) => {
let strCss = htmlStr.replace(regStyle, '<link rel="stylesheet" href="./index.css">').replace(regJs, '<script src="./index.js"></script>')
fs.writeFile(path.join(__dirname, './index2.html'), strCss, 'utf-8', err => {
if (err) return console.log('失败' + err.message)
console.log('成功')
})
}
3. http模块
创建web服务器
导入
const http = require('http')
创建服务器实例对象
const server = http.createServer()
实例对象绑定request事件
server.on('request', (req, res) => { log('你好') })
启动服务器
server.listen(80, () => { log('服务器启动') })
1. req 对象
请求对象 包含与客户端相关属性
req.url
客户端请求地址
端口号后面地址
req.method
客户端请求类型
const http = require('http')
const server = http.createServer()
server.on('request', (req) => {
console.log( '请求地址' + req.url)
console.log( '请求方式' + req.method)
})
server.listen(80, () => {
console.log('服务器启动:http://127.0.0.1')
})
请求地址/
请求方式GET
2. res
获取服务器相关属性
res.end(数据)
向客户端发数据
const http = require('http')
const server = http.createServer()
server.on('request', (req, res) => {
console.log( '请求地址' + req.url)
console.log( '请求方式' + req.method)
// 向客户端响应内容
res.setHeader('Content-Type','text/html;charset=utf-8') // 中文乱码问题
res.end(req.url + req.method + '你好')
})
server.listen(80, () => {
console.log('服务器启动:http://127.0.0.1')
})
3. 根据不同的 url 响应不同的 html 内容
①获取请求的 url 地址
②设置默认的响应内容为 404 Not found
③判断用户请求的是否为 / 或 /index.html 首页
④判断用户请求的是否为 /about.html 关于页面
⑤设置 Content-Type 响应头,防止中文乱码
⑥使用 res.end() 把内容响应给客户端

4. 模块化
内置模块:(内置模块是由 Node.js 官方提供的,例如 fs、path、http 等)
自定义模块:(用户创建的每个 .js 文件,都是自定义模块)
第三方模块:(由第三方开发出来的模块,并非官方提供的内置模块,也不是用户创建的自定义模块,使用前需要先下载
1. 自定义模块
node 模块化
将大型模块 拆分为小的 模块 用时再组装
nodejs模块化规范 : commonJS模块化规范
js模块化规范: ESmodule 规范
1. commonJS模块化规范
导入 暴露
// a.js let num = 23 let str = '你好' const fn = () => { return '我是函数' } module.exports = { // 暴露 num, str, fn }
// b.js const { num, str, fn } = require('./a') // 导入 console.log(num) console.log(str) console.log(fn())
2. ES6 新模块化语法 ESmodule 规范
单个暴露
// a,js let a = 0 let b = 0 export a export a
单个导入
import {a, b} from './a.js' import * as xxx from './a.js' //单个暴露别名取法 不可多个暴露 //多个暴露需要 xxx.default
批量暴露
let a = 0 let b = 0 export default {a, b}
批量导入
import num = from './a.js'
注意: 页面 html 引入 js 文件时 需要告诉浏览器
<script src='./b.js' type="module"></script>
2. 第三方模块 npm包
1. 下载
有哪些包:npm | Home
下载地址
命令
npm i 包名
事件处理包 moment
2. 解决下包慢
切换下载地址
npm config set registry https://registry.npmmirror.com
管理地址切换
安装
npm i nrm -g
nrm ls // 可用镜像地址
nrm use taobao //使用
3. 开发自己的包
①新建 itheima-tools 文件夹,作为包的根目录
②在 itheima-tools 文件夹中,新建如下三个文件:
package.json (包管理配置文件)
index.js (包的入口文件)
README.md (包的说明文档)
- package.json
{
"name": "zy-tools",
"version": "1.0.0",
"main": "index.js",
"description": "提供时间格式化,HTMLEscape功能",
"keywords": ["zy", "dateFormat","escape"],
"license":"ISC"
}
index.js
function dateFormat(dataStr) { const dt = new Date(dataStr) const y = dt.getFullYear() const m = padZero(dt.getMonth() + 1) const d = padZero(dt.getDate()) const hh = padZero(dt.getHours()) const mm = padZero(dt.getMinutes()) const ss = padZero(dt.getSeconds()) return `${y}-${m}-${d} ${hh}:${mm}:${ss}` } // 加0 function padZero(str) { return str < 10 ? '0' + str : str } module.exports = { dateFormat }
使用
const zy = require('../myPackage/index') console.log(zy.dateFormat(new Date()))
3. 模块的加载机制
- 内置模块
模块在第一次加载后会被缓存。 这也意味着多次调用 require() 不会导致模块的代码被执行多次。
注意:不论是内置模块、用户自定义模块、还是第三方模块,它们都会优先从缓存中加载,从而提高模块的加载效率
- 自定义模块
使用 require() 加载自定义模块时,必须指定以 ./ 或 ../ 开头的路径标识符。在加载自定义模块时,如果没有指定 ./ 或 ../ 这样的路径标识符,则 node 会把它当作内置模块或第三方模块进行加载。
同时,在使用 require() 导入自定义模块时,如果省略了文件的扩展名,则 Node.js 会按顺序分别尝试加载以下的文件:
①按照确切的文件名进行加载
②补全 .js 扩展名进行加载
③补全 .json 扩展名进行加载
④补全 .node 扩展名进行加载
⑤加载失败,终端报错
- 第三方模块****的加载机制
如果没有找到对应的第三方模块,则移动到再上一层父目录中,进行加载,直到文件系统的根目录。
例如,假设在 'C:\Users\itheima\project\foo.js' 文件里调用了 require('tools'),则 Node.js 会按以下顺序查找:
① C:\Users\itheima\project\node_modules\tools
② C:\Users\itheima\node_modules\tools
③ C:\Users\node_modules\tools
④ C:\node_modules\tools
5. Express 模块
官方给出的概念:Express 是基于 Node.js 平台,快速、开放、极简的 Web 开发框架。
通俗的理解:Express 的作用和 Node.js 内置的 http 模块类似,是专门用来创建 Web 服务器的。
Express 的本质:就是一个 npm 上的第三方包,提供了快速创建 Web 服务器的便捷方法。
Express 的中文官网: http://www.expressjs.com.cn/
1. 安装
// 快速 eser
npm i express
2. 创建服务器
const express = require('express')
// 创建web服务器
const app = express()
// 启动服务器
app.listen(80, () => {
console.log('http://127.0.0.1')
})
3. 监听GET POST请求
app.get('请求URL'function(req, res) => {
// 处理函数
})
const express = require('express')
// 创建web服务器
const app = express()
// 监听get请求
app.get('/user', (req, res) => {
// 返回JSON
res.send({name:'张三',age: 18})
})
// 监听post请求
app.post('/users', (req, res) => {
res.send('请求成功')
})
// 启动服务器
app.listen(80, () => {
console.log('http://127.0.0.1')
})
4. 获取URL传的参数?name=20
?name=20=age = 20
app.get('/', (req, res) => {
res.send(req.query)
})
//{"name":"张安","age":"20"}
5. 获取动态的URL 动态参数 /user/:id
app.get('/user/:id', (req, res) => {
res.send(req.params)
})
// {"id":":123"}
6. 访问静态资源
通过代码访问 public 下的图片 css js 文件
app.use(express.static('public'))
const express = require('express')
const app = express()
app.use(express.static('../../public'))
app.listen(80, () => {
console.log('http://127.0.0.1')
})
- 托管多个静态资源
app.use(express.static(('public')) // 如有相同文件先查找写在前面的 如果没有 则查找后面的
app.use(express.static(('files'))
访问时 想挂载路径前缀
app.use('/public',express.static(('public'))
7. 热部署 nodemon
- 安装
npm i -g nodemon
- 使用
nodemon index.js
8. Express 路由
映射关系
客户端的请求和服务器的处理函数的映射关系
- 使用
app.请求类型(请求路径, 处理函数)
1. 路由模块化
不直接挂载到app上
- 创建路由模块.js
- 利用express.Router() 创建路由对象
- 在路由对象上挂载具体路由
- 导出路由对象
- app.use()使用路由
const express = require('express')
const router = express.Router()
router.get('/user/list', (req, res) => {
res.send('数据发送')
})
// 导出路由
module.exports = router
const router = require('./router.js')
const express = require('express')
const app = express()
app.use(router)
app.use('/api',router) // 可以加访问前缀
app.listen(80, () => {
console.log('http://127.0.0.1')
})
9. 中间组件
业务流程的中间环节


注意:中间件函数的形参列表中,必须包含 next 参数。而路由处理函数中只包含 req 和 res。
next() 函数式定义
next 函数是实现多个中间件连续调用的关键,它表示把流转关系转交给下一个中间件或路由。
const mw = (req, res, next) => { // 数据处理 // 传给下个中间件或路由 next() }
1.全局生效的中间件
客户端发起的任何请求,到达服务器之后,都会触发的中间件,叫做全局生效的中间件。
const mw = (req, res, next) => { // 数据处理 // 传给下个中间件或路由 next() } app.use(mw) // 简写 app.use((req, res, next) => { // 数据处理 // 传给下个中间件或路由 next() })
作用
多个中间件之间,共享同一份 req 和 res。基于这样的特性,我们可以在上游的中间件中,统一为 req 或 res 对象添加自定义的属性或方法,供下游的中间件或路由进行使用。
定义多个全局中间件
2. 局部中间件


3. 注意事项
①一定要在路由之前注册中间件
②客户端发送过来的请求,可以连续调用多个中间件进行处理
③执行完中间件的业务代码之后,不要忘记调用 next() 函数
④为了防止代码逻辑混乱,调用 next() 函数后不要再写额外的代码
⑤连续调用多个中间件时,多个中间件之间,共享 req 和 res 对象
4. 中间件分类
应用界别
通过 app.use() 或 app.get() 或 app.post() ,绑定到 app 实例上的中间件,叫做应用级别的中间件
路由级别
绑定到 express.Router() 实例上的中间件,叫做路由级别的中间件。它的用法和应用级别中间件没有任何区别。只不过,应用级别中间件是绑定到 app 实例上,路由级别中间件绑定到 router 实例上
错误级别
错误级别中间件的作用:专门用来捕获整个项目中发生的异常错误,从而防止项目异常崩溃的问题
格式:错误级别中间件的 function 处理函数中,必须有 4 个形参,形参顺序从前到后,分别是 (err, req, res, next)。
**注意:**错误级别的中间件,必须注册在所有路由之后
内置级别
app.use(express.static('public'))
express.json // 解析JSON格式表单数据 4.16.0后可用
express.urlencoded // 解析URL-encoded 格式的请求数据
三方
body-parser 解析请求数据
npm i body-parser // 导入 const parser = require('body-parser') // 使用 app.use(parser.urlencoded({extended: false}))
5. 自定义中间件
数据太大 一部分一部分接受 监听req的data事件

数据接收完之后 会触发 req的end 事件

利用 内置qureystring 模块专门用来处理查询字符串。通过这个模块提供的 parse() 函数,可以轻松把查询字符串,解析成对象的格式。

将解析出来的数据对象挂载为 req.body

10. 写接口
创建基本服务器
const express = require('express') const app = express() app.listen(80, () => { console.log('http://127.0.0.1') })
创建路由模块
const express = require('express') const apiRouter = express.Router() apiRouter.get('/user', (req, res) => { const query = req.query res.send({ status: 0, msg: 'GET请求成功', data: query }) }) module.exports = apiRouter // 返回数据 { "status": 0, "msg": "GET请求成功", "data": { "name": "123", "age": "12" } }
POST接口
const express = require('express') const apiRouter = require('./api/apiRouter') const app = express() app.use(express.json()) // 解析客户端json app.use('/api',apiRouter) app.listen(80, () => { console.log('http://127.0.0.1') })
const express = require('express') const apiRouter = express.Router() // get接口 apiRouter.get('/user', (req, res) => { const query = req.query res.send({ status: 0, msg: 'GET请求成功', data: query }) }) // post接口 apiRouter.post('/post', (req, res) => { const body = req.body res.send({ status: 0, msg: 'POST请求成功', data: body }) }) module.exports = apiRouter
11. 跨域问题
1. CORS(主流的解决方案,推荐使用)
CORS (Cross-Origin Resource Sharing,跨域资源共享)由一系列 HTTP 响应头组成,这些 HTTP 响应头决定浏览器是否阻止前端 JS 代码跨域获取资源。
安装
npm i cors
使用导入
const cors = reqire('cors')
在路由之前导入
app.use(cors())
2. JSONP(有缺陷的解决方案:只支持 GET 请求)
3. cors 的响应头
- **- Access-Control-Allow-**Origin


- **- Access-Control-Allow-**Headers

- **Access-Control-Allow-**Methods

12. 身份认证
对于服务端渲染和前后端分离这两种开发模式来说,分别有着不同的身份认证方案:
① 服务端渲染推荐使用 Session 认证机制
② 前后端分离推荐使用 JWT 认证机制
session
Cookie 是存储在用户浏览器中的一段不超过 4 KB 的字符串。它由一个名称(Name)、一个值(Value)和其它几个用于控制 Cookie 有效期、安全性、使用范围的可选属性组成。
不同域名下的 Cookie 各自独立,每当客户端发起请求时,会自动把当前域名下所有未过期的 Cookie 一同发送到服务器。
Cookie****的几大特性:
①自动发送
②域名独立
③过期时限
④4KB 限制
客户端第一次请求服务器的时候,服务器通过响应头的形式,向客户端发送一个身份认证的 Cookie,客户端会自动将 Cookie 保存在浏览器中。
随后,当客户端浏览器每次请求服务器的时候,浏览器会自动将身份认证相关的 Cookie,通过请求头的形式发送给服务器,服务器即可验明客户端的身份。

2. 在 Express 中使用 Session 认证
安装
npm i express-session
配置 express-session 中间件

向 session 中****存数据
从 session 中****取数据

- 清空 session

3. JWT 认证机制

JWT 通常由三部分组成,分别是 Header(头部)、Payload(有效荷载)、Signature(签名)。
JWT 的三个组成部分,从前到后分别是 Header、Payload、Signature。
其中:
l Payload 部分才是真正的用户信息,它是用户信息经过加密之后生成的字符串。
l Header 和 Signature 是安全性相关的部分,只是为了保证 Token 的安全性。
4. 在 Express 中使用 JWT
安装
npm i jsonwebtoken@8.5.1

ljsonwebtoken 用于生成 JWT 字符串
lexpress-**jwt 用于将** JWT 字符串解析还原成 JSON 对象
导入
定义 secret 密钥
为了保证 JWT 字符串的安全性,防止 JWT 字符串在网络传输过程中被别人破解,我们需要专门定义一个用于加密和解密的 secret 密钥:
①当生成 JWT 字符串的时候,需要使用 secret 密钥对用户的信息进行加密,最终得到加密好的 JWT 字符串
②当把 JWT 字符串解析还原成 JSON 对象的时候,需要使用 secret 密钥进行解密

- 在登录成功****后生成 JWT 字符串

- 将 JWT 字符串****还原为 JSON 对象

使用 req.user 获取用户信息
捕获解析 JWT 失败后产生的错误

13. 密码加密
不可被逆向破解
安装
npm i bcryptjs@2.4.3
使用
const bcrypt = require('bcryptjs') // 在注册用户的处理函数中,确认用户名可用之后,调用 bcrypt.hashSync(明文密码, 随机盐的 长度) 方法,对用户的密码进行加密处理: // 对用户的密码,进行 bcrype 加密,返回值是加密之后的密码字符串 userinfo.password = bcrypt.hashSync(userinfo.password, 10)
14. 表单验证
- 安装 @hapi/joi 包,为表单中携带的每个数据项,定义验证规则:
npm install joi
- 安装 @escook/express-joi 中间件,来实现自动对表单数据进行验证的功能:
npm i @escook/express-joi
- 新建 /schema/user.js 用户信息验证规则模块,并初始化代码如下:
const joi = require('joi')
/**
* string() 值必须是字符串
* alphanum() 值只能是包含 a-zA-Z0-9 的字符串
* min(length) 最小长度
* max(length) 最大长度
* required() 值是必填项,不能为 undefined
* pattern(正则表达式) 值必须符合正则表达式的规则
* ref('新密码') 验证新密码一致
*/
// 用户名的验证规则
const username = joi.string().alphanum().min(1).max(10).required()
// 密码的验证规则
const password = joi
.string()
.pattern(/^[\S]{6,12}$/)
.required()
// 注册和登录表单的验证规则对象
exports.reg_login_schema = {
// 表示需要对 req.body 中的数据进行验证
body: {
username,
password,
},
}
- 修改 /router/user.js 中的代码如下:
const express = require('express')
const router = express.Router()
// 导入用户路由处理函数模块
const userHandler = require('../router_handler/user')
// 1. 导入验证表单数据的中间件
const expressJoi = require('@escook/express-joi')
// 2. 导入需要的验证规则对象
const { reg_login_schema } = require('../schema/user')
// 注册新用户
// 3. 在注册新用户的路由中,声明局部中间件,对当前请求中携带的数据进行验证
// 3.1 数据验证通过后,会把这次请求流转给后面的路由处理函数
// 3.2 数据验证失败后,终止后续代码的执行,并抛出一个全局的 Error 错误,进入全局错误级别中间件中进行
处理
router.post('/reguser', expressJoi(reg_login_schema), userHandler.regUser)
// 登录
router.post('/login', userHandler.login)
module.exports = router
- 在 app.js 的全局错误级别中间件中,捕获验证失败的错误,并把验证失败的结果响应给客户 端:
const joi = require('joi')
// 错误中间件
app.use(function (err, req, res, next) {
// 数据验证失败
if (err instanceof joi.ValidationError) return res.cc(err)
// 未知错误
res.cc(err)
})
15 上传图片
安装处理forData模块
npm i multer@1.4.2
使用
const express = require("express"); const multer = require("multer"); const path = require("path"); const app = express(); // 配置存储引擎 const storage = multer.diskStorage({ destination: (req, file, cb) => { cb(null, path.join(__dirname, '../uploads'); // 保存到服务器本地的目录 }, filename: (req, file, cb) => { // 生成唯一文件名:时间戳+随机数+后缀 const uniqueName = Date.now() + "-" + Math.round(Math.random() * 1e9) + path.extname(file.originalname); cb(null, uniqueName); } }); // 文件类型过滤 const fileFilter = (req, file, cb) => { const allowedTypes = ["image/jpeg", "image/png", "image/gif"]; if (allowedTypes.includes(file.mimetype)) { cb(null, true); } else { cb(new Error("仅支持 JPEG/PNG/GIF 格式"), false); } }; const upload = multer({ storage: storage, limits: { fileSize: 5 * 1024 * 1024 }, // 限制5MB fileFilter: fileFilter }); // 上传接口 app.post("/upload", upload.single("image"), (req, res) => { if (!req.file) { return res.status(400).json({ error: "未选择文件" }); } // 返回文件访问路径 res.json({ url: `http://localhost:3000/uploads/${req.file.filename}` }); }); // 启动服务器 app.listen(3000, () => console.log("服务运行在 3000 端口"));
app.js
// 托管静态资源文件
app.use('/uploads', express.static(path.join(__dirname, 'uploads')));
- // 其他接口编写 ,请查询文档
- ev_api_server.pdf