一.什么是node.js
官网解释:Node.js 是一个开源的、跨平台的 JavaScript 运行时环境。
二.初步使用node.js
需要区分开的是node.js和javascript互通的只有console和定时器两个API.
三.Buffer
Buffer 是一个类似于数组的 对象,用于表示固定长度的字节序列。
Buffer 本质是一段内存空间,专门用来处理 二进制数据。
特点:
- Buffer 大小固定且无法调整
- Buffer 性能较好,可以直接对计算机内存进行操作
- 每个元素的大小为 1 字节(byte)
①Node.js中创建 buffer 的方式主要如下几种:
②Buffer与字符串转换
Number类型的toString()方法比较特殊,有默认模式和基模式两种。
四.fs模块
文件操作
1.文件写入
writeFile 异步写入
writeFileSync 同步写入
appendFile / appendFileSync 追加写入
createWriteStream 流式写入
①writeFile(异步写入)
语法: fs.writeFile(file, data[, options], callback)
② writeFileSync(同步写入)
语法: fs.writeFileSync(file, data[, options])
- 同步处理 JavaScript 主线程`会等待`其他线程的执行结果,然后再继续执行主线程的代码,效率较低
- 异步处理 JavaScript 主线程`不会等待`其他线程的执行结果,直接执行后续的主线程代码,效率较好
③ appendFile / appendFileSync (追加写入)
语法:
- fs.appendFile(file, data[, options], callback)
- fs.appendFileSync(file, data[, options])
④ createWriteStream ( 流式写入 )
程序打开一个文件是需要消耗资源的`,流式写入可以减少打开关闭文件的次数。
流式写入方式适用于`大文件写入或者频繁写入`的场景, writeFile 适合于`写入频率较低的场景
语法:fs.createWriteStream(path[, options])
2.文件读取
- readFile 异步读取
- readFileSync 同步读取
- createReadStream 流式读取
①readFile (异步读取)
②readFileSync (同步读取)
③createReadStream (流式读取)
3.文件复制
①method1
②method2 流式复制
还有一种方式
crf.pipe(cwf)
4.文件重命名和移动
语法:
fs.rename(oldPath, newPath, callback)
fs.renameSync(oldPath, newPath)
参数说明:
* oldPath 文件当前的路径
* newPath 文件新的路径
* callback 操作后的回调
①重命名
②移动
fs.renameSync(oldPath, newPath)操作 略
5.文件删除
①method1
②method2 nodejs 14.4版本引入
文件夹操作
1.创建文件夹
fs.mkdir(path [, options], callback)
fs.mkdirSync(path [, options])
递归创建:
2.读取文件夹
fs.readdir(path [, options], callback)
fs.readdirSync(path [, options])
3.删除文件夹
fs.rmdir(path [, options], callback)
:删除一个空的文件夹。
如果删除不为空的文件夹:
可以同样使用递归删除,但是不推荐利用fs.rmdir()进行递归删除,官方说将会弃用,推荐fs.rm()
fs.rm(path [, options], callback)
:删除一个文件夹或者文件。
查看资源信息
fs.stat(path [, options], callback)
__dirname
因为相对路径的参照物为命令行的工作目录,所以有不确定性:
__dirname与 require 类似,都是 Node.js 环境中的'全局'变量
__dirname 保存着当前文件所在目录的绝对路径,可以使用 __dirname 与文件名拼接成绝对路径
let data = fs.readFileSync(__dirname + '/data.txt');
console.log(data);
练习:批量重命名文件
const fs=require('fs')
// for(let i=1;i<=10;i++){
// fs.writeFileSync(`./test/${i}-test.js`,'hello')
// }
//批量重命名
const files=fs.readdirSync('./test')
files.forEach(item=>{
let splitname=item.split('-')
let[num,name]=splitname
if(Number(num)<10){
num='0'+num
}
let newname=num+'-'+name
fs.renameSync(`./test/${item}`,`./test/${newname}`)
})
五.path模块
1.path.resolve([...paths]):
该方法将一系列路径或路径段解析为 绝对路径.
①没有传递任何值的话,返回当前工具目录
②给定的路径序列从右到左处理,直到构造绝对路径。
③参数'/tmp/file/'
,如以斜杠开头,被视为绝对路径。此时,路径解析就从这个绝对路径开始,完全忽略剩余的路径参数
④如果只有一个相对路径,返回当前工作的绝对路径
⑤经典用法是传入绝对路径 + 相对路径
2.path.sep
提供特定于平台的 path segment separator(路径分隔符):
\
在 Windows 上/
在 POSIX 上
官方文档给出的用例:
3.path.parse(path)
path
<字符串>- 返回: <Object>
该方法返回一个对象,其属性表示 的重要元素
4.获取路径/文件名/扩展名
- 获取路径:path.dirname(
path
) - 获取文件名:path.basename(
path[, suffix])
- 获取扩展名:path.extname(path)
①path.dirname(path
)
② path.basename(path[, suffix])
path
<字符串>
suffix
<字符串>要删除的可选后缀- 返回: <string>
官方文档解释:模块的默认操作因操作而异 运行 Node.js 应用程序的系统。具体来说,在 Windows 操作系统,则模块将假定 正在使用 Windows 样式的路径。
因此,在 POSIX 和 Windows 上使用可能会产生不同的结果:path.basename()
要在任何 操作系统中,请使用 path.win32:
在任何 POSIX 文件路径上使用 POSIX 文件路径时获得一致的结果 操作系统中,请使用 path.posix:
③path.extname(path)
该方法返回 , 最后一部分出现 字符串末尾的'.'的字符
如果没有则返回空
5.path.isAbsolute(path)
该方法确定 是否为绝对路径
六.HTTP(Hypertext Transfer Protocol)协议
客户端和服务器的传输协议
请求报文:从客户端发往服务器的报文。
响应报文:服务器收到请求报文后,作为响应发往客户端的报文文。
①请求报文:
请求行举例:(http:80 https:443)
②响应报文
响应报文中:
七.IP
ip也称为ip地址,本身就是一个数字标识。用来标识网络中的设备,实现设备间通信。
本质是一串32bit的二进制数字->分隔成每8bit即1byte为一组就形成了ip地址。
IP地址由两部分组成,即网络地址和主机地址,二者是主从关系:
1. 网络号 net-id,它标志主机(或路由器)所连接到的网络,网络地址表示其属于互联网的哪一个网络
2.主机号 host-id,它标志该主机(或路由器),主机地址表示其属于该网络中的哪一台主机。
两级的 IP 地址可以记为: IP 地址 ::= { <网络号>, <主机号>}
简而言之就是:IP地址 = 网络号+主机号
分为A、B、C三类及特殊地址D、E。
全0和全1的都保留不用。
八.HTTP模块
1.启动http服务
(require->request)
2.http与https
协议端口区别:http默认端口为80(http协议服务开发常用端口为3000,8080,8090,9000等) https默认端口为443
3.获取请求报文内容
①请求路径 URL路径以及URL查询字符串
直接获取请求路径:
会获取路径和查询字符串 有时候需要拆分这个两个模块,此时借助url模块
如果想直接获取参数值而不是字符串
方法①
方法②(建议使用)
练习:
const http = require('http')
const server = http.createServer((request, response) => {
let url = new URL(request.url, 'http://127.0.0.1')
let { method } = request
let { pathname } = url
response.setHeader('content-type', 'text/html;charset=utf-8')
if (method == 'GET' && pathname == '/login') {
response.end('登陆页面')
} else if (method == 'GET' && pathname == '/reg') {
response.end('注册页面')
}else{
response.end('failure')
}
})
server.listen(9000, () => {
console.log('success')
})
4.获取响应报文内容
如果要使用res.write()最后必须要有res.end,两者是成对出现的,缺一不可,也就是说使用res.write方法向前端返回数据,必须调用res.end方法结束请求。否则浏览器会一直处于处于请求状态
常见响应状态码:
常见的十种:
200 : 成功,表示访问成功,正常状态。
301 : 永久移动,表示本网页已经永久性的移动到一个新的地址,在客户端自动将请求地址改为服务器返回的新地址。
302 : 临时重定向,表示网页暂时性的转移到一的新的地址,客户端在以后可以继续向本地址发起请求。
303 : 表示必须临时重定向,并且必须使用GET方式请求。
304 : 重定向至浏览器本身,当浏览器多次发起同一请求,且内容未更改时,使用浏览器缓存,这样可以减少网络开销。
401 : 表示用户没有访问权限,需要进行身份认证。
403 : 表示没有权限,服务器拒绝访问请求。
404 : 这是最常见的错误,(请求的资源或者网页不存在)表示找不到系统资源,但是只是暂时性地。
500 : 表示服务器程序错误,一个通用的错误信息。
503 : 表示服务器繁忙,或者服务器负载,通常这只是一个临时状态
练习:
//fight.js
const http = require('http')
const fs = require('fs')
const server = http.createServer((request, response) => {
let html = fs.readFileSync(__dirname + '/highlight.html')
response.end(html)//响应体可以执行多次
})
server.listen(9000, () => {
console.log('success')
})
//highlight.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<table border="1">
<tr>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td></td>
<td></td>
<td></td>
</tr>
</table>
</body>
<script>
let highlight=document.querySelectorAll('td')
highlight.forEach(item=>{
item.onclick=()=>{
item.style.background='#fff'
}
})
</script>
<style>
td{
padding: 20px 40px;
}
table tr:nth-child(odd){
background: aqua;
}
table tr:nth-child(even){
background: rgb(96, 238, 143);
}
table,td{
border-collapse: collapse;
}
</style>
</html>
5.资源服务
静态资源是指内容长时间不发生改变的资源
动态资源是指内容经常更新的资源
静态资源搭建:
const http = require('http')
const fs = require('fs')
const server = http.createServer((request, response) => {
let {pathname}=new URL(request.url,'http://127.0.0.1')
//根目录
let root=__dirname+'/page'
let filePath=root + pathname
fs.readFile(filePath,(err,data)=>{
if(err){
response.statusCode=500
response.end('error')
return
}
response.end(data)
})
})
server.listen(9000, () => {
console.log('success')
})
6.绝对路径和相对路径
绝对路径在实际用的比较多
绝对路径(框内部分为常用)
相对路径:
7.mime类型
动态设置subType
处理乱码:
8.get和post请求的区别
get请求通常用于从服务器获取资源,参数暴露在URL中,存在安全隐患,并且传输长度受URL限制,一般为2k。
情况例举:在地址栏输入url访问;点击a链接;link标签引入css;;img标签引入图片;script标签引入js;video与audio引入多媒体;form标签的method为get;ajax的get请求。
post请求通常用于向服务器提交数据或者创建新的资源。数据放在请求体中,而不是暴露在URL中,理论上来说没有传输长度限制。
情况举例:;form标签的method为post;ajax的post请求。
九.模块化
将一个复杂的程序文件依据一定的规则拆分为多个文件的过程称之为模块化
其中拆分出的每个文件就是一个模块,模块的内容数据是私有的,不过模块可以暴露内部数据以使其他模块使用。
1.模块暴露数据
注意:自己写的导入模块当中./和../是不可以省略的,导入nodejs内置模块才可以。
十.express框架
1.基本路由
路由用于确定应用程序如何响应对特定端点的客户机请求,包含一个 URI(或路径)和一个特定的 HTTP 请求方法(GET、POST 等)。
每个路由可以具有一个或多个处理程序函数,这些函数在路由匹配时执行。
路由定义采用以下结构:
app.METHOD(PATH, HANDLER)
其中:
app
是express
的实例。METHOD
是 HTTP 请求方法。PATH
是服务器上的路径。HANDLER
是在路由匹配时执行的函数。
有一种特殊路由方法:app.all()
,它并非派生自 HTTP 方法。该方法用于在所有请求方法的路径中装入中间件函数。
2.express中的API操作报文数据
请求报文:
原生
express API:
响应报文:
注意:
1. res.send()响应的数据是经过处理的
res.send()会自动发送更多的响应报文头,其中就有Content-Type:text/html;charset=utf-8,所以没有乱码。
即res.send返回的数据是被处理过的,打开浏览器控制台,在响应头中被自动添加了context-type,也就是说,res.send()方法响应返回给页面数据时,在响应头信息里会被自动添加设置返回数据类型的context-type属性
2. res.send()只能被调用一次,因为它等同于res.write+res.end()
多个send输出只执行第一个send语句,后续send语句将不被执行
重定向:
响应json:
响应文件内容:
3.获取路由参数
练习:根据路由参数响应歌手的信息
const express=require('express')
const {singers}=require('./singers.json')
const app=express();
app.get('/singer/:id.html',(req,res)=>{
let {id}=req.params
let result=singers.find(item=>{
return(item.id==Number(id))
})
if(result==undefined)
{
res.end('not found')
}else{
res.end(`<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<h1>${result.singer_name}</h1>
<img src='${result.singer_pic}' alt="">
<h2>${result.singer_id}</h2>
<h2>${result.id}</h2>
</body>
</html>`)
}
})
app.listen(3000,()=>{
console.log('3000 start')
}
)
4.中间件
中间件(Middleware)本质是一个回调函数
中间件函数可以像路由回调一样访问 请求对象(request) ,响应对象(response)
中间件的作用就是 使用函数封装公共操作,简化代码
①全局中间件
②路由中间件
③静态资源中间件
一个API解决了之前需要利用URL多次拼接才能达成的效果。
注意事项:
①如果有index.html文件,则为默认打开的资源
②如果静态资源和路由同时匹配,谁先匹配响应谁(单线程从上到下)
③路由响应动态资源,静态资源中间件响应静态资源
练习:
将手机和电脑置于同局域网下‘
将一个静态页面资源放置根目录下:
电脑:
手机:
5.获取请求体信息
获取请求体内容需要利用中间件:
两种方式:全局中间件和路由中间件
这里使用路由中间件:
6.防盗链
防盗链的作用:防止外部网站盗用本网站资源。(别人的域名没办法访问服务资源)
app.use((req, res, next) => {
//检测请求头中的referer是否为127.0.0.1
//获取referer
let referer = req.get('referer')
if (referer) {
let url = new URL(referer)
let hostname = url.hostname;
console.log(hostname)
if(hostname !== '127.0.0.1') {
res.status(404).send('<h1>404 not find</h1>');
}
}
next()
})
referer是header的一部分,当浏览器向web服务器发送请求的时候,一般会带上Referer,告诉服务器该网页是从哪个页面链接过来的,服务器因此可以获得一些信息用于处理。在本机上就是:127.0.0.1:3000; localhost:3000。
当我们获取referer后,我们可以获取域名。hostname用于获取域名。获取域名后,可以写一个简单的判断,如果域名是127.0.0.1就可以显示资源否则图片请求不到。
这里要说一个细节,细节就是,只有静态资源里面 有图片才可以正常获取referer。
7.路由模块化
8.ejs模板引擎
9.express-generator
Express 应用程序生成器 - Express中文文档 | Express中文网
文件上传也是发送http报文
①处理文件上传
利用工具包:formidable - npm然后配置post请求
保存:
var express = require('express');
var router = express.Router();
const formidable = require('formidable')
/* GET home page. */
router.get('/', function (req, res, next) {
res.render('index', { title: 'Express' });
});
router.get('/protrait', (req, res) => {
res.render('portrait')
})
router.post('/protrait', (req, res, next) => {
const form = formidable({
multiples: true,
uploadDir: __dirname + '/../public/images',
//保持文件后缀
keepExtensions: true,
});
form.parse(req, (err, fields, files) => {
if (err) {
next(err);
return;
}
console.log(fields)
console.log(files)
//服务器保存该图片的访问URL
let url='/images/'+files.protrait.newFilename//该数据以后需要存在数据库
res.send(url)
// res.json({ fields, files });
});
});
module.exports = router;
十一、mongoDB
Mongodb 中有三个重要概念需要掌握
数据库(database) 数据库是一个数据仓库,数据库服务下可以创建很多数据库,数据库中可以存 放很多集合
集合(collection) 集合类似于 JS 中的数组,在集合中可以存放很多文档 文档(document)
文档是数据库中的最小单位,类似于 JS 中的对象.
可以通过 JSON 文件来理解 Mongodb 中的概念
一个 JSON 文件好比是一个 数据库,
一个 Mongodb 服务下可以有 N 个数据库
JSON 文件中的 一级属性的数组值好比是集合
数组中的对象好比是文档
对象中的属性有时也称之为字段
一般情况下:
一个项目使用一个数据库 一个集合会存储同一种类型的数据
安装方法:MongoDB新版本安装配置教程(7.0.15版本-zip下载)-CSDN博客
1.Mongoose
Mongoose是一个对象文档模型库,方便使用代码操作mongodb数据库
npm i mongoose
①模型
Mongoose 中的一切都以 Schema 开头。每个 schema 都映射到一个 MongoDB 集合中定义 Shape 的 Shape 并定义该集合中文档的形状。
模型的实例称为文档。模型负责创建和 从底层 MongoDB 数据库中读取文档。
②字段类型
字段值校验:
必须:
默认:
枚举:
唯一(需要重建集合生效):
③文档操作
删除文档
删除多条则使用deleteMany
更新文档
更新多条则使用updateMany
读取文档:
读取单条还可以使用findById
读取多条则使用find(有条件读取满足条件的所有,没有则查询文档下所有)
④条件控制
运算符
在 mongodb 不能 > < >= <= !== 等运算符,需要使用替代符号
>使用 $gt
< 使用 $lt
>= 使用 $gte
<= 使用 $lte
!== 使用 $ne
let BookModel = mongoose.model('books', BookSchema)
BookModel.find({price:{$lt:20}}).then(data=>{
console.log(data)
}).catch(err=>{
console.log('err')
})
})
逻辑运算
$or 逻辑或的情况
let BookModel = mongoose.model('books', BookSchema)
BookModel.find({$or:[{author:'吴承恩'},{author:'余华'}]}).then(data=>{
console.log(data)
}).catch(err=>{
console.log('err')
})
})
$and 逻辑与的情况同样的用法
let BookModel = mongoose.model('books', BookSchema)
BookModel.find({$and:[{price:{$gt:10}},{price:{$lt:18}}]}).then(data=>{
console.log(data)
}).catch(err=>{
console.log('err')
})
})
正则匹配
条件中可以直接使用 JS 的正则语法,通过正则可以进行模糊查询
let BookModel = mongoose.model('books', BookSchema)
BookModel.find({name:new RegExp('红')}).then(data=>{
console.log(data)
}).catch(err=>{
console.log('err')
})
})
⑤个性化读取
十二.接口
接口是前后端通信的桥梁
简单理解:一个接口就是服务器中的一个路由规则,根据请求响应结果
接口->API(Application Program interface),所以有时称之为API接口
这里的接口是数据接口 以其他部分编程语言中的接口语法不同。
接口一般组成部分:
- 请求方法
- 接口地址(URL)
- 请求参数
- 响应结果
例如:身份证查询-免费API,收集所有免费的API
1.RESTful API
RESTful API是一种特殊风格的接口,主要特点有如下几个
- URL中的路径表示资源,路径中不能有动词,例如create等
- 操作资源要与HTTP 请求方法对应
- 操作结果要与HTTP 响应状态码对应
json-server
一个JS编写的工具包,可以快速搭建RESTful API服务
2.接口测试工具
apipost: