赞
踩
node -v
方式一:通过交互式解释器repl(较少用
类似前端的console
在 nodejs 环境下,不能使用 BOM 和 DOM ,也没有全局对象 window
全局对象的名字叫 global
使用
启动repl环境:命令行输入node
退出repl环境:ctrl+c 按2次
方式二:执行js文件(推荐
node ./index.js
安装提示模块 npm i --save-dev @types/node
在项目根目录下 新建config.js输入如下内容
{
compilerOptions: {
types: ["node"]
}
}
const http = require("http"); // 引入nodejs内置模块 http
// 利用http创建一个服务器
const server = http.createServer((req,res)=>{
// req: request 请求 ,所有浏览器到服务器的内容都是和req 有关;
// res: response 返还,所有服务器到浏览器的内容 和 res有关;
res.setHeader("Content-Type","text/html;charset=utf-8");
res.write("<h1>hello world1111大家好</h1>");//通过服务器给浏览器输出
res.end();// 告诉浏览器 服务器输出完毕;
});
server.listen(8989); // 设置浏览器访问服务器的端口号 ;
命令行输入node
ctrl+c 按2次
http://localhost:8989
http://127.0.0.1:8989
http://ip地址:8989
最好使用1024以上的端口
以下的多数为默认配置
chrome保留的端口号不可使用
1, // tcpmux
7, // echo
9, // discard
11, // systat
13, // daytime
15, // netstat
17, // qotd
19, // chargen
20, // ftp data
21, // ftp access
22, // ssh
23, // telnet
25, // smtp
37, // time
42, // name
43, // nicname
53, // domain
69, // tftp
77, // priv-rjs
79, // finger
87, // ttylink
95, // supdup
101, // hostriame
102, // iso-tsap
103, // gppitnp
104, // acr-nema
109, // pop2
110, // pop3
111, // sunrpc
113, // auth
115, // sftp
117, // uucp-path
119, // nntp
123, // NTP
135, // loc-srv /epmap
137, // netbios
139, // netbios
143, // imap2
161, // snmp
179, // BGP
389, // ldap
427, // SLP (Also used by Apple Filing Protocol)
465, // smtp+ssl
512, // print / exec
513, // login
514, // shell
515, // printer
526, // tempo
530, // courier
531, // chat
532, // netnews
540, // uucp
548, // AFP (Apple Filing Protocol)
554, // rtsp
556, // remotefs
563, // nntp+ssl
587, // smtp (rfc6409)
601, // syslog-conn (rfc3195)
636, // ldap+ssl
989, // ftps-data
990, // ftps
993, // ldap+ssl
995, // pop3+ssl
1719, // h323gatestat
1720, // h323hostcall
1723, // pptp
2049, // nfs
3659, // apple-sasl / PasswordServer
4045, // lockd
5060, // sip
5061, // sips
6000, // X11
6566, // sane-port
6665, // Alternate IRC [Apple addition]
6666, // Alternate IRC [Apple addition]
6667, // Standard IRC [Apple addition]
6668, // Alternate IRC [Apple addition]
6669, // Alternate IRC [Apple addition]
6697, // IRC + TLS
10080,
require("文件路径path")
module.exports = 导出内容
require("文件路径path")
的返回值即为导出内容nodejs里的模块化规范——commonjs规范
commonjs规范的3种格式:1.文件模块;2.目录模块;3.特殊目录模块
把文件作为模块使用
⚠导入时路径的
./
不可缺
⚠目录名不能是中文
⚠导入时路径的
./
不可缺
方式一:通过文件路径引入目录模块
require("./amodule/index.js");
方式二:直接引入目录模块
默认单入口就是index.js
let {hello} = require("./amodule");
npm init
npm init -y
nodejs里的模块种类
内置模块(nodejs自己安装的模块)
自定义模块(文件模块、目录模块)
.第三方模块
一般都会下载到 特殊目录里 ,这个目录就是node_modules;
方式一:通过路径查找模块名称
let res = require("./node_modules/mymodule/index");
console.log(res);
let res = require("./node_modules/mymodule");
console.log(res);
方式二:直接引入模块名称
在特殊目录node_modules里的模块不需要加./
node_modules引入会自动向目录上层查找
在当前文件夹没有找到模块,会继续将上一层目录找模块,直到找到根目录C盘
let res = require("mymodule");
一般情况下node_modules 模块,都不被git管理起来
模块很大,同步时间会增长,且模块显示可下载)
.gitignore文件里面可以配置不被git管理起来的文件或者目录(文件里写入node_modules
即可)
commonjs拓展
commonjs可以引入js文件,也可以引入.json和.node后缀的文件
let data = require("./a"); //会先找后缀为js的a文件,再找.json,再找.node,如果都没有就报错
- 1
- 2
commonjs会自动把json字符串转成数组或者对象;
let data = require("./a.json");
- 1
index.js
let data = require("./a.json");
const http = require("http");
let server = http.createServer((req, res) => {
res.setHeader("Content-type", "text/html;charset=utf8");
console.log(req.url);
var pathName = req.url.split("?")[0];
if (req.url === "/") {
var str = `<ul>`;
data.forEach((item) => {
str += `<li><a href="/detail?id=${item.id}">${item.title}</a></li>`;
});
str += `</ul>`;
res.write(str);
res.end;
} else if (pathName === "/detail") {
var id = Number(req.url.split("?")[1].split("=")[1]);
console.log(id);
var result = data.find((item) => item.id === id);
res.write(result.content);
res.end();
}
});
server.listen(8787);
a.json
[
{
"id": 1,
"title": "标题一",
"content": "内容一"
},
{
"id": 2,
"title": "标题2",
"content": "内容2"
}
]
node package manager 包管理器
查看电脑npm是否安装好:npm -v
node_modules 一般放第三方模块 (下载)
下载地址
官方下载服务器 :https://registry.npmjs.org/
淘宝源地址 : https://registry.npmmirror.com
查看npm下载源地址
npm get registry
把官方下载源改成淘宝源地址
npm config set registry https://registry.npmmirror.com
npm 包查找:https://www.npmjs.com/
初始化一个package.json文件 ,用于记录安装包的信息(目标目录没有package.json才需要初始化)
npm init
⚠上层目录的名称不能是中文
安装模块
npm install 包名
npm install
//简写
npm i
查找指令所在目录的下的package.json文件 把package.json文件里的依赖自动下载安装 ;
默认局部安装,模块会安装在命令运行的目录
npm i 包名 -g
查看全局安装的目录
npm root -g
如果没有全局安装过内容 这个目录找不到 ;
全局安装自动刷新node服务器工具
npm i nodemon -g
使用:nodemon ./inde.js
只会在开发时使用的依赖 ,正式上线时不要的依赖(如Less)
因此该依赖包只在开发过程中存在,正式上线时该依赖包会删除
会显示在package.json的 devDependencies选项里
npm i 包名 --save-dev
//简写
npm i 包名 --D
生成和开发时都需要用到的依赖,如vue 、react
默认安装是生成依赖
npm i 包名 --save
//简写
npm i 包名 --S
npm i 包名@版本号
如果不指定安装版本 默认会安装最新版本
npm uninstall 包名
第一步 : 把npm的源地址改成官方源;
npm config set registry https://registry.npmjs.org/
第二步: https://www.npmjs.com/ 网站注册用户名密码
第三步 : 添加用户名和密码到npm配置里 ,只需要添加一次;
npm adduser
;
第四步 : 创建自己的包
注意点
包名不能是中文
npmjs上不能有重复的名称
npm publish
第五步: 删除包
npm unpublish --force
;项目原来用的哪个就用哪个,如用npm就用npm
npm代替品
yarn
facebook出品
cnpm
- china npm
- 一般不使用,会把npm的源改为cnpm
cyarn
npm i yarn -g
;yarn --version
;指令 | yarn | npm |
---|---|---|
初始化package.json文件 | yarn init | npm init |
安装模块(默认生成依赖) | yarn add 模块名 | npm i 模块名 |
安装开发依赖 | yarn add 模块名 --dev | npm i 模块名 --save-dev |
安装所有依赖 | yarn | npm i |
全局安装 | yarn global add 模块名 ⚠不要写成 yarn add global 模块名 | npm i 模块名 -g |
全局安装路径 | yarn global dir | npm root -g |
删除模块 | yarn remove 模块名 | npm uninstall 模块名 |
Buffer类似数组,其元素是16进制的两位数
Buffer.alloc(buffer长度,填充数(可选)) | 创建一个buffer 1.buffer长度即元素个数 2.填充数(0~255),以16进制显示出来,如255为ff |
Buffer.allocUnsafe(buffer长度) | 快速创建一个buffer |
Buffer.from(string或array) | 把字符串或数组转成buffer数据 |
buf.toString() | 把buffer转成字符串 (使用的多) |
说明 | |
---|---|
__dirname | 获取执行文件所在目录的绝对路径 |
__filename | 获取执行文件的绝对路径 |
process.cwd() | 获取命令执行的绝对路径/即当前工作目录绝对路径 在哪输入执行命令就是哪个路径 |
process.argv | 获取nodejs执行的目录 |
process.argv返回值是一个有2个参数的数组
第一个参数:nodejs的绝对路径
process.argv[0]
第二个参数:执行文件的绝对路径
❗process.argv可传第三个参数
node ./index.js 第三个参数
第三个参数可以用来区分执行,见下例
if(process.argv[2]==="production"){
console.log("执行生产模式的代码");
}else{
console.log("执行开发模式的代码");
}
处理文件路径
const path = require("path")
将参数从右往左向前拼接到一起,并返回绝对路径
path.resolve(path1,path2...)
拼接的几种情况:
字符以 / 开头,不会拼接到前面的路径
path.resolve('/foo/bar', '/baz')
结果: '/baz'
因为 '/baz' 已经是一个绝对路径 故不会再向前拼接
字符以 ./ 开头or没有符号,正常拼接前面路径
如果是第一个参数开头为./,前面会拼接当前工作目录的绝对路径
path.resolve('/foo/bar', './baz')
结果:'/foo/bar/baz'
字符以 …/ 开头,跳过前面路径的最后一节路径拼接
path.resolve('/foo/bar', '../baz')
结果: '/foo/baz'
拼接前面路径 '/foo/bar' 但是不包含前面路径最后 '/bar' 这部分
没有传入参数,则 path.resolve()
将返回当前工作目录的绝对路径
将所有参数拼接到一起后返回
path.join(path1,path2...)
../
会抵消前面的路径
const pathStr = path.join('/a','/b/c','../','./c','/d')
console.log(pathStr); // \a\b\c\d
处理url路径,已弃用的旧版url
const url = require("url");
https://nodejs.cn/api/url.html#the-whatwg-url-api
和旧版url内置模块不同,无需引入node自带的url模块即可使用
const myURL = new URL('https://example.org:8989/test?id=1');
const myURL = new URL('/foo', 'https://example.org/');
// https://example.org/foo
必须实例对象才能用
说明 | |
---|---|
url.hash | 获取和设置网址的哈希部分 1.获取:返回带了#的哈希值 'https://example.org/foo#bar' --> #bar 2.设置:设置无需带#,会自动补充# |
url.host | 获取和设置网址的主机部分,即根网址'https://example.org:81/foo' --> example.org:81 |
url.hostname | 获取和设置网址的主机名部分'https://example.org:81/foo' --> example.org |
url.href | 获取和设置整个网址'https://example.org/foo?123' --> https://example.org/foo?123 |
url.pathname | 获取和设置网址的路径部分'https://example.org/abc/xyz?123' --> /abc/xyz |
url.search | 获取和设置网址查询参数'https://example.org/abc?123' --> ?123 |
url.searchParams | 获取网址查询参数的Map对象 获取设置等操作同Map对象) |
文件系统 file system
可以操作文件或目录
const fs = require("fs")
文件操作都分2种
文件操作 | 同步执行 | 异步执行 |
---|---|---|
创建文件 | fs.writeFileSync("新文件及其路径", 新文件中的数据) | fs.writeFile"新文件及其路径", 新文件中的数据, err=>{}) |
读取文件 | fs.readFileSync("文件及其路径") | fs.readFile("文件及其路径", err=>{}) |
重命名文件 | fs.renameSync("文件及其路径", 新文件名) | fs.rename("文件及其路径", 新文件名, err=>{}) |
删除文件 | fs.unlinkSync("文件及其路径") | fs.unlink("文件及其路径", err=>{}) |
判断文件是否存在 | fs.existsSync("文件及其路径") | —————— |
同步读取
try {
let data = fs.readFileSync("./data.json");
let mydata = JSON.parse(data.toString());
console.log(mydata[0].name);
} catch (err) {
console.log(err);
}
异步读取
fs.readFile("./1.txt", (err, data) => {
if (err) {
return console.log(err);
}
console.log(data.toString());
});
fs.unlink("./1.txt",err=>{
if(err){
return console.log(err);
}
console.log("删除成功");
})
将异步文件操作函数包装成一个promise,方便使用async&await
function mypromisify(fn) {
return function (...args) {
return new Promise((resolve, reject) => {
fn(...args, (err, data) => {
if (err) {
reject(err);
} else {
if (typeof data === "undefined") {
// 写入等操作
resolve("写入成功");
} else {
// 读取;
resolve(data.toString());
}
}
});
});
};
}
//promisify(fs.readFile)("users3.json")
目录操作 | 同步 | 异步 |
---|---|---|
创建一个目录 | fs.mkdirSync("新目录及其路径") | fs.mkdirSync("新目录及其路径", err=>{}) |
修改目录 | fs.renameSync("目录及其路径", "新目录名及其路径") | fs.rename("目录及其路径", "新目录名及其路径", err=>{}) |
读取目录内容 ❗只能读一层目录 | fs.readdirSync("目录及其路径") | fs.readdir("目录及其路径", err=>{}) |
删除目录 ❗只能删除空目录 | fs.rmdirSync("目录及其路径") | fs.rmdir("目录及其路径", err=>{}) |
判断目录是否存在 | fs.existsSync("目录及其路径") | |
查看目录详细 | fs.statSync("目录及其路径") | fs.stat("目录及其路径", err=>{}) |
判断路径是否是文件 | fs.statSync("目录及其路径").isFile() | fs.stat("目录及其路径", err=>{}).isFile() |
判断路径是否是目录 | fs.statSync("目录及其路径").isDirectory() | fs.stat("目录及其路径", err=>{}).isDirectory() |
const fs = require("fs");
function rmDir(dirpath) {
if (fs.existsSync(dirpath)) {
let dirList = fs.readdirSync(dirpath);
dirList.forEach(item=>{
let itempath = dirpath+"/"+item;
let isDir = fs.statSync(itempath).isDirectory();
if(isDir){
//delete dir
rmDir(itempath);
}else{
//delete file
fs.unlinkSync(itempath);
}
})
fs.rmdirSync(dirpath);
} else {
console.log("目录不存在");
}
}
rmDir("./r14");
内存溢出/爆仓 & 内存泄漏 的区别
内存溢出
程序在申请内存时,没有足够的内存空间供其使用
内存泄漏
程序在申请内存后,无法释放已申请的内存空间(这块内存不释放,就不能再用了,就叫这块内存泄漏了)
指在程序中分配了内存空间,但在不再需要使用该内存空间时没有及时释放或释放不完全。这导致这部分内存无法再被程序使用,最终导致系统的可用内存逐渐减少。如果内存泄漏持续发生,最终可能导致系统崩溃或运行缓慢。常见的内存泄漏情况包括未释放的堆内存、循环引用、未清理的定时器或事件监听器等。
流 stream
数据传输的一种方式
将大数据切割为64k的小块后,再传输数据,避免内存溢出
⚠流是异步执行
说明 | |
---|---|
data | 流读取数据时触发 |
end | 流数据传递完毕时触发 |
error | 流在接收和写入过程中发生错误时触发 |
const fs = require("fs");
let rs = fs.createReadStream("./1.txt");
// 创建可读流;
// 获取流的数据;
let count = 0;
// 监听流的数据
let str = "";
rs.on("data",chunk=>{
//data事件:流读取数据时触发
str += chunk;
//由于流每次只传文件的一部分数据
//将其拼接在一起,方便后期使用
//拼接时会自动做字符串转换
count++;
})
rs.on("end",()=>{
//data事件:流数据传递完毕时触发
console.log(str);
})
re.on("error",err=>{
//error事件:流在接收和写入过程中发生错误时触发
console.log(err)
})
npm包——cheerio
https://github.com/cheeriojs/cheerio
安装cheerio
npm install cheerio
使用的第一步——使用cheerio加载HTML
一般直接将其赋值为 (以下以 (以下以 (以下以表达)
const $ = cheerio.load(HTML文档)
在文档中查找元素,语法同css选择器
$(selector)
获取元素的文本 text (相当于innerText)
$('body').text()
const http = require("http");
let weburl = "http://www.atguigu.com/";
const cheerio = require("cheerio");
// 第三方模块 ,需要安装;
// 安装 cheerio : npm i cheerio;
const fs = require("fs");
http.get(weburl,(req)=>{
// 监听流的数据;
let str = "";
req.on("data",chunk=>{
str += chunk;
})
req.on("end",()=>{
let $ = cheerio.load(str);
let arr = [];
$(".news .rr a").each((key,item)=>{
arr.push({
id:(key+1),
titile:$(item).text()
})
})
fs.writeFileSync("./data.json",JSON.stringify(arr));
})
})
实现querystring解析
module.exports = { parse(str) { var obj = {}; let arr = str.split("&"); arr.forEach((item) => { let itemarr = item.split("="); obj[itemarr[0]] = itemarr[1]; }); return obj; }, };
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
const http = require("http");
const fs = require("fs");
const { parse } = require("./myquerystring");
let server = http.createServer((req, res) => {
res.setHeader("Content-Type", "text/html;charset=utf-8");
let url = new URL("http://localhost:8787" + req.url);
if (url.pathname === "/") {
res.write(fs.readFileSync("./index.html"));
res.end();
} else if (url.pathname === "/post") {
let str = "";
req.on("data", (chunk) => {
str += chunk;
});
req.on("end", () => {
let obj = parse(str);
console.log(obj);
});
res.write("post");
res.end();
}
});
server.listen(8787);
作用:解耦
emit可传参,参数传到事件对象里
const EventEmitter = require("events");
let obj = new EventEmitter();
说明 | |
---|---|
obj.addListener("事件名",()=>{事件函数}) | 绑定自定义事件 |
obj.on("事件名",()=>{事件函数}) | 绑定自定义事件 |
obj.once("事件名",()=>{事件函数}) | 绑定一次性事件 绑定的事件只能被触发一次 |
obj.emit("事件名"); obj.emit("事件名", arg) | 触发事件 |
obj.once("事件名",事件函数) | 解除绑定事件 |
obj.removeListener("事件名",事件函数) | 解除绑定事件 |
obj.removeAllListeners() | 移除所有事件 |
obj.listenerCount("事件名") | 查看监听事件数量 |
obj.addListener("myevent",()=>{
console.log("hello")
})
obj.addListener("myevent",()=>{
console.log("hello2222")
})
obj.on("myevent",e=>{
console.log("hello",e);
})
obj.on("myevent",e=>{
console.log("hello222",e);
})
obj.once("myevent",()=>{
console.log("hello");
})
obj.once("myevent",()=>{
console.log("hello22");
})
obj.emit("myevent")//hello hello22
obj.emit("myevent")
obj.emit("myevent")
obj.emit("事件名");
obj.emit("事件名", arg)
obj.on("myevent",arg=>{
console.log("hello",arg);
})
setTimeout(() => {
obj.emit("myevent","arg");
}, 1000);
var fn2 = function(){
console.log("fn2");
}
obj.on("myevent",fn2)
obj.off("myevent",fn2);
var fn1 = function(){
console.log("fn1");
}
obj.on("myevent",fn1)
obj.removeListener("myevent",fn1);
var fn1 = function () {
console.log("fn1");
}
var fn2 = function () {
console.log("fn2");
}
obj.on("myevent", fn1)
obj.on("myevent1", fn2)
obj.removeAllListeners();
obj.emit("myevent");
obj.emit("myevent1");
var fn1 = function () {
console.log("fn1");
}
var fn2 = function () {
console.log("fn2");
}
obj.on("myevent", fn1)
// obj.on("myevent", fn2)
let res = obj.listenerCount("myevent");
console.log(res);
解除多个类或模块间的耦合
index.js
const http = require("http");
const url = require("url");
const EventEmitter = require("events");
let routers = require("./routers");
let eventObj = new EventEmitter();
routers(eventObj)
// 主页面,主入口 和 路由 耦合 ; 把路由拆分出来;
const server = http.createServer((req, res) => {
res.setHeader("Content-Type", "text/html;charset=utf-8;hello=123");
let { pathname } = url.parse(req.url);
if (eventObj.listenerCount(pathname) > 0) {
eventObj.emit(pathname, res);
}else {
res.write("<h1>404 not found!!</h1>");
res.end();
}
server.listen(8989);
routers.js
module.exports = function(eventObj){
eventObj.on("/",res=>{
res.write("<h1>主页</h1>");
res.end();
})
eventObj.on("/product",res=>{
res.write("<h1>产品页面</h1>");
res.end();
})
eventObj.on("/users",res=>{
res.write("<h1>用户页面</h1>");
res.end();
})
}
获取相对的url
res.header
let urlObj = new URL(req.url, “http://” + req.headers.host);
class Events {
constructor() {
this.handel = {
//myevent:[fn1,fn2]
};
}
on(eventName, eventFn) {
if (typeof this.handel[eventName] === "undefined") {
this.handel[eventName] = [];
}
this.handel[eventName].push(eventFn);
}
emit(eventName, ...args) {
if (typeof this.handel[eventName] !== "undefined") {
this.handel[eventName].forEach((item) => {
item(...args);
});
}
}
off(eventName, eventFn) {
this.handel[eventName].forEach((item, key) => {
if (item === eventFn) {
this.handel[eventName].splice(key, 1);
}
});
}
listenerCount(eventName) {
if (typeof this.handel[eventName] === "undefined") {
return 0;
} else {
return this.handel[eventName].length;
}
}
removeAllListener(eventName) {
delete this.handel[eventName];
}
}
var obj = new Events();
function fn1() {
console.log(111);
}
function fn2() {
console.log(222);
}
obj.on("myevent1", fn1);
obj.on("myevent1", fn2);
obj.removeAllListener("myevent1");
obj.emit("myevent1");
let res = obj.listenerCount("myevent1");
console.log(res);
超文本传输协议 (网络协议)Hyper Text Transfer Protocol ,是一种约定
https是http的升级版,做了加密
HTTP+加密+认证+完整性保护 = HTTPS
请求or返还首行
请求or返还头部信息(header)
请求头的Content-Type(form表单
enctype="application/x-www-form-urlencoded"
需记忆,后期使用ajax需要自己设置
请求or返还空行
请求or返还体(body)
每个都是一个http请求,包括请求报文和返还报文
影响返还体的格式和编码
res.setHeader("Content-Type", "text/html;charset=utf-8");
遵循MINE标准
method | 说明 |
---|---|
get | 获取数据 |
post | 添加数据 |
put | 替换更新 |
patch | 局部更新 |
delete | 删除操作 |
head | 头部请求 |
options | 预检请求 |
说明 | |
---|---|
1xx | 和http等待有关 |
2xx | 和成功有关 200:返还成功 201:部分返还成功,等待更新操作 204:头部返还成功 |
3xx | 和重定向有关(redirect,跳转) 301:永久重定向 302:临时重定向 304:读取缓存信息 |
4xx | 和客户端有关错误 401:没有权限 404:资源找不到 |
5xx | 和服务器有关错误 500:服务器错误 503:服务器请求处理不过来 |
const http = require("http");
let server = http.createServer((req,res)=>{
res.setHeader("Content-Type","text/html;charset=utf-8");
// 浏览器收到302状态码后会检测返还头部的location字段,然后做跳转
res.statusCode = 302;
res.setHeader("location","http://www.baidu.com");
res.write("<h1>你好</h1>");
res.end();
})
server.listen(8989);
预览返还体内容,相当于innerHTML
返还体内容,相当于innerText
网址后带参数时,可查询参数
文件不是相对于加载的html,而是相对于后端nodejs运行文件的位置
const http = require("http");
const fs = require("fs");
const path = require("path");
const mime = require("./mime.json");
let server = http.createServer((req, res) => {
let urlobj = new URL(req.url, "http://" + req.headers.host);
let pathname = urlobj.pathname;
res.setHeader("Content-Type", "text/html;charset=utf-8");
if (pathname === "/") {
let data = fs.readFileSync("./src/index.html");
res.write(data);
res.end();
} else if (pathname !== "/favicon.ico") {
let extName = path.extname(pathname);
let content = mime[extName];
res.setHeader("Content-Type", content);
let data = fs.readFileSync("./src/" + pathname);
res.write(data);
res.end();
}
});
server.listen(8787);
层次模型 | 作用 | 数据单元 | 主要设备 |
---|---|---|---|
应用层 | 网络服务与最终用户的一个接口 | message | |
表示层 | 数据的表示、压缩、格式化、加密 | message | |
会话层 | 建立、管理、中止会话 | message | |
传输层 | 定义传输数据的协议端口号,以及流量和差错校验 | 数据段 | |
网络层 | 进行逻辑地址寻址,实现不同网络之间的路径选择 | 数据包 | 路由器 |
数据链路层 | 建立逻辑连接,进行硬件地址寻址,差错校验等功能 | 数据帧 | 交换机 |
物理层 | 建立、维护、断开物理连接 | 比特流 | 网卡 |
Http协议是建立在TCP协议基础之上的,当浏览器需要从服务器获取网页数据的时候,会发出一次Http请求。Http会通过TCP建立起一个到服务器的连接通道,当本次请求需要的数据完毕后,Http会立即将TCP连接断开,这个过程是很短的,所以Http连接是一种短连接,是一种无状态的连接。
三次握手是网络客户端跟网络服务器之间建立连接,并进行通信的过程。相当于客户端和服务器之间 你来我往的3个步骤。
第一次握手是建立连接,客户端发送连接请求报文,并传送规定的数据包;
第二次握手是服务器端表示接收到连接请求报文,并回传规定的数据包;
第三次握手是客户端接收到服务器回传的数据包后,给服务器端再次发送数据包。这样就完成了客 户端跟服务器的连接和数据传送。
四次挥手表示当前这次连接请求已经结束,要断开这次连接。
第一次挥手是客户端对服务器发起断开请求,
第二次握手是服务器表示收到这次断开请求,
第三次握手是服务器表示已经断开连接
第四次握手是客户端断开连接。
express:nodejs极简框架
基于 Node.js 平台,快速、开放、极简的 Web 开发框架
Express 框架核心特性:
npm install express --save
说明 | |
---|---|
app.get() | 处理get请求 |
app.post() | 处理post请求 |
// 引入 express ,安装express;
let express = require("express");
// 创建express 应用
let app = express(); // app -->application
// 设置路由 get 是http的请求方法
app.get("/",(req,res)=>{
// req: request ,把原生的req做了封装 ;
// res: response ,把原生的res做了封装处理;
res.send("<h1>我是主页</h1>");
// send理解为write和end集合;
})
app.get("/product/:id",(req,res)=>{
res.send("<h1>我是产品页面</h1>")
})
// 设置端口号
app.listen(8989,function(){
console.log("服务器启动成功");
});
以下是一些常用的Express方法:
说明 | |
---|---|
req.query | 获取URL的查询参数串,结果值为对象 |
req.params | 获取路由的parameters/product/:id --> /product/10 --> { id: '10' } |
req.path | 获取请求路径,类似原生的pathname |
req.route | 获取当前匹配的路由相关信息 |
req.body / req.cookies | 获得「请求主体」/ Cookies (接收post参数 ,借助第三方模块) |
req.get() | 获取请求头部信息 |
说明 | |
---|---|
res.send() | 类似write和end的集合体 ⚠一个app.get中只能有一个res.send() |
res.readFile() | 读取文件,输出给返还体(加载页面 ⚠需要一个绝对地址(__dirname) |
res.json(data) | 将数据自动转成json输出 |
res.redirect(“url”) | 跳转到一个地址,设置响应的Location HTTP头,并且设置状态码302 |
res.status(状态码) | 设置HTTP状态码 |
res.write() | res.write() 相当于res.send() 的存储器所有res.write() 里写的东西在调用res.send() 时都会发出去 |
res.readFile()读取文件的地址必须是绝对地址
地址拼接
res.sendFile(__dirname + "/views/index.html");
root参数
res.sendFile("./views/index.html",{
root:__dirname
})
谷歌插件:极简插件
中间件middleware,类似插件
中间件本质上就是函数
⚠中间件的res和req是相通的
const express = require("express");
let app = express();
app.use(中间件名);
⚠next()里面不能传参
let m1 = function (req, res, next) {
// 自定义函数,自定义中间件
console.log("我是m1中间件111start");
req.money = 100;
next();
console.log("我是m1中间件111end");
};
let m2 = function (req, res, next) {
// 自定义函数,自定义中间件
req.money -= 20;
console.log("我是m2中间件222start");
next();
console.log("我是m2中间件222end");
};
遵循洋葱模型
如下例自定义中间件的执行顺序即为
我是m1中间件111start
我是m1中间件222start
我是m1中间件333start
我是m3中间件333end
我是m3中间件222end
我是m3中间件111end
let m1 = function (req, res, next) {
// 自定义函数,自定义中间件
console.log("我是m1中间件111start");
req.money = 100;
next();
console.log("我是m1中间件111end");
};
let m2 = function (req, res, next) {
req.money -= 20;
console.log("我是m2中间件222start");
next();
console.log("我是m2中间件222end");
};
let m3 = function (req, res, next) {
req.money -= 30;
console.log("我是m3中间件333start");
// next(123); // 错误的写法, 把错误传递给错误处理中间件;
next();
console.log("我是m3中间件333end");
};
app.use(m1);
app.use(m2);
app.use(m3);
//由于中间件的res和req是相通的
//此时req.money值为50
捕捉所有中间件的错误,多了一个err参数
let errFn = function (err, req, res, next) {
console.log("错误处理中间件");
if (err) {
return console.log("错误了:", err);
}
next();
};
npm i body-parse
解析为text
将所有请求内容作为字符串来处理
bodyParser.text()
解析为json
bodyParser.json()
解析表单数据
解析来自HTML表单的数据时使用的特殊方法
bodyParser.urlencoded({
extended:true
})
const express = require("express");
let app = express();
// 自定义中间件
// let mybodyparser = require("./mybodyparse.js");
// app.use(mybodyparser);
// 第三方中间件
// 安装 body-parser :
const bodyParser = require("body-parser");
app.use(
bodyParser.urlencoded({
extended: true,
})
);
// app.use(bodyParser.json)
// app.use(bodyParser.text)
app.get("/", (req, res) => {
// 加载post页面
res.sendFile(__dirname + "/views/index.html");
});
app.post("/post", (req, res) => {
console.log(req.body);
res.send("post接收了");
});
app.listen(8989);
nodejs内置模块qs中的parse会将搜索参数转换为对象
const qs = require("querystring");
module.exports = function () {
return function (req, res, next) {
let str = "";
req.on("data", (chunk) => {
str += chunk;
});
req.on("end", () => {
req.body = qs.parse(str);
next();
});
};
};
static中间件用于处理静态文件
express.static //自动读取,写入文件
⚠静态文件首先发送请求读取后再设置,使用express.static需要设置相对于static的地址
const express = require("express");
let app = express();
let static = express.static;
app.use(static(__dirname+"/static"));
//自动把static目录下的内容映射到服务器下面
// http://localhost:8989/a.js
//⚠使用express.static需要设置相对于static(__dirname+"/static")的地址
//如static文件夹下有个a.js文件,在index.html中引入时应为<script src="./a.js"></script>
app.get("/",(req,res)=>{
res.sendFile(__dirname+"/views/index.html");
})
app.listen(8989);
index.js
const express = require("express");
let app = express();
let router = require("./router");
app.use(router);
app.listen(8787);
router.js
const express = require("express");
const router = express.Router();
router.get("/", (req, res) => {
res.send("<h1>index</h1>");
});
router.get("/users", (req, res) => {
res.send("<h1>users</h1>");
});
router.get("/pro", (req, res) => {
res.send("<h1>pro</h1>");
});
module.exports = router;
index.js
⚠设置多级路由,app.use需多加一个父级路由参数,,
app.use("/index", indexrouter);
const express = require("express");
let app = express();
let indexrouter = require("./routers/indexrouter");
let adminrouter = require("./routers/adminrouter");
app.use("/index", indexrouter);
app.use("/admin", adminrouter);
app.listen(8787);
routers
//1.indexrouter.js
const express = require("express");
const router = express.Router();
router.get("/users", (req, res) => {
res.send("<h1>我是index下的users路由</h1>");
});
router.get("/pro", (req, res) => {
res.send("<h1>我是index下的users路由</h1>");
});
module.exports = router;
//2.adminrouter.js
const express = require("express");
const router = express.Router();
router.get("/users", (req, res) => {
res.send("<h1>我是admin下的users路由</h1>");
});
router.get("/pro", (req, res) => {
res.send("<h1>我是admin下的users路由</h1>");
});
module.exports = router;
使html有逻辑,让html拥有js语法
前端js可以使用;nodejs也可使用
npm i ejs
<%= 变量 %>
<% js语法 %>
⚠即使2行相邻都是js代码,也需要每行都包裹
<script src="./node_modules/ejs/ejs.min.js"></script>
ejs.render(模板字符串, { 传入ejs的变量 })
<head>
<script src="./node_modules/ejs/ejs.min.js"></script>
</head>
<body>
<script>
let users = [
{
name: "张三1",
age: 20,
},
{
name: "张三2",
age: 21,
},
];
// let myname = "李四";
let tamplate = `<ul>
<% users.forEach(item=>{ %>
<li>姓名是<%=item.name%> 年龄是<%=item.age %></li>
<% }) %>
</ul>`;
let res = ejs.render(tamplate, { users, myname });
console.log(res);
document.body.innerHTML = res;
</script>
</body>
nodejs使用
ejs文件整体和html文件差不多,除了多了js语法
后端数据传递给前端有2种形式:
ssr
服务器渲染server send render
目前我们利于nodejs和ejs写的就是一种ssr
客户端主动请求数据
js请求数据,用ajax实现
const express = require("express");
let app = express();
// 设置模板引擎的配置
app.set("view engin", "ejs");
//设置使用什么模板引擎
app.set("views", "./views");
//模板所在的目录,默认目录就是views
//"./views"
res.render("要渲染的ejs文件",{ 传入ejs的变量 });
const express = require("express");
let app = express();
app.set("view engin", "ejs");
app.set("views", "./views");
app.get("/", (req, res) => {
let username = "张三";
res.render("users.ejs", { username });
});
app.listen(8989);
持久化保存数据
- 服务端保存(后端
- 文件存储
- 数据库
- session
- 客户端保存(前端
- cookie
- 本地存储 localStorage
- 本地存储sessionStorage
没有设置Max-Age属性,则默认有效期是会话结束时,即浏览器关闭时销毁
设置了Max-Age属性,Max-Age的值即为有效期,单位为秒
cookie在读取时存在访问权限,只能够访问path路径下的cookie
故在网站根目录/
创建的cookie,在该网站的任何位置都能够访问到这个cookie,即path=/
document.cookie = "myname=zhangsan;Max-Age=7200;path=/";
document.cookie
cookie没有过期
相同的浏览器的相同域名下获取
相同域名:同源(协议、域名、端口都一样
同源的页面支持跨页获取
IE存的在IE获取,不能在谷歌获取
获取cookie的路径必须和存储的路径相同,或比它小
cookie只能通过服务器打开页面;如:nodejs服务器、liveserver
将cookie的过期时间改为0或-1即可,刷新后过期的cookie会自动删除
document.cookie = "myname='';Max-Age=-1;";
借助第三方模块,cookie-parser
npm i cookie-parser --save
let cookieParser = require("cookie-parser");
app.use(cookieParser());
⚠maxAge过期时间单位为毫秒
res.cookie("键名","键值",{
maxAge:7200*1000 , // 单位毫秒
httpOnly:true, // 设置 cookie 只能服务器操作
});
req.cookies
res.clearCookie("键名");
cookie的缺点主要集中于安全性和隐私保护
⚠面试问到cookie时会一起问,两者区别
和cookie一样也是一种记录客户状态的机制
session | cookie | |
---|---|---|
存储位置 | 服务端 | 客户端 |
依赖关系 | session需要借助cookie记录一个sessionid 如果人为把cookie禁用,session也会失效 | —— |
安全 | 更安全 | —— |
存储量 | 理论上不限大小 | 大小一般4k左右,每个域名可以存50个左右 |
网络资源消耗 | 相对少 | 会随着http请求发送给服务端,比较消耗网络资源 |
npm i express-session --save
let session = require("express-session");
app.use(session({
name: 'id22', //设置cookie的name,默认值是:connect.sid
secret: 'atguigu', //参与加密的字符串(又称签名)
saveUninitialized: false, //是否为每次请求都设置一个cookie用来存储session的id
resave: true ,//是否在每次请求时重新保存session
cookie:{
// cookie配置项
maxAge:7200*1000,
httpOnly:true
}
}));
session的所有操作都基于req
⚠设置session后不要重启服务器,否则session会重置,导致无法获取
req.session.键名 = "键值"
req.session.myname = "张三";
req.session.键名
req.session.myname;
req.session.destroy();
const express = require("express");
const session = require("express-session");
let app = express();
app.use(session({
// 配置项
name:"mysessionid",
secret:"mysecret",
resave:true,
saveUninitialized :true,
cookie:{
maxAge:7200*1000,
httpOnly:true
}
}))
// 设置session
app.get("/setSession",(req,res)=>{
req.session.myname = "张三";
res.send("设置session")
})
// 获取session
app.get("/getsession",(req,res)=>{
let myname = req.session.myname;
res.send("获取session"+myname);
})
// 删除所有session
app.get("/clear",(req,res)=>{
req.session.destroy();
res.send("删除session");
})
app.listen(8989);
把数据存储在本地浏览器上,基于服务器打开页面,同源可访问
localStorage没有时效性,只要不手动清除,永远会在浏览器中
localStorage.setItem("键名","键值");
localStorage.getItem("键名");
localStorage.removeItem("键名");
localStorage.clear();
将复杂数据类型如对象存入localStorage,会自动转换为字符串再存入
因此,需要将复杂数据转为json再存储
btn5.onclick = function(){
let obj = {name:"张三"};
//存储复杂数据
localStorage.setItem("data",JSON.stringify(obj));
//读取复杂数据
console.log(JSON.parse( localStorage.getItem("data")))
}
sessionStorage的销毁时间是浏览器关闭的时间,无法更改
sessionStorage.setItem("键名","键值");
sessionStorage.getItem("键名");
sessionStorage.removeItem("键名");
sessionStorage.clear();
如注册内容不止一页
返回上一页时刚刚填的内容还在(即通过sessionStorage进行设置)
但关闭浏览器后内容销毁
// 通过sessionStroage 缓存 注册信息 ;
let unameEle = document.querySelector("#uname");
if (sessionStorage.getItem("uname")) {
unameEle.value = sessionStorage.getItem("uname");
}
let btn = document.querySelector("button");
btn.onclick = function () {
let uname = unameEle.value;
sessionStorage.setItem("uname", uname);
// location localhost
window.location.href = "./register2.html";
};
第1、2步只需配置一次,后续只需连接数据库即可
下载安装
www.mangodb.com - products - community server - Download
本地建立一个数据文件夹
用于放置数据库数据,后期连接数据库要用
配置文件(以下基于命令行操作
cd到第2步建立的数据文件夹
touch .bash_profile
vim .bash_profile
进入vim文本编辑器
单击“i”,以启动插入模式
输入以下代码(6.0改为自己下载的版本)
alias mongod="/c/Program\\ files/MongoDB/Server/6.0/bin/mongod.exe"
alias mongos="/c/Program\\ Files/MongoDB/Server/6.0/bin/mongos.exe"
单击“Esc”退出插入模式
输入:wq!
并回车保存并退出vim
检查是否安装成功
mongod --version
⚠输入
mongod --version
后显示没有该命令——需要配置环境变量
右击此电脑,出现属性选项
属性–>高级系统设置–>环境变量–>系统变量的path
新建输入mongodb的bin文件夹绝对地址
C:\Program Files\MongoDB\Server\6.0\bin
全部确定后,重启命令行
再次输入
mongod --version
mongosh:让我们能够使用命令行与本地系统上的mongoDB数据库进行交互的一种方式
下载地址
https://www.mongodb.com/try/download/shell
下载指南
https://www.mongodb.com/docs/mongodb-shell/install/
mongodb的数据以文件形式存在,需先连接文件,才能操作数据
以下基于命令行操作:
cd到数据文件夹
输入mongod
出现以下代码即表示连接成功
{ "t" :{ "$date" : "2022-08-19T22:29:25.840-07:00" }, "s" : "I" , "c" : "NETWORK" , "id" : 46887 , "ctx" : "listener" , "msg" : "等待连接" , "attr" :{ "port" : 27017 , "ssl" : "off" }}
http://127.0.0.1:27017
It looks like you are trying to access MongoDB over HTTP on the native driver port.
说明成功连接打开新的命令行页面
在新命令行输入mongosh
出现test>
或>
即可
MongoDB所有操作
https://docs.mongodb.com/manual/reference/method
MongoDB的结构:数据库–>集合–>数据
说明 | |
---|---|
show dbs | 查看数据库 |
use 数据库名称 | 使用数据库/选中数据库 |
db | 查看当前选中的数据库 |
1. use 数据库名称 2.db.集合名称.insertOne() | 创建数据库 向一个不存在的数据库添加数据即可创建数据库 |
db.dropDatabase() | 删除当前选中的数据库(选中数据库后才能删除) |
⚠选中进入数据库后,才能对集合进行操作
说明 | |
---|---|
show collections | 查看集合 |
db.createCollecion(“集合名称”) | 创建空集合 |
db.集合名称.drop() | 删除集合 |
数据CRUD操作
https://www.mongodb.com/basics/crud
增 | 说明 |
---|---|
db.集合名称.insertOne() | 向指定集合添加一个数据 |
db.集合名称.insertMany() | 向指定集合添加多个数据 |
删 | |
db.集合名称.deleteOne({条件}) | 删除指定集合中符合条件的第一个数据 |
db.集合名称.deleteMany({条件}) | 删除指定集合中符合条件的所有数据 |
改 | |
db.集合名称.updateOne({条件}, {$set: {修改内容}}) | 修改指定集合中符合条件的第一个数据的内容 (如果原数据没有该键名,则添加新键值对) |
db.集合名称.updateMany({条件}, {$set: {修改内容}}) | 修改指定集合中符合条件的所有数据的内容 (如果原数据没有该键名,则添加新键值对) |
db.集合名称.replaceOne({条件}, {替换数据}) | 替换符合条件的第一个数据 |
查/读取 | |
db.集合名称.find({条件}) | 查找指定集合中符合条件的所有数据 若条件为空,则为获取指定集合中所有数据 |
db.集合名称.findOne({条件}) | 查找指定集合中符合条件的第一个数据 |
db.集合名称.find().limit(num) | 限制返回条数 |
db.集合名称.find().pretty(); | 美化返回结果 |
db.集合名称.sort({键名: num}) | 根据键名将结果排序后再返回 正序排序:num为1 倒序排序:num为-1 |
db.集合名称.find().skip(num) | 跳过前n个数据查询获取 |
使用find()时,可以通过配置项指定返回哪些字段/键值对
db.集合名称.find({条件},{配置项})
//只返回_id和 name 字段
db.crud.find({name: 'Tom'}, {name: 1});
//不返回_id
db.crud.find({name: 'Tom'}, {_id: 0, name: 1});
设置配置项后,配置1的字段会显示,其他字段不显示
_id例外,默认显示,配置0后不显示
db.集合名.find({name:"张三"})
⚠只能查询数字类型的字段
⚠需要用花括号括起来
说明 | |
---|---|
$gt | 大于 |
$lt | 小于 |
$ne | 不等于 |
$eq | 等于 |
$gte | 大于等于 |
$lte | 小于等于 |
//查询年龄大于20岁的数据
db.集合名.find({age:{$gt:20}});
条件可以使用正则
//查询所有姓张的用户
db.集合名.find({name:/^张.+/})
db.集合名.find({age:{条件一,条件二}})
//查询所有年龄大于20 且 小于 25的用户
db.集合名.find({age:{$gt:20,$lt:25}});
db.集合名.find({$or:[{age:{条件一}},{age:{条件二}}]})
//查询年龄小于20 或者年龄大于25的用户
db.集合名.find({$or:[{age:{$lt:20}},{age:{$gt:25}}]});
官网:https://mongoosejs.com/
guide文档:https://mongoosejs.com/docs/index.html
nodejs操作数据库
npm i mongoose --save
mongoose.connect()返回一个promise
const mongoose = require("mongoose");
(async function () {
try {
await mongoose.connect('mongodb://127.0.0.1:27017/要连接or创建的数据库名称');
//如果数据库需要验证,使用如下代码
//await mongoose.connect('mongodb://user:password@localhost:27017/test');
console.log("success");
} catch (err) {
console.log(err);
}
})();
设置数据的格式
let schema = mongoose.Schema({
name: String,
age: Number,
gender: String,
});
let usermodel = mongoose.model("集合名(复数)", 为此集合的数据创建的schema);
一个模型对应一个集合
需要创建集合 ,集合名称必须是**复数形式**
如users
所有模型操作都会返还一个promise对象
模型所有操作
https://mongoosejs.com/docs/api/model.html
增 | 说明 |
---|---|
模型名称.create() | 添加一个数据 |
模型名称.insertMany() | 添加多个数据 |
删 | |
模型名称.deleteOne({条件}) | 删除符合条件的第一个数据 |
模型名称.deleteMany({条件}) | 删除符合条件的所有数据 |
改 | |
模型名称.updateOne({条件}, {修改内容}) | 修改符合条件的第一个数据的内容 |
模型名称.updateMany({条件}, {修改内容}) | 修改所有符合条件的数据的内容 |
模型名称.replaceOne({条件}, {替换数据}) | 替换符合条件的第一个数据 |
查/获取 | |
模型名称.find({条件}) | 获取符合条件的所有数据 条件为空,返回所有数据 |
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。