当前位置:   article > 正文

nodejs学习文档_nodejs文档

nodejs文档

Nodejs

Node.js前言

Node.js简介

  • nodejs就是js
  • JavaScript运行在客户端,而nodejs运行在服务端
  • nodejs也是一门后端语言

为什么学Node.js

  1. mock数据 模拟数据,实现前后端分离
  2. 为前后端交互做铺垫
  3. 前端工程化打基础(前端的开发环境基于nodejs环境)

安装nodejs

  • 安装nodejs:下载低版本(稳定版)
  • 查看nodejs是否安装好:node -v

运行nodejs

  1. 方式一:通过交互式解释器repl(较少用

    • 类似前端的console

      在 nodejs 环境下,不能使用 BOM 和 DOM ,也没有全局对象 window

      全局对象的名字叫 global

      • DOM(完全没有)
      • BOM(干掉了绝大部分,只保留少部分)
        • console.log
        • setInterval
        • setTimeout
    • 使用

      • 启动repl环境:命令行输入node

      • 退出repl环境:ctrl+c 按2次

  2. 方式二:执行js文件(推荐

    1. 命令行进入目标文件夹
    2. 使用node运行文件 node ./index.js

vscode配置node提示

  1. 安装提示模块 npm i --save-dev @types/node

  2. 在项目根目录下 新建config.js输入如下内容

    {
      compilerOptions: {
        types: ["node"]
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

Nodejs基础

使用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); // 设置浏览器访问服务器的端口号 ;
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
启动服务器

命令行输入node

关闭服务器

ctrl+c 按2次

访问自建服务器
内部访问
  1. http://localhost:8989
  2. http://127.0.0.1:8989
外部访问

http://ip地址:8989

端口命名建议
  1. 最好使用1024以上的端口

    以下的多数为默认配置

  2. 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, 
    
    • 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
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80

commonjs

文件导入导出
文件导入
require("文件路径path")
  • 1
文件导出
module.exports = 导出内容
  • 1
  1. 导出内容可以是各种格式:变量,对象等
  2. require("文件路径path")的返回值即为导出内容
commonjs规范引入文件模块

nodejs里的模块化规范——commonjs规范

commonjs规范的3种格式:1.文件模块;2.目录模块;3.特殊目录模块

文件模块

把文件作为模块使用

⚠导入时路径的./不可缺

目录模块

⚠目录名不能是中文

目录模块引入

⚠导入时路径的./不可缺

  1. 方式一:通过文件路径引入目录模块

    require("./amodule/index.js");
    
    • 1
  2. 方式二:直接引入目录模块

    默认单入口就是index.js

    let {hello} =  require("./amodule");
    
    • 1
改变目录模块单入口文件
  1. 进入到模块内创建一个文件叫package.josn文件
    • 命令行创建package.json文件npm init
    • 快速创建package.json文件,所有的选项都是yes npm init -y
  2. 修改package.json文件里的main配置为指定的主入口文件
特殊目录模块node_modules???

nodejs里的模块种类

  1. 内置模块(nodejs自己安装的模块)

  2. 自定义模块(文件模块、目录模块)

  3. .第三方模块

    一般都会下载到 特殊目录里 ,这个目录就是node_modules;

引入特殊目录里的模块
  1. 方式一:通过路径查找模块名称

    let res = require("./node_modules/mymodule/index");
    console.log(res);
    let res = require("./node_modules/mymodule");
    console.log(res);
    
    • 1
    • 2
    • 3
    • 4
  2. 方式二:直接引入模块名称

    1. 在特殊目录node_modules里的模块不需要加./

    2. node_modules引入会自动向目录上层查找

      在当前文件夹没有找到模块,会继续将上一层目录找模块,直到找到根目录C盘

      let res = require("mymodule");
      
      • 1
    3. 一般情况下node_modules 模块,都不被git管理起来

      模块很大,同步时间会增长,且模块显示可下载)

      .gitignore文件里面可以配置不被git管理起来的文件或者目录(文件里写入node_modules即可)

commonjs拓展

  1. commonjs可以引入js文件,也可以引入.json和.node后缀的文件

    let data = require("./a");
    //会先找后缀为js的a文件,再找.json,再找.node,如果都没有就报错
    
    • 1
    • 2
  2. commonjs会自动把json字符串转成数组或者对象;

    let data = require("./a.json");
    
    • 1
nodejs显示列表
  • 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);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
  • a.json

    [
      {
          "id": 1,
          "title": "标题一",
          "content": "内容一"
      },
      {
          "id": 2,
          "title": "标题2",
          "content": "内容2"
      }
    ]
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

npm

node package manager 包管理器

查看电脑npm是否安装好:npm -v

npm下载

node_modules 一般放第三方模块 (下载)

  • 下载地址

    • 官方下载服务器 :https://registry.npmjs.org/

    • 淘宝源地址 : https://registry.npmmirror.com

  • 查看npm下载源地址

    npm get registry
    
    • 1
  • 把官方下载源改成淘宝源地址

    npm config set registry https://registry.npmmirror.com
    
    • 1
  • npm 包查找:https://www.npmjs.com/

npm 安装
一般安装
  1. 初始化一个package.json文件 ,用于记录安装包的信息(目标目录没有package.json才需要初始化)

    npm init
    
    • 1

    上层目录的名称不能是中文

  2. 安装模块

    npm install 包名
    
    • 1
一次性安装所有模块
npm install
//简写
npm i
  • 1
  • 2
  • 3

查找指令所在目录的下的package.json文件 把package.json文件里的依赖自动下载安装 ;

全局安装&局部安装
局部安装

默认局部安装,模块会安装在命令运行的目录

全局安装
npm i 包名 -g
  • 1
  • 查看全局安装的目录

    npm root -g

    如果没有全局安装过内容 这个目录找不到 ;

  • 全局安装自动刷新node服务器工具

    npm i nodemon -g

    使用:nodemon ./inde.js

开发依赖&生产依赖
开发依赖

只会在开发时使用的依赖 ,正式上线时不要的依赖(如Less)

因此该依赖包只在开发过程中存在,正式上线时该依赖包会删除

会显示在package.json的 devDependencies选项里

npm i 包名 --save-dev
//简写
npm i 包名 --D
  • 1
  • 2
  • 3
生产依赖/正式依赖

生成和开发时都需要用到的依赖,如vue 、react

默认安装是生成依赖

npm i 包名 --save
//简写
npm i 包名 --S
  • 1
  • 2
  • 3
安装指定版本依赖
npm i 包名@版本号
  • 1

如果不指定安装版本 默认会安装最新版本

npm 删除安装包
npm uninstall 包名
  • 1
发布自己的包
  • 第一步 : 把npm的源地址改成官方源;

    • npm config set registry https://registry.npmjs.org/
  • 第二步: https://www.npmjs.com/ 网站注册用户名密码

    • 需要邮箱验证;
  • 第三步 : 添加用户名和密码到npm配置里 ,只需要添加一次;

    npm adduser;

  • 第四步 : 创建自己的包

    注意点

    1. 包名不能是中文

    2. npmjs上不能有重复的名称

    • 初始化package.json
    • 写自己的代码
    • 发布自己的包,推送到npmjs.com服务器里
      • 首先保证在包文件夹内
      • npm publish
  • 第五步: 删除包

    • 进入 模块内 : npm unpublish --force;
yarn-npm代替

项目原来用的哪个就用哪个,如用npm就用npm

npm代替品

  1. yarn

    facebook出品

  2. cnpm

    • china npm
    • 一般不使用,会把npm的源改为cnpm
  3. cyarn

  • 全局安装yarn npm i yarn -g;
  • 查看yarn的版本号 yarn --version;
指令yarnnpm
初始化package.json文件yarn initnpm init
安装模块(默认生成依赖)yarn add 模块名npm i 模块名
安装开发依赖yarn add 模块名 --devnpm i 模块名 --save-dev
安装所有依赖yarnnpm i
全局安装yarn global add 模块名
⚠不要写成 yarn add global 模块名
npm i 模块名 -g
全局安装路径yarn global dirnpm root -g
删除模块yarn remove 模块名npm uninstall 模块名

nodejs内置模块

buffer缓冲区

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
  1. process.argv返回值是一个有2个参数的数组

    • 第一个参数:nodejs的绝对路径

      process.argv[0]

    • 第二个参数:执行文件的绝对路径

  2. ❗process.argv可传第三个参数

    node ./index.js 第三个参数
    
    • 1

    第三个参数可以用来区分执行,见下例

    if(process.argv[2]==="production"){
        console.log("执行生产模式的代码");
    }else{
        console.log("执行开发模式的代码");
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
path内置模块

处理文件路径

引入模块
const path = require("path")
  • 1
解析为绝对路径

将参数从右往左向前拼接到一起,并返回绝对路径

path.resolve(path1,path2...)
  • 1

拼接的几种情况:

  1. 字符以 / 开头,不会拼接到前面的路径

    path.resolve('/foo/bar', '/baz') 
    结果: '/baz'
    因为 '/baz' 已经是一个绝对路径  故不会再向前拼接
    
    • 1
    • 2
    • 3
  2. 字符以 ./ 开头or没有符号,正常拼接前面路径

    如果是第一个参数开头为./,前面会拼接当前工作目录的绝对路径

    path.resolve('/foo/bar', './baz')  
    结果:'/foo/bar/baz'
    
    • 1
    • 2
  3. 字符以 …/ 开头,跳过前面路径的最后一节路径拼接

    path.resolve('/foo/bar', '../baz')  
    结果: '/foo/baz'
    拼接前面路径  '/foo/bar'  但是不包含前面路径最后 '/bar'  这部分
    
    • 1
    • 2
    • 3
  4. 没有传入参数,则 path.resolve() 将返回当前工作目录的绝对路径

路径拼接

将所有参数拼接到一起后返回

path.join(path1,path2...)
  • 1
  1. ../会抵消前面的路径

    const pathStr = path.join('/a','/b/c','../','./c','/d')
    console.log(pathStr);  //   \a\b\c\d
    
    • 1
    • 2
url内置模块(已弃用

处理url路径,已弃用的旧版url

const url = require("url");
  • 1
URL类❗

https://nodejs.cn/api/url.html#the-whatwg-url-api

和旧版url内置模块不同,无需引入node自带的url模块即可使用

创建URL类实例对象
const myURL = new URL('https://example.org:8989/test?id=1');
const myURL = new URL('/foo', 'https://example.org/');
// https://example.org/foo
  • 1
  • 2
  • 3
URL类实例方法

必须实例对象才能用

说明
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对象)
fs内置模块

文件系统 file system

可以操作文件或目录

引入模块
const fs = require("fs")
  • 1
文件操作

文件操作都分2种

  1. 同步操作(所有同步操作后面都有sync
  2. 异步操作(通过回调来实现
文件操作同步执行异步执行
创建文件fs.writeFileSync("新文件及其路径", 新文件中的数据)fs.writeFile"新文件及其路径", 新文件中的数据, err=>{})
读取文件fs.readFileSync("文件及其路径")fs.readFile("文件及其路径", err=>{})
重命名文件fs.renameSync("文件及其路径", 新文件名)fs.rename("文件及其路径", 新文件名, err=>{})
删除文件fs.unlinkSync("文件及其路径")fs.unlink("文件及其路径", err=>{})
判断文件是否存在fs.existsSync("文件及其路径")——————
读取文件
  1. 同步读取

    try {
      let data = fs.readFileSync("./data.json");
      let mydata = JSON.parse(data.toString());
      console.log(mydata[0].name);
    } catch (err) {
      console.log(err);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
  2. 异步读取

    fs.readFile("./1.txt", (err, data) => {
      if (err) {
        return console.log(err);
      }
      console.log(data.toString());
    });
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
异步的回调使用
fs.unlink("./1.txt",err=>{
    if(err){
        return console.log(err);
    }
    console.log("删除成功");
})
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
mypromisify的实现

将异步文件操作函数包装成一个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")
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
目录操作
目录操作同步异步
创建一个目录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");
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21

内存溢出/爆仓 & 内存泄漏 的区别

  • 内存溢出

    程序在申请内存时,没有足够的内存空间供其使用

  • 内存泄漏

    程序在申请内存后,无法释放已申请的内存空间(这块内存不释放,就不能再用了,就叫这块内存泄漏了)

    指在程序中分配了内存空间,但在不再需要使用该内存空间时没有及时释放或释放不完全。这导致这部分内存无法再被程序使用,最终导致系统的可用内存逐渐减少。如果内存泄漏持续发生,最终可能导致系统崩溃或运行缓慢。常见的内存泄漏情况包括未释放的堆内存、循环引用、未清理的定时器或事件监听器等。

流 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)
})
  • 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
简易爬虫

npm包——cheerio

https://github.com/cheeriojs/cheerio

  1. 安装cheerio

    npm install cheerio

  2. 使用的第一步——使用cheerio加载HTML

    一般直接将其赋值为 (以下以 (以下以 (以下以表达)

    const $ = cheerio.load(HTML文档)

  3. 在文档中查找元素,语法同css选择器

    $(selector)

  4. 获取元素的文本 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));
    })
})
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
接收post数据

实现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);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23

nodejs事件系统

作用:解耦

emit可传参,参数传到事件对象里

  • 预定义事件:data、end
  • 自定义事件:观察者模式
引入内置events模块
const EventEmitter = require("events");
let obj = new EventEmitter();
  • 1
  • 2
说明
obj.addListener("事件名",()=>{事件函数})绑定自定义事件
obj.on("事件名",()=>{事件函数})绑定自定义事件
obj.once("事件名",()=>{事件函数})绑定一次性事件
绑定的事件只能被触发一次
obj.emit("事件名");
obj.emit("事件名", arg)
触发事件
obj.once("事件名",事件函数)解除绑定事件
obj.removeListener("事件名",事件函数)解除绑定事件
obj.removeAllListeners()移除所有事件
obj.listenerCount("事件名")查看监听事件数量
绑定自定义事件
addListener绑定
obj.addListener("myevent",()=>{
    console.log("hello")
})
obj.addListener("myevent",()=>{
    console.log("hello2222")
})
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
on绑定
obj.on("myevent",e=>{
    console.log("hello",e);
})
obj.on("myevent",e=>{
    console.log("hello222",e);
})
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
绑定一次性事件
obj.once("myevent",()=>{
    console.log("hello");
})
obj.once("myevent",()=>{
  console.log("hello22");
})
obj.emit("myevent")//hello  hello22
obj.emit("myevent")
obj.emit("myevent")
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
触发事件
obj.emit("事件名");
obj.emit("事件名", arg)
  • 1
  • 2
emit可传参数
obj.on("myevent",arg=>{
  console.log("hello",arg);
})
setTimeout(() => {
  obj.emit("myevent","arg");
}, 1000);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
解除绑定事件
off解绑
var fn2 = function(){
    console.log("fn2");
}
obj.on("myevent",fn2)
obj.off("myevent",fn2);
  • 1
  • 2
  • 3
  • 4
  • 5
removeListener解绑
var fn1 = function(){
    console.log("fn1");
}
obj.on("myevent",fn1)
obj.removeListener("myevent",fn1);
  • 1
  • 2
  • 3
  • 4
  • 5
移除所有事件
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");
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
查看监听事件数量
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);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
应用-解耦

解除多个类或模块间的耦合

  1. 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);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
  2. 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();
        })
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

获取相对的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);
  • 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

http协议

HTTP协议

HTTP协议

超文本传输协议 (网络协议)Hyper Text Transfer Protocol ,是一种约定

https是http的升级版,做了加密

HTTP+加密+认证+完整性保护 = HTTPS

报文

在这里插入图片描述

报文的4个部分
  1. 请求or返还首行

  2. 请求or返还头部信息(header)

    1. 请求头的Content-Type(form表单

      enctype="application/x-www-form-urlencoded"

      需记忆,后期使用ajax需要自己设置

  3. 请求or返还空行

  4. 请求or返还体(body)

    1. 请求体:传递给服务器的参数 (post)
    2. 返还体:HTML
请求报文

在这里插入图片描述

返还报文

在这里插入图片描述

通过浏览器查看http信息

每个都是一个http请求,包括请求报文和返还报文

在这里插入图片描述

重要的报文信息
返还头部的Content-Type

影响返还体的格式和编码

res.setHeader("Content-Type", "text/html;charset=utf-8");
  • 1
1.文件类型

遵循MINE标准

2.编码格式
method
method说明
get获取数据
post添加数据
put替换更新
patch局部更新
delete删除操作
head头部请求
options预检请求
状态码status code
说明
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);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
preview

预览返还体内容,相当于innerHTML

response

返还体内容,相当于innerText

payload

网址后带参数时,可查询参数

http处理静态资源

文件不是相对于加载的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);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

网络协议

七层网络协议OSI
  1. 物理层 :利用物理传输介质为数据链路层提供物理连接。
  2. 数据链路层 :将IP数据报加头加尾构成帧,通常头部加物理地址等信息,尾部加差错检测等信息。通过差错控制、流量控制等方法,实现数据在不可靠的物理线路上的可靠传递。
  3. 网络层 :负责为分组交换网上的不同主机提供通信服务。为了区分哪些MAC地址属于同一个子网,网络层定义了IP和子网掩码,通过对IP和子网掩码进行与运算就知道是否是同一个子网,再通过路由器和交换机进行传输。
  4. 传输层 :为上层协议提供端到端的可靠传输;TCP、UDP 有了网络层的MAC+IP地址之后,为了确定数据包是从哪个进程发送过来的,就需要端口号,通过端口来建立通信。
  5. 会话层 :建立、断开和维护通信链接
  6. 表示层 :数据格式转换、数据压缩和数据加密 。
  7. 应用层 :为应用程序提供网络服务;最高层,面对用户,提供计算机网络与最终呈现给用户的界面。
层次模型作用数据单元主要设备
应用层网络服务与最终用户的一个接口message
表示层数据的表示、压缩、格式化、加密message
会话层建立、管理、中止会话message
传输层定义传输数据的协议端口号,以及流量和差错校验数据段
网络层进行逻辑地址寻址,实现不同网络之间的路径选择数据包路由器
数据链路层建立逻辑连接,进行硬件地址寻址,差错校验等功能数据帧交换机
物理层建立、维护、断开物理连接比特流网卡
TCP/IP参考模型
  1. 网络接口层(数据链路层):他包含了OSI模型的物理层和数据链路层,把电脑连接起来。
  2. 网络层,也叫做IP层,处理IP数据包的传输、路由,建立主机间的通信。
  3. 传输层,就是为两台主机设备提供端到端的通信。TCP UDP
  4. 应用层,包含OSI的会话层、表示层和应用层,提供了一些常用的协议规范,比如FTP、SMPT、HTTP、DNS等
TCP & OSI对应关系

在这里插入图片描述

http协议

Http协议是建立在TCP协议基础之上的,当浏览器需要从服务器获取网页数据的时候,会发出一次Http请求。Http会通过TCP建立起一个到服务器的连接通道,当本次请求需要的数据完毕后,Http会立即将TCP连接断开,这个过程是很短的,所以Http连接是一种短连接,是一种无状态的连接。

tcp三次握手

三次握手是网络客户端跟网络服务器之间建立连接,并进行通信的过程。相当于客户端和服务器之间 你来我往的3个步骤。

第一次握手是建立连接,客户端发送连接请求报文,并传送规定的数据包;

第二次握手是服务器端表示接收到连接请求报文,并回传规定的数据包;

第三次握手是客户端接收到服务器回传的数据包后,给服务器端再次发送数据包。这样就完成了客 户端跟服务器的连接和数据传送。

在这里插入图片描述

tcp四次挥手

四次挥手表示当前这次连接请求已经结束,要断开这次连接。

第一次挥手是客户端对服务器发起断开请求,

第二次握手是服务器表示收到这次断开请求,

第三次握手是服务器表示已经断开连接

第四次握手是客户端断开连接。

在这里插入图片描述

Express

express:nodejs极简框架

基于 Node.js 平台,快速、开放、极简的 Web 开发框架

  • Express 框架核心特性:

    • 可以设置中间件来响应 HTTP 请求。
    • 定义了路由表用于执行不同的 HTTP 请求动作。
    • 可以通过向模板传递参数来动态渲染 HTML 页面

express基本使用

安装express
npm install express --save
  • 1
通过express构建服务
说明
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("服务器启动成功");
});
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
app常用参数

以下是一些常用的Express方法:

  1. express(): 创建一个Express应用实例。
  2. app.use(): 注册中间件函数,用于处理HTTP请求。
  3. app.get(): 注册一个处理GET请求的路由处理函数。
  4. app.post(): 注册一个处理POST请求的路由处理函数。
  5. app.put(): 注册一个处理PUT请求的路由处理函数。
  6. app.delete(): 注册一个处理DELETE请求的路由处理函数。
  7. app.all(): 注册一个处理所有HTTP方法的路由处理函数。
  8. app.route(): 创建一个处理特定路径的路由处理链。
  9. app.param(): 注册一个路由参数处理函数。
  10. app.listen(): 启动Express应用并监听指定的端口。
  11. app.set(): 设置应用的配置参数。
  12. app.get(): 获取应用的配置参数。
  13. app.render(): 渲染视图模板。
  14. app.locals(): 设置和获取应用级别的本地变量。
  15. res.send(): 发送HTTP响应。
  16. res.json(): 发送JSON格式的HTTP响应。
  17. res.render(): 渲染视图模板并发送HTTP响应。
  18. res.redirect(): 发送HTTP重定向响应。
  19. res.sendFile(): 发送文件作为HTTP响应。
  20. req.params: 获取路由参数。
  21. req.query: 获取查询参数。
  22. req.body: 获取请求体参数。
  23. req.cookies: 获取请求中的Cookie。
  24. req.session: 获取或设置会话数据。
  25. req.headers: 获取请求头信息。
req对象常用参数
说明
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对象常用参数
说明
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.readFile()读取文件的地址必须是绝对地址

  1. 地址拼接

    res.sendFile(__dirname + "/views/index.html");
    
    • 1
  2. root参数

    res.sendFile("./views/index.html",{
      root:__dirname
    })
    
    • 1
    • 2
    • 3

谷歌插件:极简插件

express中间件

中间件middleware,类似插件

中间件本质上就是函数

⚠中间件的res和req是相通的

中间件基本使用
执行中间件 use
const express = require("express");
let app = express();
app.use(中间件名);
  • 1
  • 2
  • 3
调用下一个中间件 next()

⚠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");
};
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
多个中间件执行顺序

遵循洋葱模型

在这里插入图片描述

如下例自定义中间件的执行顺序即为

我是m1中间件111start
我是m1中间件222start
我是m1中间件333start
我是m3中间件333end
我是m3中间件222end
我是m3中间件111end
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
自定义中间件
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
  • 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
错误处理中间件

捕捉所有中间件的错误,多了一个err参数

let errFn = function (err, req, res, next) {
  console.log("错误处理中间件");
  if (err) {
    return console.log("错误了:", err);
  }
  next();
};
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
第三方中间件-bodyparser
安装bodyparser
npm i body-parse
  • 1
bodyparser的3种模式
  1. 解析为text

    将所有请求内容作为字符串来处理

    bodyParser.text()
    
    • 1
  2. 解析为json

    bodyParser.json()
    
    • 1
  3. 解析表单数据

    解析来自HTML表单的数据时使用的特殊方法

    bodyParser.urlencoded({
        extended:true
    })
    
    • 1
    • 2
    • 3
实例
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);
  • 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
自定义bodyparser中间件

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();
    });
  };
};
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
内置中间件
static中间件

static中间件用于处理静态文件

express.static //自动读取,写入文件
  • 1

⚠静态文件首先发送请求读取后再设置,使用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);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
Router路由中间件
设置一级路由
  1. index.js

    const express = require("express");
    let app = express();
    let router = require("./router");
    app.use(router);
    app.listen(8787);
    
    • 1
    • 2
    • 3
    • 4
    • 5
  2. 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;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
设置多级路由
  1. 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);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
  2. 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;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

ejs模块引擎

使html有逻辑,让html拥有js语法

前端js可以使用;nodejs也可使用

ejs基本使用
安装ejs
npm i ejs
  • 1
显示变量 <%=
<%= 变量 %>
  • 1
js语法包裹 <%
<% js语法 %>
  • 1

⚠即使2行相邻都是js代码,也需要每行都包裹

前端使用ejs
前端引入ejs
<script src="./node_modules/ejs/ejs.min.js"></script>
  • 1
前端ejs渲染
ejs.render(模板字符串, { 传入ejs的变量 })
  • 1
实例
<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>
  • 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
后端使用ejs

nodejs使用

ejs文件整体和html文件差不多,除了多了js语法

后端数据传递给前端有2种形式:

  1. ssr

    服务器渲染server send render

    目前我们利于nodejs和ejs写的就是一种ssr

  2. 客户端主动请求数据

    js请求数据,用ajax实现

后端引入ejs
const express = require("express");
let app = express();
// 设置模板引擎的配置
app.set("view engin", "ejs"); 
//设置使用什么模板引擎
app.set("views", "./views"); 
//模板所在的目录,默认目录就是views
//"./views"
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
后端ejs渲染
res.render("要渲染的ejs文件",{ 传入ejs的变量 });
  • 1
实例
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);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

cookie & session

持久化保存数据

  1. 服务端保存(后端
    1. 文件存储
    2. 数据库
    3. session
  2. 客户端保存(前端
    1. cookie
    2. 本地存储 localStorage
    3. 本地存储sessionStorage

cookie

cookie常用属性
过期时间Max-Age

没有设置Max-Age属性,则默认有效期是会话结束时,即浏览器关闭时销毁

设置了Max-Age属性,Max-Age的值即为有效期,单位为秒

路径path

cookie在读取时存在访问权限,只能够访问path路径下的cookie

故在网站根目录/创建的cookie,在该网站的任何位置都能够访问到这个cookie,即path=/

前端操作cookie
设置cookie
document.cookie = "myname=zhangsan;Max-Age=7200;path=/";
  • 1
获取cookie
document.cookie
  • 1
获取条件
  1. cookie没有过期

  2. 相同的浏览器的相同域名下获取

在这里插入图片描述

  • 相同域名:同源(协议、域名、端口都一样

  • 同源的页面支持跨页获取

  • IE存的在IE获取,不能在谷歌获取

  1. 获取cookie的路径必须和存储的路径相同,或比它小

  2. cookie只能通过服务器打开页面;如:nodejs服务器、liveserver

删除cookie

将cookie的过期时间改为0或-1即可,刷新后过期的cookie会自动删除

document.cookie = "myname='';Max-Age=-1;";
  • 1
后端操作cookie

借助第三方模块,cookie-parser

安装cookie-parser
npm i cookie-parser --save
  • 1
引入使用cookie-parser
let cookieParser = require("cookie-parser");
app.use(cookieParser());
  • 1
  • 2
设置cookie

⚠maxAge过期时间单位为毫秒

res.cookie("键名","键值",{
    maxAge:7200*1000 ,  // 单位毫秒
    httpOnly:true,  // 设置 cookie 只能服务器操作
});
  • 1
  • 2
  • 3
  • 4
获取cookie
req.cookies
  • 1
删除cookie
res.clearCookie("键名");
  • 1
cookie的缺点

cookie的缺点主要集中于安全性和隐私保护

  1. cookie可能被禁用
  2. cookie不能跨浏览器
  3. cookie可能被删除
  4. cookie安全性不够高
  5. 在浏览器向服务器发送请求时,cookie会随着请求一同发送给服务器,会影响请求和响应的速率
  6. cookie大小一般为4k左右,每个域名可以存50个左右

session

⚠面试问到cookie时会一起问,两者区别

和cookie一样也是一种记录客户状态的机制

session与cookie的区别
sessioncookie
存储位置服务端客户端
依赖关系session需要借助cookie记录一个sessionid
如果人为把cookie禁用,session也会失效
——
安全更安全——
存储量理论上不限大小大小一般4k左右,每个域名可以存50个左右
网络资源消耗相对少会随着http请求发送给服务端,比较消耗网络资源
  1. session存储在服务端;cookie存储在客户端
  2. session需要借助cookie记录一个sessionid。如果人为把cookie禁用,session也会失效
  3. session更安全,纯后端存储方案
  4. cookie大小一般4k左右,每个域名可以存50个左右;session理论上大小是无限的
  5. cookie会随着http请求发送给服务端,比较消耗网络资源;session相对而言消耗的资源少一点
安装session
npm i express-session --save
  • 1
引入配置session
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
  }
}));
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
session操作

session的所有操作都基于req

设置session

⚠设置session后不要重启服务器,否则session会重置,导致无法获取

req.session.键名 = "键值"
req.session.myname = "张三";
  • 1
  • 2
获取session
req.session.键名
req.session.myname;
  • 1
  • 2
删除所有session
req.session.destroy();
  • 1
实例
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);
  • 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

localStorage本地存储

把数据存储在本地浏览器上,基于服务器打开页面,同源可访问

localStorage没有时效性,只要不手动清除,永远会在浏览器中

存储localStorage
localStorage.setItem("键名","键值");
  • 1
获取localStorage
localStorage.getItem("键名");
  • 1
删除一个localStorage
localStorage.removeItem("键名");
  • 1
删除所有localStorage
localStorage.clear();
  • 1
存储复杂数据

将复杂数据类型如对象存入localStorage,会自动转换为字符串再存入

因此,需要将复杂数据转为json再存储

btn5.onclick = function(){
    let obj = {name:"张三"};
    //存储复杂数据
    localStorage.setItem("data",JSON.stringify(obj));
    //读取复杂数据
    console.log(JSON.parse( localStorage.getItem("data")))
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

sessionStorage本地存储

sessionStorage的销毁时间是浏览器关闭的时间,无法更改

存储sessionStorage
sessionStorage.setItem("键名","键值");
  • 1
获取sessionStorage
sessionStorage.getItem("键名");
  • 1
删除一个sessionStorage
sessionStorage.removeItem("键名");
  • 1
删除所有sessionStorage
sessionStorage.clear();
  • 1
sessionStorage应用-缓存注册信息

如注册内容不止一页

返回上一页时刚刚填的内容还在(即通过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
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

MongoDB

配置MongoDB

第1、2步只需配置一次,后续只需连接数据库即可

1.安装MongoDB
  1. 下载安装

    www.mangodb.com - products - community server - Download

    • 一般安装文件夹为
      • C:\Program Files\MongoDB\Server\6.0\bin
      • C:\Users\asa152hs\AppData\Local\Programs\mongosh
  2. 本地建立一个数据文件夹

    用于放置数据库数据,后期连接数据库要用

  3. 配置文件(以下基于命令行操作

    1. cd到第2步建立的数据文件夹

    2. touch .bash_profile

    3. vim .bash_profile 进入vim文本编辑器

      1. 单击“i”,以启动插入模式

      2. 输入以下代码(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"
        
        • 1
        • 2
      3. 单击“Esc”退出插入模式

      4. 输入:wq! 并回车保存并退出vim

  4. 检查是否安装成功

    1. 重启hyper
    2. 命令行输入mongod --version

输入mongod --version后显示没有该命令——需要配置环境变量

  1. 右击此电脑,出现属性选项

  2. 属性–>高级系统设置–>环境变量–>系统变量的path

  3. 新建输入mongodb的bin文件夹绝对地址

    C:\Program Files\MongoDB\Server\6.0\bin

  4. 全部确定后,重启命令行

  5. 再次输入mongod --version

2.安装Mongosh

mongosh:让我们能够使用命令行与本地系统上的mongoDB数据库进行交互的一种方式

  1. 下载地址

    https://www.mongodb.com/try/download/shell

  2. 下载指南

    https://www.mongodb.com/docs/mongodb-shell/install/

3.连接数据库

mongodb的数据以文件形式存在,需先连接文件,才能操作数据

以下基于命令行操作:

  1. cd到数据文件夹

  2. 输入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" }}
    
    • 1
    1. 可以通过浏览器 或者 可视化工具测试是否连接成功
      • 浏览器
        1. 输入地址 : http://127.0.0.1:27017
        2. 出现It looks like you are trying to access MongoDB over HTTP on the native driver port.说明成功连接
      • 可视化工具
        • root 3t
        • nivecate
  3. 打开新的命令行页面

  4. 在新命令行输入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
  • 2
  • 3
  • 4
  • 5
  • 6
  1. 设置配置项后,配置1的字段会显示,其他字段不显示

  2. _id例外,默认显示,配置0后不显示

查-条件的具体使用
字段等值
 db.集合名.find({name:"张三"})
  • 1
大小关系

⚠只能查询数字类型的字段

⚠需要用花括号括起来

说明
$gt大于
$lt小于
$ne不等于
$eq等于
$gte大于等于
$lte小于等于
//查询年龄大于20岁的数据
db.集合名.find({age:{$gt:20}});
  • 1
  • 2
模糊查询

条件可以使用正则

//查询所有姓张的用户
db.集合名.find({name:/^张.+/})
  • 1
  • 2
且关系
db.集合名.find({age:{条件一,条件二}})

//查询所有年龄大于20 且 小于 25的用户
db.集合名.find({age:{$gt:20,$lt:25}});
  • 1
  • 2
  • 3
  • 4
或关系
db.集合名.find({$or:[{age:{条件一}},{age:{条件二}}]})

//查询年龄小于20 或者年龄大于25的用户
db.集合名.find({$or:[{age:{$lt:20}},{age:{$gt:25}}]});
  • 1
  • 2
  • 3
  • 4

Mongoose

官网:https://mongoosejs.com/

guide文档:https://mongoosejs.com/docs/index.html

nodejs操作数据库

安装Mongoose
npm i mongoose --save
  • 1
1.连接数据库

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);
  }
})();
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
2.创建骨架schema

设置数据的格式

let schema = mongoose.Schema({
  name: String,
  age: Number,
  gender: String,
});
  • 1
  • 2
  • 3
  • 4
  • 5
3.创建模型model
let usermodel = mongoose.model("集合名(复数)", 为此集合的数据创建的schema);
  • 1

一个模型对应一个集合

需要创建集合 ,集合名称必须是**复数形式**

如users

4.操作模型

所有模型操作都会返还一个promise对象

模型所有操作

https://mongoosejs.com/docs/api/model.html

说明
模型名称.create()添加一个数据
模型名称.insertMany()添加多个数据
模型名称.deleteOne({条件})删除符合条件的第一个数据
模型名称.deleteMany({条件})删除符合条件的所有数据
模型名称.updateOne({条件}, {修改内容})修改符合条件的第一个数据的内容
模型名称.updateMany({条件}, {修改内容})修改所有符合条件的数据的内容
模型名称.replaceOne({条件}, {替换数据})替换符合条件的第一个数据
查/获取
模型名称.find({条件})获取符合条件的所有数据
条件为空,返回所有数据
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/天景科技苑/article/detail/983197
推荐阅读
相关标签
  

闽ICP备14008679号