• Node.js学习笔记【二】


    模块化

    在nodejs中,应用由模块组成,nodejs中采用commonJS模块规范。

    模块分类

    1. 系统模块:http、querystring、url等
    2. 自定义模块
    3. 模块管理

    系统模块

    可以去node.js手册上查看 node 本身提供的模块

    例如:

    Crypto 加密

    Events 事件

    FileSystem 文件系统

    Net 网络操作

    OS操作系统信息

    Path 处理文件路径

    Stream 流操作

    Timers 定时器(可用于定时清理服务器垃圾等)

    ZLIB 压缩

    自定义模块

    1. 模块组成
    2. npm
    3. 发布模块

    模块化基本组成部分

    • require引入模块('.js'文件后缀可选)

    require有自己的引入规则:

    1. 如果有'./',从当前目录
    2. 如果没有'./',先从系统模块再从node_modules
    • module module身上也有一个exports,和下面的exports其实是同个东西。可以批量对外输出信息。
    • exports单独一个个对外输出信息,需要加给exports上

    npm

    npm:NodeJS Package Manager(NodeJS包管理器)

    它有以下作用:

    1. 提供统一下载途径 npm install xxx(卸载为npm uninstall xxx)
    2. 自动下载依赖

    使用npm install安装npm后,会生成一个node_modules文件夹,它不仅用来存放下载别人的模块,还可以存放自己的模块,这样就可以在require引入组件的模块时,可以不用加./

    发布模块

    1. 发布自己的模块需要有自己的npm账号,然后使用命令npm login登录账号

    2. 使用npm init初始化包的信息:设置包名name,版本version,描述description,包的主文件/入口地址entry point,包的测试命令test command,填写git仓库地址git repository,包的标签关键字keywords,作者author,包遵循的开源协议license

    3. 初始化完毕后会生成一个package.json(描述包的文件)

    4. 使用npm publish上传发布,使用npm install xxx下载自己成功发布的包

    5. 当修改自己包的代码时,不能直接使用npm publish上传发布,需要在package.json修改版本号再使用npm publish上传发布或者使用npm update xxx按照package.json中标注的版本号进行更新

    6. 使用npm unpublish/npm --force unpublish下架自己上传的包

    express框架

    Express是一个简洁而灵活的 node.js Web应用框架, 提供了一系列强大特性帮助你创建各种 Web 应用,和丰富的 HTTP 工具。

    安装

    进入目录cmd下,使用npm install express命令安装express框架

    image-20210723143557174

    image-20210723143627049

    安装express框架后,就可以引入使用它啦

    const express = require('express');
    //1.创建服务
    var server = express();
    //2.处理请求
    // 用户访问a.html时
    server.use('/a.html',function(req,res){
      // res.send与之前的res.write差不多
      res.send('abc');
      res.end()
    });
    // 用户访问b.html时
    server.use('/b.html',function(req,res){
      res.send('123');
      res.end()
    });
    //3.监听
    server.listen(8080);
    

    image-20210723145016659

    image-20210723145033091

    image-20210723145045319

    实际上,这里的resreq是express框架封装的,非nodeJs原生的,它们保留了原生的功能,还添加了其他功能。

    例如:res.send()res.send()胃口比res.write()好,不仅能"吃"字符串、二进制数据,也可以"吃"数组、JSON等其他数据,是一个增强版的res.write()

    总而言之,express保留了原生的功能,添加了一些方法(例如,.send),增强原有的功能。

    express接收请求方法

    express中有三种接收用户请求的方法:

    1. .get('/',function(req,res){}):负责接收用户的get请求
    2. .post('/',function(req,res){}):负责接收用户的post请求
    3. .use('/',function(req,res){}):接收所有类型的请求,通吃。

    使用.get.post方法

    const express = require('express');
    var server = express()
    server.get('/',function(req,res){
     console.log("有GET");
    });
    server.post('/',function(req,res){
      console.log("有POST");
    });
    server.listen(8080);
    

    表单提交get请求

      <form action="http://localhost:8080" method="post">
        用户:<input type="text" name="user"/>
        <input type="submit" value="提交">
      </form>
    

    image-20210723153709517

    image-20210723153743029

    表单提交post请求

    image-20210723153925969

    express读取文件

    express里有许多中间件,其中express-static可以帮助我们处理静态文件。

    使用npm install express-static命令

    image-20210723161759741

    使用express-static读取静态文件

    const express = require('express');
    const expressStatic = require('express-static')
    var server = express();
    server.listen(8080);
    //在use里使用express-static读取www目录下的静态文件
    server.use(expressStatic('./www'))
    

    image-20210723162532258

    express创建接口

    假设定义一个这样的登录接口:

    /login?user=xxx&pass=xxx

    => {ok:true/false,msg:"原因"}

    使用express创建接口,其中用users模拟用户数据

    //用户数据
    var users={
      'blue':'123456',
      'pink':'654321',
      'purple':'987654'
    };
    server.get('/login',function(req,res){
      // express提供了req.query查询前端发送过来的参数
      var user = req.query['user'];
      var pass = req.query['pass'];
    

    if(users[user]==null){
    res.send({ok:false,msg:'此用户不存在'});
    }else{
    if(users[user]!=pass){
    res.send({ok:false,msg:'密码错了'});
    }else{
    res.send({ok:true,msg:'成功'})
    }
    }
    })

    检测接口:一切正常

    image-20210723164815039

    完善前台

    <!DOCTYPE html>
    <html lang="en">
    <head>
      <meta charset="UTF-8">
      <meta http-equiv="X-UA-Compatible" content="IE=edge">
      <meta name="viewport" content="width=device-width, initial-scale=1.0">
      <title>Document</title>
      <script src="ajax.js" charset="utf-8"></script>
      <script>
        window.onload = function(){
          var oUser = document.getElementById('user');
          var oPass = document.getElementById('pass');
          var oBtn = document.getElementById('btn1');
    
      oBtn.onclick = function(){
        ajax({
          url:&#39;/login&#39;,
          data:{user:oUser.value,pass:oPass.value},
          success:function(str){
            var json = eval(&#39;(&#39;+str+&#39;)&#39;);
            if(json.ok){
              alert(&#39;登录成功&#39;);
            }else{
              alert(&#39;失败:&#39;+json.msg);
            }
          },
          error:function(){
            alert(&#39;通信失败&#39;);
          }
        })
      }
    }
    

    </script>
    </head>
    <body>
    用户:<input type="text" id="user"/><br>
    密码:<input type="password" id="pass"><br>
    <input type="button" value="登录" id="btn1"/>
    </body>
    </html>

    运行服务器,前台提交数据给后台

    image-20210723170951057

    image-20210723171034852

    image-20210723171048155

    接下来我们将表单get请求换成post请求。

    因为解析post数据比get数据略微麻烦,这里需要引入新的中间件body-parser(使用npm install body-parser命令安装)

    注意body-parser只能解析数据不能解析文件,中间件multer可以解析上传的文件。

    const express = require('express');
    const bodyParser = require('body-parser');
    

    var server = express();
    server.listen(8080);

    server.use(bodyParser({}));
    server.use('/',function(req,res){
    console.log(req.body);//POST
    })

    image-20210724134302892

    这里的bodyParser.urlencoded({})其实有两个参数:

    server.use(bodyParser.urlencoded({
    

    extended,//扩展模式,值为true则为开启扩展模式

    limit//限制接收多大数据,默认为100K

    }))

    GET数据与POST数据处理区别

    1. GET——无需中间件,req.query

    2. POST——需要body-parser分成两步:

      ①加工:server.use(bodyParser.urlencoded({}));

      ②server.use(function(){req.body});

    链式操作

    使用链式操作是规定这个操作流程有一个步骤,即需要先做什么,然后做什么。依次下去形成一个流水线

    简而言之:请求接口相同,同时又有next()操作,这样就是express链式操作

    image-20210724135404931

    image-20210724135333483

    我们可以利用链式操作仿写body-parser解析POST数据的过程。

    const express = require('express');
    const querystring = require('querystring');
    var server = express();
    server.listen(8080);
    //没有指明路径,意味着针对所有路径。
    server.use(function(req,res,next){
      var str = '';
      //收集数据
      req.on('data',function(data){
        str+=data;
      });
      req.on('end',function(){
        // 使用原生的querystring将数据解析成JSON格式返回给body
        req.body = querystring.parse(str);
    
    next();//数据都获取完了再进行下一步
    

    })
    })

    server.use('/',function(req,res){
    console.log(req.body);
    })

    image-20210724142143925

    这样我们仿写的body-parser中间件就完成啦(^▽^),我们可以将它封装起来。

    创建libs->my-body-parser.js

    const querystring = require('querystring');
    

    module.exports=function(req,res,next){
    var str = '';
    req.on('data',function(data){
    str+=data;
    });
    req.on('end',function(){
    req.body = querystring.parse(str);
    next();
    })
    }

    引入使用包装好的中间件

    const express = require('express');
    const bodyParser2 = require('./libs/my-body-parser');
    

    var server = express();
    server.listen(8080);

    server.use(bodyParser2);

    server.use('/',function(req,res){
    console.log(req.body);
    })

    效果同body-parser一样

    image-20210724144908431

    cookie与session

    http有个不足:无状态。也就是两次请求之间,服务器是无法识别是否是同个人,这样每次用户刷新页面就得重新登录一次,所以cookie就应运而生。


    cookie在浏览器保存一些数据,每次请求都会带过来。这样的话服务器可以根据cookie判断登录状态。

    缺陷:不安全(用户可修改)、存储空间有限(4k)


    cookie保存在客户端,是不安全的。所以session应运而生。

    session: session是不能独立存在的,是基于cookie实现。它保存数据,保存在服务端。

    优点:安全、存储空间依据服务器空间

    原理:客户端请求服务端,先带一个空的cookie传到服务器,然后服务端对这个cookie赋值一个sessionID并传回客户端;客户端向服务端发起下一个请求时,就会带上这个cookiecookie中会有一个sessionID,服务器利用sessionID找到session文件读取或写入。

    image-20210724164241009

    隐患:session劫持

    读取与发送cookie


    1. 读取——cookie-parser
    2. 发送——res.cookie()
    const express = require('express');
    const cookieParser = require('cookie-parser');
    var server = express();
    

    //cookie
    server.use(cookieParser());

    server.use('/',function(req,res){
    //发送——res.cookie可选参数path,maxAge
    //path:指定在该路径下可以读这个cookie
    //maxAge:设置过期时间(有效期),单位毫秒
    res.cookie('user','purple',{path:'/aaa',maxAge:30243600*1000});
    //读取cookie
    console.log(req.cookies);
    });

    server.listen(8080);

    值得注意的是,cookie是可以往上访问的,所以在根目录下可以读取在aaa目录下的cookie

    cookie的安全性比较差,所以可以实现给它加签名。

    const express = require('express');
    const cookieParser = require('cookie-parser');
    

    var server = express();

    //cookie,把签名传给它解析
    server.use(cookieParser('hasd2jk2jke'));

    server.use('/',function(req,res){
    //设置签名
    req.secret='hasd2jk2jke';
    //有了密钥还要将参数签名signed设置为true才能让cookie数据签名
    res.cookie('user','purple',{signed:true});
    console.log("带签名的cookie",req.signedCookies);
    console.log("无签名的cookie",req.cookies);
    })

    server.listen(8080);

    总结

    1. cookie空间非常小——省着用,要精打细算,能不签名就不签名
    2. 安全性非常差——要校验cookie是否被篡改过
    3. 发送cookie——res.cookie(名字,值,{path:'/',maxAge:毫秒,signed});
    4. 读取cookie——使用到中间件cookieParser,server.use(cookieParser('密钥'))
    5. req.cookies 未签名版,req.signedCookies带签名版
    6. 删除cookie:res.clearCookie(cookie名);
    7. cookie加密:cookie-encrypter,cookie加密意义不大。

    写入与读取session

    cookie的基础上,我们可以使用中间件cookie-session往cookie写入或者读取session

    const express = require('express');
    const cookieParser = require('cookie-parser');
    const cookieSession = require('cookie-session');
    var server = express();
    

    //cookie
    server.use(cookieParser());
    server.use(cookieSession({
    name:'sess',
    //session名
    //使用session时,需要加keys--密钥,keys为数组,会依次循环使用keys中的密钥对session加密
    //keys密钥数组越长,越安全
    keys : ['aaa','bbb','ccc'],
    //设置有效期(单位毫秒)
    maxAge: 136001000//有效期1小时
    }));

    server.use('/',function(req,res){
    //第一次访问时
    if(req.session['count']==null){
    req.session['count']=1;
    }else{
    req.session['count']++;
    }
    //session在request上
    console.log(req.session['count']);
    res.send('ok');
    });

    server.listen(8080);

    模板引擎

    为了便于维护,且使后端逻辑能够比较好的融入前端的HTML代码中,同时便于维护,很多第三方开发者就开发出了各种Nodejs模板引擎,其中比较常用的就是Jade模板引擎和Ejs模板引擎。

    jade:破坏式的、侵入式;强依赖

    ——使用了jade就不能跟普通的html和CSS共存

    ejs:温和、非侵入式、弱依赖

    conslidate帮助express适配各种模板引擎。通过conslidate,各种模板引擎可以得到整合,可以对express提供统一的接口。

    Jade和Ejs使用

    Jade

    使用npm install jade命令安装jade

    使用jade.renderFile方法读取文件,渲染文件里面的内容,输出解析后的html字符串。

    image-20210724211848803

    1.jade

    html 
      head 
        style 
        script 
      body 
        div
          ul 
            li 
            li 
            li 
        p 
    

    简单用法:

    • jade根据缩进,规定层级
    • 属性放在()里面,逗号分隔
    <script src="a.js"></script>
    =>
    script(src="a.js")
    
    • 内容接在属性后面空一格的位置
    <a href="http://www.baidu.com/">百度</a>
    =>
    a(href="http://www.baidu.com/") 百度
    
    • 行内CSS样式可以用①普通属性写法②用json
        div(style="200px;height:200px;background:red")
        div(style= { '200px', height: '200px', background: 'red'})
    
    • class内部标签CSS样式可以用①普通属性写法②数组
        div(class="aaa left-warp active")
        div(class= ['aaa', 'left-warp', 'active'])
    
    • 竖线|可以使内容在body或script等标签中原样输出
    <body>abcd</body>
    =>
    <body>
    	|abcd
    </body>
    
    • .可以使下一级内容都是原样输出
    <head>div<head>
    =>
    head.
    	div
    
    • 可以使用include引入代码文件
    script
    	include a.js
    

    更多关于jade知识可以看官方网站

    Ejs

    使用npm install ejs命令安装ejs

    使用ejs.renderFile方法读取文件,渲染文件里面的内容,输出解析后的html字符串

    image-20210724213210712

    1.ejs

    <!DOCTYPE html>
    <html lang="en">
    <head>
      <title>Document</title>
    </head>
    <body>
      我的名字:<%= name %>
    </body>
    </html>
    

    注意这里的<%= name %>变量name来自ejs.js中

    简单用法:

    • <%= 变量名 %>可以输出变量,变量来自ejs.js中。
    • <% for(){}%> 可以使用for循环语句
    • <%= %>转义输出;<%- %>不转义输出
    <%=
    	var str="<div></div>";
    %>
    <%- str %>
    
    • <% include 文件名路径 %>可以输出文件内容

    更多关于ejs知识可以看官方网站

    解析post文件

    <!DOCTYPE html>
    <html lang="en">
    <head>
      <meta charset="UTF-8">
      <title></title>
    </head>
    <body>
      <form action="http://localhost:8080" method="post" enctype="multipart/form-data">
        文件:<input type="file" name="f1"/><br>
        <input type="submit" value="上传">
      </form>
    </body>
    </html>
    

    前面学过body-parser可以解析post数据,但是它不能解析post上传的文件,我们需要用到新的中间件multer解析post文件

    const express = require('express');
    const multer = require('multer');
    //dest参数可以设置上传文件保存的地址
    var objMulter = multer({dest:'./www/upload/'});
    var server = express();
    

    //指定一个文件
    // server.use(objMulter.single('f1'))
    // 接收任何文件
    server.use(objMulter.any());

    server.post('/',function(req,res){
    console.log(req.files);
    });

    server.listen(8080);

    image-20210725094707845

    image-20210725094735714

    可以看到文件上传后被保存在指定目录下,同时原始文件名被修改成随机序列(防止重名)以及扩展名消失了,但是我们想保留文件的扩展名,需要求助path获取原始扩展名然后使用fs重命名

    path解析出来的路径有这么几个属性:

    1. base 文件名(包含扩展名)
    2. ext 扩展名
    3. dir 文件路径
    4. name 文件名(不包含扩展名)

    我们就可以使用ext获取其文件扩展名

    fs中有一个函数fs.rename(旧文件名,新文件名,回调函数),可以使用它把刚才保存的上传文件重命名修改其文件扩展名。

    const express = require('express');
    const multer = require('multer');
    const fs = require('fs');
    const pathLib = require('path');
    //dest参数可以设置上传文件保存的地址
    var objMulter = multer({dest:'./www/upload/'});
    var server = express();
    

    //指定一个文件
    // server.use(objMulter.single('f1'))
    // 接收任何文件
    server.use(objMulter.any());

    server.post('/',function(req,res){
    console.log(req.files);
    //新文件名
    var newName = req.files[0].path+pathLib.parse(req.files[0].originalname).ext;
    fs.rename(req.files[0].path,newName,function(err){
    if(err)
    res.send('上传失败')
    else
    res.send('ok')
    })
    //1.获取原始文件扩展名
    //2.重命名临时文件
    });

    server.listen(8080);

    可以看到成功上传文件并保留了其文件扩展名

    image-20210725100747288

    route——路由

    routeroute是express里的一个重要部分,它可以把不同的目录对应到不同的模块

    使用步骤:

    1. 创建router:var router = express.Router();
    2. 把router添加到server:server.use('/user',router);
    3. router内部处理
    const express = require('express');
    var server = express();
    //目录1::/user/
    //创建user相关的路由
    var routeUser = express.Router();
    //记得把创建好的路由告诉server
    server.use('/user',routeUser);
    routeUser.get('/1.html',function(req,res){
      res.send('user1');
    })
    routeUser.get('/2.html',function(req,res){
      res.send('user2');
    })
    

    //目录2:/atricle/
    //创建article相关的路由
    var articleRouter=express.Router();
    server.use('/article', articleRouter);

    articleRouter.get('/10001.html', function (req, res){ //http://xxxx.com/article/10001.html
    res.send('asdfasdfasdf');
    });
    server.listen(8080);

    image-20210725222729261

    MySQL

    在MySQL里,有两种单位:

    1. 库:文件夹—用来管理,本身没法存数据
    2. 表:文件—存数据

    使用mysql建表,例如user_table表

    image-20210726155244182

    建表完成之后,可以进行mysql客户端的操作。node.js不支持mysql,需要使用第三方库,npm install mysql下载mysql模块(client).

    const mysql = require('mysql');
    

    //1.链接
    //createConnection(哪台服务器,用户名,密码,库)
    var db=mysql.createConnection({
    host:'localhost',
    user:'root',
    password:'123456',
    database:'20210726'
    })

    //2.查询
    //query(sql语言,回调函数)
    db.query("",(err,data)=>{
    if(err)
    console.log('出错了',err);
    else
    console.log('成功了',data);
    //可以把数组或JSON转换成适于传输的字符串
    console.log(JSON.stringify(data));
    });

    连接池(Pool)

    保持某个数目的连接数,连接的时候选择能用的连接,避免重复连接

    //createPool
    const db = mysql.createPool({
        host:'localhost',
        port:3306,
        user:'root',
        password:'123456',
        database:'20210726'
    });
    

    SQL语句

    SQL 标准写法

    1. 关键字大写
    2. 库、表、字段需要加上”
    3. 分号结尾

    SQL四大操作语句

    1. 删 DELETE

      DELETE FROM 表 WHERE 条件

    2. 增 INSERT

      INSERT INTO 表(字段列表)VALUES(值列表)

    3. 改 UPDATE

      UPDATE 表 SET 字段=值,字段=值,……WHERE 条件

    4. 查 SELECT

      SELECT * FROM 表 WHERE 条件

    子句

    WHERE条件

    WHERE age<=10
    WHERE age>=10 AND score<60 
    WHERE age>15 OR score>80
    

    ORDER 排序

    ASC——升序(从小到大)

    DESC——降序(从大到小)

    ORDER BY age ASC/DESC
    //先按价格升序,再按销量降序
    ORDER BY price ASC,sales DESC 
    

    GROUP 聚类、合并相同

    //按班级分组,将class相同的合并,统计班级人数
    SELECT class,COUNT(class) FROM student
    GROUP BY class
    

    //计算各班平均分
    SELECT class,AGE(score) FROM student
    GROUP BY class
    //计算各班最高分,最低分
    SELECT class,MAX(score),MIN(score) FROM student
    GROUP BY class
    //统计每个人消费总价,按总价升序排列
    SELECT name,SUM(price) FROM sales_table GROUP BY name ORDER BY SUM(price) ASC

    GROUP子句使用时可以配合COUNTMINMAXAVG配合使用

    LIMIT限制输出

    应用:分页。

    分页的方式

    1. 所有数据一次性传给前端;不适合数据量大的情况。
    2. 每次后台只给一页数据给前端;
    LIMIT 10;  前10条
    LIMIT 5,8; 从5开始,要8条
    

    注意:字句之间有顺序: WHERE, GROUP BY, ORDER BY, LIMIT

    (筛选→合并→排序→限制)

    SELECT class,COUNT(class) FROM student
    WHERE score>60
    GROUP BY COUNT(class) DESC
    LIMIT 2;
    
  • 相关阅读:
    Django 前戏
    SQL基本语句
    如何正确安装Mysql
    JQuery
    解疑答惑—解决脱离标准文档流(恶心的浮动)
    事件
    卷基于快照进行恢复
    centos7下Firewall使用详解
    基于镜像卷启动的虚机快照代码分析
    nova卸载volume源码分析
  • 原文地址:https://www.cnblogs.com/Small-Windmill/p/15068233.html
Copyright © 2020-2023  润新知