前言:
node.js 是一个基于 V8 javaScript引擎的JavaScript运行时环境
在chrome浏览器,还需要 解析,渲染html , CSS 相关引擎, 支持浏览器相关操作的api,
浏览器自己的事件循环
同样,在node.js中,也包含了一些额外的操作,比如文件系统的读写 ,网络的IO ,加密, 压缩文件的操作
node 架构
一、fs模块
File system | Node.js v20.10.0 Documentation (nodejs.org)
01、认识
对于服务端通常都会用自己的文件系统,服务端需要将各种数据,文件,图片放到不同的地方,数据大多数放在数据库,而配置文件,用户资源, 图片,视频和音频 都是以文件的形式存放在操作系统上的
02、API介绍
2.1、api的3种操作方式
方式一、同步方式,会阻塞后续代码的运行
方式二、异步回调:代码不会被阻塞,需要传入回调函数
方式三、异步Promise操作文件,返还一个Promise对象
2.2、读写
2.3、目录
读取目录下的文件可以使用 递归读取
二、events模块
01、基本使用
const EventEmitter = require('node:events');
class MyEmitter extends EventEmitter {}
const myEmitter = new MyEmitter();
myEmitter.on('event', () => {
console.log('an event occurred!');
});
myEmitter.emit('event');
02、API
三、Buffer
Buffer | Node.js v20.10.0 Documentation (nodejs.org)
01、Buffer类
//创建buffer
const buf = Buffer.from()
const buf = Buffer.alloc(size[,file[,encoding]])
在创建buffer时,直接创建一个pool(Buffer | Node.js v20.10.0 Documentation (nodejs.org)),8kb , 如果剩余长度不够填充,才会新创建createPool, 如果够使用,就直接使用, 之后使用poolOffset
02、Blob类
四、stream
01、认识
02、Node.js中的4种流
03、可读流的基本使用:
cosnt fs = require('fs')
//通过流读取文件
const readstream = fs.createReadStream('aaa.txt' ,()=>{
start : 8,
end : 22 ,
highWaterMark : 3
}
)
//监听读取过来的数据
readstream.on('data' , (data)=>{
console.log(data.toString())
})
//监听文件打开事件, 传入一个参数fd 文件描述符
readstream.on('open' , (fd)=>{
})
//监听文件关闭事件
readstream.on('close',()=>{
})
04、可写流
05、pipe
readstream.pipe(writestrream)
五、http (可以开发服务器)
01、创建http
const http = require('node:http');
// Create a local server to receive data from
const server = http.createServer((req, res) => {
res.writeHead(200, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({
data: 'Hello World!',
}));
});
server.listen(8000);
可以创建多个端口,每一个端口提供不同的服务,
server.listen()函数有不同的参数,具体看文档Net | Node.js v20.10.0 Documentation (nodejs.org)
注意:访问localhost:8000访问了2次
第一次访问:localhost:8000
第二次访问: localhost :8000/favicon.ico (浏览器的默认行为)
02、nodemon
作用:监测服务器文件发生改变,重启服务器
03、request
3.1、req携带的数据的分析:
url, query (这里想得到req的,使用url , 和 querstring 'qs')处理即可
3.2、req的本质是一个可读流,传入的数据
通过使用req.on('data',()=>{
})可以拿到传入req对象的body数据
3.3、登陆原理
const http = require('http')
const server = http.createServer((req,res)=>{
//req 的本质是一个可写流, 如果不设置编码格式,会以二进制形式
req.setEncoding('utf-8')
//拿到req传递的数据
//如果数据在body中
req.on('data', ()=>{
//和服务器端的数据进行比较
//判断是否成功
})
req.on('end',()=>{
// xxx
})
})
3.4、请求头Header
content-type
content-length: 文件的大小长度
keep-alive:
accept-encoding :
告诉服务器客户端支持的文件压缩格式, 比如 js文件压缩的gzip .gz文件 ,通常使用默认值即可
accept :
告诉服务器,客户端可以接受的普通的文件格式
user-agent
客户端的相关信息
3.5、Authorization
用来设置token,用来验证
04、response
4.1、res本质是一个可写流
如果希望客户端返回数据
res.write('msg') //直接写出的数据 , 但是可写流是没有关闭的
res.end('。。。。') //最后写出的数据 ,但是需要手动关闭 ,
//如果没有end ,客户端会一致等待 , 需要在给请求设置超时时间
4.2、响应状态码
常见的状态码:
HTTP 响应状态码 - HTTP | MDN (mozilla.org)
HTTP 响应状态码用来表明特定 HTTP 请求是否成功完成。 响应被归为以下五大类:
设置状态码;
//1\
res.statuscode = 401
//2
res.writeHead(401,{
})
4.3、响应头
设置头
res.setHeader('content-type' , 'application/json ; charset=utf8)
res.serHeader(200, {
'content-type' :'application/json ;
})
05、node中的axios ,nodemon
axios库可以在浏览器中使用,也可以在Node中使用
在浏览器,axios使用的是封装的xhr
在Node,使用的是内置的http模块封装
nodemon, node moniter , 监控浏览器的资源是否改变,改变就重启浏览器
安装: npm install nodemon -g (全局安装)
06、使用http发送网络请求
const http = require('http')
http.get('http://localhost:8000' , res=>{
res.on('data', data=>{
console.log(Json.parse(data.toString()))
})
})
//和服务器建立一个可读流,写入一些东西
const req = http.request( {
method:'POST',
hostname:'localhost',
port : 8000
}, res =>{
res.on('data', data=>{
console.log(Json.parse(data.toString()))
})
})
//注意post请求要 调一下end()表示写入结束
req.end()
07、文件上传
使用 Node.js 的 http 模块处理文件上传 - 掘金 (juejin.cn)
上传文件多是用form-data格式
可写流中包含了其他数据,使用字符串处理,将图片对应的二进制串,单独的保存在文件中
const http = require('http')
const fs = require('fs')
const server = http.createServer((req, res) => {
req.setEncoding('binary')
const boundary = req.headers['content-type'].split('boundary=')[1]
let reqData = ''
req.on('data', data => {
reqData += data
})
req.on('end', () => {
const imgType = 'image/png'
const imgDataStartIndex = reqData.indexOf(imgType) + imgType.length
const imgDataEndIndex = reqData.indexOf(`--${boundary}--`)
const imgData = reqData.substring(imgDataStartIndex, imgDataEndIndex).trim()
fs.writeFile('./img.png', imgData, 'binary', err => {
if (!err) console.log('图片写入成功')
})
res.end('上传成功')
})
})
server.listen(3010, () => console.log('服务器开启'))
08、浏览器上传文件到Node服务器
六、后续
七、express(开发服务器)
01、搭建express
npm init
npm install
main.js
const express = require('express')
const app = express()
app.get('/',(res ,req)=>{
res.end('访问成功')
})
app.post('/',(req , res)=>{
res.end('成功')
})
app.listen(3000 , ()=>{
console.log('端口启动')
})
02、中间件
编写中间件 - Express 中文文档 (nodejs.cn)
简单来说,就是给express传递的回调函数就是中间件
中间件函数是在应用程序的请求-响应周期中可以访问 请求对象 (req
)、响应对象 (res
) 和 next
函数的函数。next
函数是 Express 路由中的一个函数,当被调用时,它会在当前中间件之后执行中间件。
中间件函数可以执行以下任务:
- 执行任何代码。
- 更改请求和响应对象。
- 结束请求-响应周期。
- 调用堆栈中的下一个中间件。
如果当前中间件函数没有结束请求-响应循环,它必须调用 next()
将控制权传递给下一个中间件函数。否则,请求将被挂起。
下图显示了中间件函数调用的元素:
中间件函数适用的 HTTP 方法。 中间件函数适用的路径(路由)。 中间件函数。 中间件函数的回调参数,按约定称为 "next"。 中间件函数的 HTTP response 参数,按约定称为 "res"。 中间件函数的 HTTP request 参数,按约定称为 "req"。 |
从 Express 5 开始,返回 Promise 的中间件函数在拒绝或抛出错误时将调用 next(value)
。next
将使用被拒绝的值或抛出的错误来调用。
03、编写中间件
const express = require('express')
const app = express()
//通过use注册的中间件是最简单的中间件
app.use((req, res, next)=>{
next()
})
app.use((req, res, next)=>{
//上一个中间件,执行了next方法,才会运行到这里,对于一个中间件,不是end(),就next()
//这一点上边提到, 如果 没有next() 和 end() 就会被 挂起
})
app.listen(3000,()=>{})
路径匹配中间件
app.use('/path' , (req ,res , next) =>{
//中间件的逻辑
res.end(data)
})
路径和方法匹配中间件
app.get('/path' , (req ,res , next )=>{
//中间件的相关逻辑
res.end(data)
} )
注册多个中间件
app.method('/path',中间件1 , 中间件2 ,...)
多个匹配的中间件是否执行,取决与第一个中间件是否为next()
04、应用中间件
4.1、 req传入数据格式处理,res.body如何获取
const express = require('express')
const app= express()
/**
* 想要在res.body中得到传入的数据, 要使用中间件对res携带的数据解析
/
//如果使用的是json格式的
app.use(exxpress.json())
//如果传入的是urlencoded 格式的,使用
app.use(express.urlencoded())
//如果传入的是formdata, 使用muleter
app.use(multer().any)
//需要注意的是, 默认使用的是querystring模块,有点老,控制台会出警告
//改为使用qs第三方库
app.use(express.urlencoded({extended:true}))
app.post('/login',(req,res,next)=>{
})
app.listen(8000,()=>{
console.log('端口已启动')
})
4.2、日志中间件morgan
// npm install morgan
const express = require('express')
const fs = require('fs')
const morgan = require('morgan')
const app= express()
const writeStream = fs.createWriteStream('path')
app.use(morgan('combined'),{stream : writeStream})
app.post('/login',(req,res,next)=>{
})
app.listen(8000,()=>{
console.log('端口已启动')
})
4.3、文件上传 multer
// npm install multer
const express = require('express')
const fs = require('fs')
const morgan = require('multer')
const app= express()
const upload = multer({
dest:'./upload'
})
const upload = multer({
storage: multer.diskStorage({
destination(req,file,cb){
cb(null, '你要存的地方')
},
filename(req,file,cb){
cb(null, '自定义的文件名'+file.originalname)//自定义的文件名 + 文件原来的后缀名
}
})
})
//传入单个文件
app.post('/login', upload.single('传入的属性名') ,(req,res,next)=>{
console.log(res.file)
})
//传入多个文件
app.post('/login', upload.array('传入的属性名') ,(req,res,next)=>{
console.log(res.file)
})
app.listen(8000,()=>{
console.log('端口已启动')
})
05、express路由
express.Router
使用 express.Router
类创建模块化、可安装的路由处理程序。一个Router
实例是一个完整的中间件和路由系统;因此,它通常被称为 "mini-app"。
以下示例将路由创建为模块,在其中加载中间件函数,定义一些路由,并将路由模块安装在主应用程序的路径上。
在app目录下创建一个名为birds.js
的路由文件,内容如下:
const express = require('express')
const router = express.Router()
// middleware that is specific to this router
router.use((req, res, next) => {
console.log('Time: ', Date.now())
next()
})
// define the home page route
router.get('/', (req, res) => {
res.send('Birds home page')
})
// define the about route
router.get('/about', (req, res) => {
res.send('About birds')
})
module.exports = router
然后,在应用程序中加载路由模块:
const birds = require('./birds')
// ...
app.use('/birds', birds)
06、express部署静态资源服务器
部署静态资源服务器的方式有很多,比如说nigax,tomcat等
app.use(express.static('文件名'))
07、express错误处理
错误处理 - Express 中文文档 (nodejs.cn)
08、express源码学习
后续补充
八、koa (开发服务器)
Koa (koajs) -- 基于 Node.js 平台的下一代 web 开发框架 | Koajs 中文文档 (bootcss.com)
01、content
和express一样,koa使用app.use()调用中间件,但是koa的中间件只接受2个参数,
app.use((ctx,next)=>{
//中间件的相关逻辑
})
Koa Context 将 node 的 request
和 response
对象封装到单个对象中,为编写 Web 应用程序和 API 提供了许多有用的方法。 这些操作在 HTTP 服务器开发中频繁使用,它们被添加到此级别而不是更高级别的框架,这将强制中间件重新实现此通用功能。
_每个_ 请求都将创建一个 Context
,并在中间件中作为接收器引用,或者 ctx
标识符,如以下代码片段所示:
app.use(async ctx => {
ctx; // 这是 Context
ctx.request; // 这是 koa Request
ctx.response; // 这是 koa Response
});
为方便起见许多上下文的访问器和方法直接委托给它们的 ctx.request
或 ctx.response
,不然的话它们是相同的。 例如 ctx.type
和 ctx.length
委托给 response
对象,ctx.path
和 ctx.method
委托给 request
。
注意:
02、koa-router路由
原来是第三方库,但是很久没维护,官方维护了一个库
03、koa参数的传递和解析
const koa = reuqire('koa')
const userRouter = new koaRouter({ prefix : '/users'})
// 参数传递的5种方式
// get: params , /:id
// get: query , ?name=value&&key=da
// post: json , { k:v , k:v}
// post: x-www-from-urlencoded
// post: from-data
1/params
userRouter.get('/:id',(ctx , next)=>{
const id = ctx.params.id
ctx.body = 'users data ' + id
})
2/query
userRouer.get('/',(ctx,next)=>{
const query = ctx.query
ctx.body = ''+ JSON.stringfy(query)
})
3/json
//使用koa-bodyparser 中间件 , 需要安装
npm install koa-bodyparser
app.use(bodyParser())
app.use((ctx,next)=>{
// ctx.request.body 就是传递过来的数据
ctx.body // 是放在request对象里的
})
4/urlencoded
//同样使用中间件,
//使用koa-bodyparser 中间件 , 需要安装
npm install koa-bodyparser
//和 json 的处理方式一样
5/from-data
//解析body中的数据,需要使用multer
//安装依赖 npm install koa-multer
// 使用multer中件件
const upload = multer({
//options
})
app.use(upload.any())
app.use((ctx,next)=>{
ctx.request.body // 就是传递过来解析号的from-data数据
})
04、koa文件上传
05、静态服务器
使用第三方库,
npm install koa-static
const Koa = require('koa')
const static = require('koa-static')
const app = new Koa()
//不同于express, koa内部没有部署相关的功能
// express , app.use(express.static('./build'))
// koa , 安装第三方库koa-static后,调用即可
app.use(static('./build'))
app.listen(8000, ()=>{
console.log('静态服务器开启成功')
})
06、响应数据的方式
输出结果:body将作为响应主题
string ,buffer, Stream , Object||Array , null ,
如果response.status没有设置, koa会自动将状态设置为200或者204
设置status,ctx.status = 200
ctx.response.status = 204
07、错误处理
const koa = require('koa')
const app = new koa()
app.use((ctx,next)=>{
ctx.app.emit(''error' , new Error('errorMsg') , ctx)
//触发error事件,处理错误
})
app.on('error',(err ,ctx)=>{
console.log(err.message)
//根据不同的错误码返回不同的数据
switch(ctx.body.code)
})
app.listen(8000,()=>{
console.log('服务器启动成功')
})
08、koa源码学习
09、和express的比较
再也不怕面试官问你express和koa的区别了 - 掘金 (juejin.cn)
架构方面:
express内置很多的功能更完善
koa是更加简洁和自由的,只包含了最核心的功能,不会对我们使用的其他中间件进行任何的限制
相同点:
他们的核心都是中间件,对于同步操作,他们的执行顺序是一样的,但是异步操作的执行逻辑略有不同
10、koa,express执行同步和异步代码的逻辑
同步
app.use((ctx,next)=>{
ctx. msg = 'aaa'
next()
console.log('ctx.msg') // aaabbbccc
})
app.use((ctx,next)=>{
ctx. msg += 'bbb'
next()
})
app.use((ctx,next)=>{
ctx. msg += 'ccc'
next()
})
异步
app.use((ctx,next)=>{
ctx. msg = 'aaa'
next()
console.log('ctx.msg') // aaabbb , koa如果下一个中间件是一个异步函数,koa默认不会等到下一个函数的结果,就会直接执行下一步的操作
})
app.use((ctx,next)=>{
ctx. msg += 'bbb'
next()
})
app.use( async (ctx,next)=>{
const data = await axios.get('')
ctx.msg+= data
next()
})
//如果需要等待异步函数的执行结果,需要在next函数前+await, 中间件函数前加上 async
app.use( async (ctx,next)=>{
ctx. msg = 'aaa'
await next()
console.log('ctx.msg') // aaabbb'data的数据'
})
app.use( async (ctx,next)=>{
ctx. msg += 'bbb'
await next()
})
app.use( async (ctx,next)=>{
const data = await axios.get('')
ctx.msg+= data
next()
})
express执行同步的逻辑和koa的方式一样
执行异步的逻辑不同,
koa的next返回的是一个promise,可以使用await
但是express 的next返回的void, await next()是不会等待异步函数执行结果
所以express, 返回结果,只能在异步函数的后面返回结果