简介

  • nodejs是一个js运行环境runtime 让js可以开发后端程序 几乎能实现其他后端语言能实现的所有function
  • nodejs基于googlev8引擎 原用于chrome的js解释部分,但后来被搬到服务器上用于做服务器的软件
  • 优点 处理高并发 可以实现的功能多

搭建环境

第一次运行nodejs

  • 建立app.js,然后console一段信息,打开终端输入node app.js(确保你的终端打开路径正确),有输出则为配置成功

创建第一个应用

http模块

  • 创建

    1
    const http = require('http');
  • 用于创建服务器

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    http.createServer((req,res)=>{
    设置响应头
    状态码是200 文件类型是html 字符集是utf-8
    res.writeHead(200,{"Content-type":"text/html;charset=utf-8"});
    解决乱码
    res.write("<head><meta charset='UTF-8'></head>");
    必写结束响应
    res.end();

    }).listen(3000);端口
    箭头函数是es6新增的特性和function差不多

url模块

  • 创建

    1
    const url = require('url');
  • 获取地址栏里面的其他属性 解析url get可以用 post要用第三方中间件

    1
    2
    3
    url.parse(req.url,true).query;
    true转换为一个对象
    返回一个json数组类型

热刷新supervisor

  • 输入cnpm找到对应网址进行安装指令 注意权限问题
  • 再安装supervisor 注意权限问题
  • 然后 将vscode 的默认终端从powershell转化成cmd
  • 接着 输入supervisor app.js
  • 便可以进行热更新 每次保存都会重启对应的js
  • 注意如果报错会一直反复运行

commandjs 自定义模块

  • 将部分功能性js封装到一个模块里面并且使用 的一种方式

  • 下面以模块tools.js为例 看index.js如何调用模块的方法并使用

    1
    2
    3
    4
    5
    >>tools.js
    参见nodejs学习/demo3/module/tools.js

    >>command02.js
    参见nodejs学习/demo3/command02.js
  • 暴露方法 分为三类

    1
    2
    3
    单个方法暴露
    一个对象多个方法暴露
    直接暴露

node_modules

  • nodejs的标准库,里面放module的js
  • 在导入的时候可以直接require(‘模块名/模块.js’),其中.js可以省略

生成package.json

  • 读取路径的时候默认是读取node_modules下面的模块的index.js,而如果想要省略的写法 即直接读取单个模块下面单个js的路径 可以使用package.json将下面的单个js路径改为默认路径
  • 方法 找到模块文件路径下的单个js 在地址栏输入cmd打开cmd窗口
  • 之后输入 npm init —yes 就可以生成相关的package.json

第三方模块导入

用md5加密模块举例

  1. 网址里面下包
  2. npm install md5 —save 这样别人就知道你项目里面导入了哪些依赖,然后使用 npm i就可以导入依赖
  3. var md5 = require(‘md5’);
  4. md5(‘123132’);

关于依赖

  • 如下

npm指令

  • npm -v 看npm版本号
  • npm -i 导入依赖
  • npm install xx —save 导入xx包并将依赖文件保存
  • npm uninstall xx
  • npm list 查看当前目录下已经安装的node包
  • npm info 模块 查看模块的版本
  • npm install xxx@1.2.0 安装指定版本的xx包
  • npm init 或者 npm init —yes 生成package.json

fs模块

  • 1.fs.stat 检测是文件还是目录

    2.fs.mkdir 创建目录

    3.fs.writeFile 创建写入文件

    4.fs.appendFile 追加文件

    5.fs.readFile 读取文件

    6.fs.readdir 读取目录

    7.fs.rename 重命名

    8.fs.rmdir 删除目录

    9.fs.unlink 删除文件

数据流 管道流

  • 数据流读取数据

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    const fs = require('fs');
    var str = '';
    var count = 0;
    var readStream = fs.createReadStream('./data/output.txt');

    readStream.on('data',(data)=>{
    str+=data;
    count++;
    })

    readStream.on('end',()=>{
    console.log(str);
    console.log(count);
    })

    readStream.on('error',(err)=>{
    console.log(err);
    })
  • 数据流写入数据

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    // 写入数据到文件流
    const fs = require('fs');
    var str = '';

    for(var i=0;i<500;i++){
    str+='数据流xxxxxxxxxxxxx';
    }

    var writeStream = fs.createWriteStream('./data/output.txt');

    writeStream.write(str);

    // 标记写入完成
    writeStream.end();

    writeStream.on('finish',()=>{
    console.log('写入完成');
    })

  • 管道流 复制文件之类的方法

    1
    2
    3
    4
    5
    6
    7
    8
    // 管道流 可以进行数据交换
    const fs = require('fs');

    var readStream = fs.createReadStream('./demo8.zip');

    var writeStream = fs.createWriteStream('./data/demo8.zip');

    readStream.pipe(writeStream);

异步

  • async 异步函数 await 等待异步函数执行完毕

  • 同步就是根据代码全部流程一步一步执行

  • 而异步就是遇到异步函数 先将其放到任务栈 然后等待所有其他函数执行完毕 之后再执行异步函数

  • 关于js的异步同步执行顺序

    1
    2
    3
    4
    流程如下
    先执行同步
    再执行全部微任务
    再执行第一项宏任务
  • 延迟函数类似于异步

  • 异步的一个例子

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    async function test(){
    return new Promise((resolve,reject)=>{
    setTimeout(()=>{
    var name = 'zhangsan222';
    resolve(name);
    },1000);
    })
    }
    async function main(){
    var data = await test();
    console.log(data);
    }
    main();

利用http模块,fs模块,url模块,path模块 创建一个静态web服务器

  1. http模块

    1. 首先一个网页的基本请求和响应需要用到http模块

    2. 导入http模块 创建服务器

    3. 设置响应头

      1
      2
      3
      4
      res.writeHead(200,{
      "Content-Type":"text/html;charset='utf-8'"
      })
      注意设置状态码和文件类型
    4. 注意写结束响应 和 端口

  2. fs模块

    1. 作用是读取本地中的静态资源
    2. 一般用到fs.readFile
    3. 注意写err
  3. path模块

    1. 作用是将地址中的后缀名给分离出来 (此时使用到path.extname()),传给getMime函数

    2. getMime函数的作用是根据后缀名 返回相应的文件类型

      1
      2
      3
      4
      5
      6
      7
      8
      9
      exports.getMime = ((extname)=>{
      switch (extname){
      case: '.html':
      return 'text/html';
      case: '.css':
      return 'text/css';
      case: ....
      }
      })
  4. url模块

    1. 通过url.parse()方法解析req.url里面的pathname

    2. 原因是使用json的时候 getMime无法将他返回相关的文件类型

    3. 使用json的时候 不需要地址栏后面的数字

    4. 所以

      1
      let pathname = url.parse(req.url).pathname;
  5. command.js

    1. modules下面新建的js 暴露一个getFIleMime方法使其根据path模块的extname方法传来的后缀名 返回相应的文件类型
    2. 自定义 自定义比较有限
    3. 通过mime.json文件(该json文件内部写了很多的文件类型 以键值对的形式存在
    4. 那么就需要读取mime.json文件 需要用到fs模块中的readFile方法

      1. 如果使用../data/mime.json’会找不到该json文件 原因是他是基于app.js的路径来读取的 所以应该使用./data/mime.json
      2. 其次 输出readFile里面的data值的时候 直接输出是一个buffer流十六进制 需要将其转换成String 此处使用到data.toString()
      3. 之后 我们的根本目的是通过json数组键值对 获取对应的文件类型 所以还要将得到的String字符串转换成json数组 此处使用到 JSON.parse(STRING)
      4. 接着 将json数组传递给Obj类型变量举例mimeObj 但此时如果直接打印mineObj.extname是无法识别的 因为变量属性名只能通过方括号的形式访问 此处用到 mimeObj[extname]
      5. 然后 因为fs.readFile是异步方法 不能直接获取到值 需要使用promise来封装 此处使用到return new Promise((resolve,reject)=>{ 异步函数 }),使用里面的参数resolve输出
      6. 最后 app.js中调用该方法的时候 注意在接收传递来的值之前使用await 并且记得await需要在异步函数中使用 即函数要加async
    5. 或者使用fs模块里面的readFileSync方法 同步读取方法

Ejs模板引擎

  • ejs属于后台模板 可以将数据库和文件读取的数据显示到html页面上,他是一个第三方模块 需要用npm安装

  • npm install ejs —save

  • nodejs中使用

    1
    2
    3
    4
    5
    6
    ejs.renderFile('./views/login.ejs',{
    msg:msg
    传入的数据
    },(err,data)=>{
    回调内容一般必写响应头和结束响应
    })
  • ejs里面传参的方法

    1
    2
    3
    4
    5
    6
    7
    假设需要传入msg给h3标签
    <h3><%=msg%></h3>

    假设需要用到for循环传输一个obj数组到li中
    <%for(var i=0;i<obj.length;i++){%>
    <li><%=obj.属性%></li>
    <%}%>

Get Post Put Delete

  • 获取get传值

    1
    2
    const url = require('url');
    vargetData = url.parse(req.url).query;
  • 获取post传值

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    var postData = '';
    req.on('data',(chunk)=>{
    postData+=chunk;
    });
    req.on('end',()=>{
    try{
    postData = JSON.parse(postData);
    }catch(e){
    req.query = postData;
    console.log(queryString.parse(postData));
    }
    })
  • put主要用于修改数据 可以用postman软件模拟

  • delete主要用于删除数据

Express框架

  • 简单来说就是一个路由框架 封装了很多路由方法

  • 引入

    1
    var express = require('express');
  • 实例化框架 然后通过app.get / app.post 配置路由

    1
    2
    3
    4
    5
    6
    7
    var app = express();
    app.get("/",(req,res)=>{
    res.send("hello world");
    })
    app.post("/",(req,res)=>{
    res.send("hello world");
    })
  • 具体封装的方法

    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
    // 4定义一个全局变量
    let G = {};


    let app = function (req, res) {
    // console.log('调用app方法');
    if (G['/login']) {
    // console.log("1");
    // console.log(G['./login']);
    G['/login'](req, res); //6执行方法
    }
    }

    /*
    1现在使用express的方法 并理解其原理
    */
    // 3注册
    app.get = function (str, cb) {
    //5注册方法 将回调函数传给str
    G[str] = cb;
    // console.log(str);
    // console.log(cb);
    }

    // app.get("/login",(req,res)=>{
    // // res.send('hello world');
    // console.log('执行login方法');
    // })
    app.get("/login", function (req, res) {
    // res.send('hello world')
    console.log("执行login方法")
    })

    // 2要想使用这个方法 需要给他绑定方法

    // 7
    setTimeout(() => {
    app('req', 'res');
    }, 1000);
  • 支持多级路由配置方式

    1
    2
    3
    app.get("/admin/user/add",(req,res)=>{

    })
  • 动态路由 可以获取下一级目录的名字 当匹配到改动态路由的时候,不会再往下继续匹配

    1
    2
    3
    4
    app.get("./article/:id",(req,res)=>{
    var id = req.param["id"];
    res.end("动态路由"+id);
    })
  • 配置路由的时候还需要注意顺序的问题 以静态路由优先 随后配置动态

获取get传值

  • 以前是要通过url模块的url.parse(req,url).query来获取 很麻烦

  • 现在可以直接使用req.query

    1
    2
    3
    4
    5
    app.get("/product",(req,res)=>{
    let query = req.query;
    console.log(query);
    res.end("product");
    })

express中使用ejs

  • 安装

    1
    cnpm install ejs --save
  • 配置模板引擎

    1
    app.set("view engine","ejs");
  • express中已经集成了ejs 所以不用引入

  • 默认在views文件夹下面,但也要自己新建一个

  • 使用的时候

    1
    2
    res.render("ejs名字",{传入的数据});
    ejs名字可以不用写后缀
  • 绑定数据

    1
    <%=shuju%>
  • 输出内容的时候解析其中的 html 标签

    1
    <%-<h3>lebpig</h3>%>
  • 条件判断

    1
    <%语句%>
  • 在一个ejs里面引入其他ejsimage-20211204160943445

  • ejs后缀修改为html 略

利用express.static托管静态文件

  • 如果你的静态资源存放在多个目录下面 可以多次调用express.static 中间件

    1
    app.use(express.static('public'));
  • 虚拟静态目录 略

中间件

简介

  • 通俗的讲就是匹配路由之前或者匹配路由完成所做的一系列操作 中间件如果想向下匹配的化 需要些 next()

express可以使用如下几种中间件

  • 应用级中间件
  • 路由级中间件
  • 错误处理中间件
  • 内置中间件
  • 第三方中间件、

其中讲解部分

  • 内置中间件 之前学的托管静态文件↑

  • 应用级别中间件 通常用于权限判断

    1
    2
    3
    4
    app.use(function(req,res,next){
    //操作
    next(); //表示完成匹配后这个中间件之后的程序继续向下执行 如果不写 会停止
    })
  • 错误处理中间件

    1
    2
    3
    4
    app.use((req,res,next)=>{
    res.status(404).send("404");
    })
    当页面没有对应的路由的时候 跳转404
  • 第三方中间件 获取post传值

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    1.安装body-parser
    cnpm install body-parser --save
    2.配置
    const bodyparser = require('body-parser');
    app.use(bodyParser.urlencoded({
    extended: false
    }));
    app.use(bodyParser.json());
    3.使用
    app.post('/doLogin', (req, res) => {
    var body = req.body;
    res.writeHead(200,{
    "Content-type":"text/html;charset=utf-8"
    })
    res.end('执行提交'+body.username);
    })

Cookies

  • 安装

    1
    cnpm install cookie-parser --save
  • 配置中间件

    1
    app.use(cookieParser())
  • 设置cookie

    1
    2
    3
    4
    5
    6
    7
    res.cookie("username","zhangsan",{maxAge:1000*60*60})
    //代表获取了username张三 并且存放一小时
    //maxAge是最大存放时间
    res.cookie("username","zhangsan",{maxAge:1000*60*60,path:"/article"})
    //path代表只有xxx能获取cookie
    {domin:'.itying.com'}
    //domin可以实现多域共享cookie
  • cookie的加密

    1
    2
    3
    4
    5
    6
    //配置中间件的时候传入加密的参数
    app.use(cookieParser("suibianxie"));
    //写他的属性
    res.cookie("username","zhangsan",{maxAge:1000*60*60,signed:true})
    //需要得到他的时候 写
    req.signedCookies

Session

简介

  • session是另一种记录客户状态的机制,不同的是Cookie保存在客户端浏览器中,而session保存在服务器上
  • session相比cookie要更加安全,由于session保存到服务器上,所以当访问量增多的时候,会比较占用服务器的性能。单个cookie保存的数据大小不能超过4k,很多浏览器限制一个站点最多保存20个cookie。而session没有这方面的限制。session是基于cookie进行工作的。

session的工作流程

  • 当浏览器访问服务器并发送第一次请求的时候,服务器会创建一个session对象,生成类似于key value的键值对,然后将key(cookie)返回到浏览器中,浏览器下次再访问时候,携带key(cookie)找到对应的session

express-session的使用

  • 安装

    1
    cnpm install express-session --save
  • 引入

    1
    var session = require('express-session');
  • 设置官方文档提供的中间件

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    app.use(session({
    secret:'keyboard cat', //服务器端生成session的签名
    name: 'xxx', //修改session对应cookie的名称
    resave: true, //强制存储session 即使它没有变化 默认是false
    saveUninitialized: true, //强制将未初始化的session存储
    cookie:{
    maxAge: 1000*60*60,
    secure: false //true表示只有https协议才能访问cookie
    },
    rolling:true //在每次请求时强制设置cookie 将重置cookie过期时间,默认false
    }))
  • 使用

    1
    2
    设置值 req.session.username = 'zhangsan';
    获取值 req.session.username;

负载均衡配置

  • 分布式架构中session保存到数据库的流程image-20211205160754719

如何使用

  • 安装express-session 和 connect-mongo模块

  • 引入模块

    1
    2
    var session = require('express-session');
    const MongoStore = require('connect-mongo')(session);
  • 配置中间件

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    app.use(session({
    name: 'xxx', //修改session对应cookie的名称
    resave: true, //强制存储session 即使它没有变化 默认是false
    saveUninitialized: true, //强制将未初始化的session存储
    cookie:{
    maxAge: 1000*60*60,
    secure: false //true表示只有https协议才能访问cookie
    },
    rolling:true //在每次请求时强制设置cookie 将重置cookie过期时间,默认false
    store: new MongoStore({
    url: 'mongodb://127.0.0.1:27017/xxx' //xx代表地址 可以直接用cmd输入mongo找到mongodb对应的地址
    touchAfter: 24*3600 //不管发出了多少次请求 24h内只更新一次session 除非你改变session
    })
    }))

路由模块化

简介

  • express中允许我们通过express.Router创建模块化的 可挂载的路由处理程序

应用程序生成器

简介

  • 通过应用程序生成器可以快速创建一个应用的股价

指令

  • 如下

    1
    cnpm install -g express-generator
  • 验证安装成功

    1
    express -h
  • 生成方法

    1
    express --view=ejs 目录

图片上传Multer

简介

  • 是nodejs一个中间件,用于处理multpart/form-data类型的表单数据,主要用于上传文件
  • 注意它不会处理除了上述数据类型的数据
  • 一般上传图片识别不了 所以需要用到它

安装

  • 指令

    1
    npm install --save multer
  • 引入模块

    1
    2
    3
    var multer = require('multer');
    var upload = multer({dest:'static/upload'});
    //上传目录 一定要存在
  • 配置

    1
    2
    3
    4
    5
    6
    7
    router.post("/doAdd",upload.single("pic"),(req,res)=>{
    xxx
    })
    //此处pic是对应的表单的name

    //并且form表单要设置类型
    <form enctype='multpart/form-data'>

MongoDb数据库

NoSql介绍

  • 是not only sql的意思 指的是非关系型数据库 是以key-value形式存储的数据库 键值对

  • 解决大数据查询问题

  • 和传统数据库的简单对比

    1
    2
    非结构型数据库 没有行列的概念 用json来存储数据,集合相当于“表” 文档相当于“行”

  • 图例image-20211203141522995

MongoDb介绍

  • 是介于关系型数据库和非关系型数据库之间的产品 他 的查询语言非常强大,几乎可以实现类似于关系型数据库表单查询的大多数功能
  • 特点是高性能 易部署 易使用 存储非常方便
  • 安装略
  • 注意配置环境变量
  • cmd输入mongo可以开启

增删改查

  • 创建数据库 已经创建则切换到这个数据库,如果想要创建成功需要插入一条数据

    1
    2
    use xxx
    db.user.insert({"name":"leb"});
  • 查看数据库

    1
    show dbs
  • 插入数据

    1
    db.user.insert({"name":"leb"});
  • 删除集合

    1
    db.user.drop();
  • 删除数据库 需切换到

    1
    db.dropDatabase();
  • 查找数据

    1
    db.user.find();
  • 查找去掉当前聚集集合中的某列的重复数据

    1
    db.user.distinct("xxx");
  • 查询age =22 的记录

    1
    db.user.find({age:22});
  • 大于22 小于22 大于等于 小于等于

    1
    2
    3
    4
    db.user.find({age:{$gt:22}});
    db.user.find({age:{$lt:22}});
    db.user.find({age:{$gte:22}});
    db.user.find({age:{$lte:22}});
  • 大于22小于26

    1
    db.user.find({age:{$gt:22,$lt:26}});
  • 查询name中包含mongo的数据 模糊查询用于搜索

    1
    db.user.find({name:/mongo/});
  • 查询指定列name age数据,age>25

    1
    2
    3
    db.user.find({age:{$gt:25}},{name:1,age:1})
    相当于
    select name,age from user where age>25
  • 按照年龄升序 降序

    1
    2
    3
    4
    升序
    db.user.find().sort({age:1});
    降序
    db.user.find().sort({age:-1});
  • 查询前五条数据

    1
    db.user.find().limit(5);
  • 查询十条以后的数据

    1
    db.user.find().skip(10);
  • 修改数据

    1
    db.student.update({"name":"小明" })
  • 删除数据

    1
    2
    db.collectionsNames.remove({"borough":"xxzxc"})
    db.users.remove({age:132})

索引基础

  • 创建索引

    1
    db.user.ensureIndex({"username":1})
  • 获取当前集合的索引

    1
    db.user.getIndexes()
  • 删除索引的命令

    1
    db.user.dropIndex({"username":1})

Nodejs操作Mongodb

  • 安装指令

    1
    npm install mongodb --save
  • 连接数据库

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    // 引入mongodb
    const {MongoClient} = require('mongodb');

    // 定义数据库连接地址
    const url = 'mongodb://127.0.0.1:27017';

    // 定义要操作的数据库
    const dbName = 'itying';

    // 实例化MongoClient 传入数据库连接地址
    const client = new MongoClient(url,{useUnifiedTopology:true});

    // 连接数据库
    client.connect((err)=>{
    if(err){
    console.log(err);
    return;
    }else{
    console.log('sjk connect successful');
    let db = client.db(dbName);//切换到这个数据库
    client.close();//关闭数据库
    }
    })
  • 查找数据

    1
    2
    3
    4
    db.collection("user").find({"name":"leb"}).toArray((err,data)=>{
    console.log(data);
    client.close();//关闭数据库
    })
  • 增加数据

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    db.collection("user").insertOne({"name":"zzl"},(err,result)=>{
    if(err){
    console.log(err);
    return;
    }else{
    console.log('add successful')
    console.log(result);
    client.close();//关闭数据库
    }
    })
  • 修改数据

    1
    2
    3
    4
    5
    6
    7
    8
    9
    db.collection("user").updateOne({"name":"leb"},{$set:{"name":"lebpig"}},(err,data)=>{
    if(err){
    return console.log(err);
    }else{
    console.log('modify successful');
    console.log(data);
    client.close();//关闭数据库
    }
    })
  • 删除数据

    1
    2
    3
    4
    5
    6
    7
    8
    9
    db.collection("user").deleteOne({"name":"zzl"},(err,data)=>{
    if(err){
    return console.log(err);
    }else{
    console.log('delete successful');
    console.log(data);
    client.close();
    }
    })