Node.js学习笔记【一】

什么是Node.js

Node.js就是运行在服务器端的JavaScript,是现在流行的语言中能同时运行在前端与后台的程序语言。

Node.js优势

常见的后台开发语言:

1.PHP

2.Java

3.Python

每个语言都有它自己的特点和优势。

Node.js的优势:

1.Node.js开发高效,代码简单

2.跟前台JS配合方便

3.Node.js便于前端学习,根据JavaScript语言,不用再单独新学一门陌生的语言。

安装Node.js

去官网下下载最新版本的Node.js。

在终端运行js

1.切换盘符

2.cd xxx 进入目录

3.node xxx.js(文件名)运行js文件

初尝试:使用node.js创建简易服务器

nodejs提供了​​模块,自身就可以用来构建服务器。

下面就使用它创建一个简易的http服务器

// 引入http
const http = require('http');
// 有了http就可以让它创建服务器
var server = http.createServer(function(req,res){
  console.log('有人来了');
});
// 监听——等着
// 端口——数字
// http://localhost:8080/
server.listen(8080);

运行服务器,浏览器访问 http://localhost:8080/,控制台输出如下:

image-20210721175238139

代码思路:首先引入http模块,然后使用模块里的createServer方法返回了一个http.Server对象,然后为其添加事件监听,这里的8080为端口号(可随意自行修改,只要端口不被占用)。

尝试II:使用request和response参数

在初尝试中使用的createServer方法有一个参数——一个回调函数。

这个回调函数的作用很简单,每当有人连接服务器来访问的时候,执行回调函数作个标识。

在这个回调函数中,有2个参数:requestresponse

参数名含义
request请求 输入—请求的信息
response响应 输出—输出的东西

1.服务器会创建response对象,这个对象与客户端连接在一起,它可以用来向客户端发送响应。

2.服务器会把客户端的请求数据封装到request对象中,request就是请求数据的载体。

参数response的应用

有了response,我们就可以实现服务器对客户端的响应,向客户端输出数据。

// 引入http
const http = require('http');
var server = http.createServer(function(req,res){
  // 向浏览器(前台)输出东西
  res.write("hello world");
  //结束
  res.end()
});
// 监听——等着
// 端口——数字
// http://localhost:8080/
server.listen(8080);

运行服务器,浏览器访问可以看到页面上出现了"hello world"

image-20210721181340515

代码思路:response.write方法可以向前端输出数据,但是需要注意的是,必须调用response.end方法结束请求,否则前端会一直处于等待状态。

参数request的应用

有了request,服务器可以根据客户端的不同请求,响应客户端所发的请求。

const http = require('http');
var server = http.createServer(function(req,res){
  // /favicon.ico chrome浏览器自己加上请求的
  // console.log(req.url);
  switch (req.url) {
    case '/1.html':
      res.write("111");
      break;
    case '/2.html':
      res.write("222");
      break;
    default:
      res.write("404")
      break;
  }
  // 结束
  res.end();
});
// 监听——等着
// 端口——数字
// http://localhost:8080/
server.listen(8080);

运行服务器,浏览器请求访问端口下不同的html网页,服务器向浏览器输出相应的内容。

image-20210721182010866

image-20210721182023883

代码思路:req.url获取用户请求的路径,使用switch方法根据不同的请求路径做出不同的响应(res.write)。

尝试III:服务器访问磁盘文件

当页面数量很多时,使用switch语句工作量大而且每次增加请求路径时,都需要重启服务器。

为了解决这个问题,需要将页面文件丢到磁盘,让服务器访问磁盘的文件。

image-20210721153230878

而这就需要用到node.js提供的​​​​​模块了。

在使用​​​​模块进行文件操作时,首先来了解异步和同步的区别:

异步——多个操作可以同时进行,前一次的操作没完事,后一次也能开始。

同步——一次一个

知道它们的区别后,我们知道

1.服务器肯定是异步的,来多少用户都能同时满足,同时服务。

2.因为文件操作是非常缓慢的操作,如果是同步的就乱套了,服务器就只专门读文件了。所以需要异步操作,既然是异步操作,就需要有回调函数告诉它什么时候结束文件操作。

​​​​模块中提供了异步版本的读写文件方法:

readFile(文件名,function(err,data){})

writeFile(文件名,function(err){})

简单使用readFile

我们可以使用​​方法读取文件,该方法有2个参数:要读取的文件名和回调函数。回调函数有2个参数:errdataerr是返回的错误信息,data则是返回的数据)

//aaa.txt
aaasd
asdasd
123321
12asda
123123
//引入fs模块
const fs = require('fs');
// readFile(文件名,回调函数)
fs.readFile("aaa.txt",function(err,data){
  if (err) {
    console.log("读取失败");
  }else{
   // 控制台输出原始的二进制数据
    console.log(data);
    //使用toString方法,控制台输出非二进制数据
    console.log(data.toString());
  }
})

简单使用writeFile

我们可以使用方法写入文件,该方法有3个参数:要写入的文件名,写入的内容和回调函数。回调函数只有1个参数:errerr是返回的错误信息)

const fs = require('fs');

//writeFile(文件名,内容,回调)
fs.writeFile("bbb.txt","hello world",function(err){
console.log(err);
});

应用

了解和学会使用后,尝试修改服务器代码,让服务器访问磁盘的文件。

1.创建www目录用于存放文件

image-20210721200103888

2.server.js

const http = require('http');
const fs = require('fs');

var server = http.createServer(function(req,res){
// req.url => '/index.html'
// 读取 => './www/index.html'
// './www'+req.url
var file_name = './www'+req.url;
fs.readFile(file_name,function(err,data){
if(err){
console.log('bbbb');
res.write('404');
}else{
console.log('bbbb');
// 机器能识别二进制文件
res.write(data);
}
});
console.log('aaaa');
res.end();
});
server.listen(8080);

3.运行

这样写在运行时会报错,而且还会发现控制台先输出a再输出b

image-20210721162113528

因为readFile异步操作,不会等着它完成,发送完readFile请求直接就开始执行end()了,所以这时候就出问题了。

==举个例子==

oBtn.onclick = function(){

alert('a');

}

alert('b');

肯定是先输出b再输出a,因为a需要用户点击了才会输出。

==举个例子==

解决方法:调整res.end()位置,等待文件读取完才执行

 fs.readFile(file_name,function(err,data){
    if(err){
      res.write('404');
    }else{
      // 机器能识别二进制文件
      res.write(data);
    }
    res.end();
  });

image-20210721162908349

image-20210721162927656

4.在不中断服务器的前提下,服务器一直运行,向www目录再加个文件2.html,然后访问。

image-20210721163229310

服务器接收数据请求

前台发送数据请求大概有以下几种方式:

  1. form表单
  2. ajax
  3. jsonp

后台不管这些数据请求是以何种方式发送过来的,一视同仁。在浏览器中,前台与后台之间的交互永远都是通过http协议,所以对于后台来说,它只是接收到了一个http请求。

http请求主要有两大方式:

  1. GET 数据在url中一起传输
  2. POST 数据不在url中

接收GET数据

先准备一个表单form.html,发送GET数据请求

<!DOCTYPE html>
<head>
  <title>from</title>
</head>
<body>
  <form action="http://localhost:8080/" method="GET">
    用户:<input type="text" name="user" value=""><br>
    密码:<input type="password" name="pass" value=""><br>
    <input type="submit"id="提交">
  </form>
</body>
</html>

创建服务器,通过req获取前台请求数据,res返回响应结果

const http = require('http');
var server = http.createServer(function(req,res){
  // 通过req获取前台请求数据
  console.log(req.url);
  // res返回响应结果
  res.setHeader("Content-Type", "text/html; charset=utf-8")
  res.write("请求成功");
  res.end();
});
server.listen(8080);

填写表单然后提交,可以看到页面跳转以及url中有form提交过来的数据

image-20210721223514667

image-20210721223604997

GET数据解析

可以用比较笨的方法处理res.url包含的数据(使用split进行切割)

const http = require('http');
var server = http.createServer(function(req,res){
  var GET = {};
  // 通过req获取前台请求数据,
  // /?user=abcdef&pass=123456
  // /favicon.ico
  //console.log(req.url);
  // 去除掉chrome自加的req.url=> /favicon.ico  避免报错
  if(req.url.indexOf('?')!=-1){
    var arr = req.url.split('?');
    // arr[0] => 地址 "/"
    var url = arr[0];
    //arr[1] => "user=abcdef&pass=123456"
    var arr2 = arr[1].split('&');
    // arr2=>["user=abcdef","pass=123456"]
    for(var i=0;i<arr2.length;i++){
      var arr3 = arr2[i].split("=");
      //arr3[0] => 名字 'user'
      //arr3[1] => 数据 'abcdef'
      GET[arr3[0]] = arr3[1]; 
    }
  }else{
    var url = req.url;
  }
  console.log(url,GET);
  res.write('success');
  res.end();
});
server.listen(8080);

再次提交表单数据可以看到

image-20210721225819746


如果每次都要一次次切割处理数据太麻烦了,node.js提供了一个​​​模块专门解析user=abcdef&pass=123456这种格式的。

const querystring = require('querystring');

var json = querystring.parse("user=abcdef&pass=123456");
console.log(json);

image-20210721231450050

可以看到querystring.parse方法成功解析了数据。我们可以使用它简化上面代码。

const http = require('http');
const querystring = require('querystring');
var server = http.createServer(function(req,res){
  var GET = {};
  if(req.url.indexOf('?')!=-1){
    var arr = req.url.split('?');
    // arr[0] => 地址 "/"
    var url = arr[0];
    GET = querystring.parse(arr[1]);
  }else{
    var url = req.url;
  }
  console.log(url,GET);
  res.write('success');
  res.end();
});
server.listen(8080);

image-20210721232102930


虽然使用模块可以简化一堆split切割语句,但是一开始还是得切割一下url。node.js提供了一个比模块还简便的​​​​​模块——专门用来解析URL地址。

url.parse方法如果设置参数为true时将使用查询模块分析查询字符串:

image-20210722075945006

默认为false时,则返回:

image-20210722075912022

const http = require('http');
const urlLib = require('url');
var server = http.createServer(function(req,res){
  var obj = urlLib.parse(req.url,true);
  var url = obj.pathname;
  var GET = obj.query;

console.log(url,GET);
res.write('success');
res.end();
});
server.listen(8080);

接收POST数据

POST数据也是从request对象过来的,但是它比GET数据大得多,这就导致在处理上有一些小小的区别——POST数据需要分段发送。原因如下:

①POST数据如果是一次性把数据发送过来,别的数据就得等它完全走完了才能传过来。

②在传输过程时,数据有可能出现错误,如果是分段的话,将错误的一小段重发就行了,无须重发一整个大数据。

因为POST数据是分段发送,我们就得分段接收,这就要用到res.on('data',func)res.on('end',func)

  • data 每当data事件发生一次的时候,说明有一段数据已经到达
  • end 每当end事件发生时,说明全部数据已经到达

先准备一个表单form.html,发送POST数据请求

<!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>
</head>
<body>
  <form action="http://localhost:8080/aaa" method="POST">
    用户:<input type="text" name="user"><br>
    密码:<input type="password" name="pass" ><br>
    <input type="submit" value="提交">
  </form>
</body>
</html>

创建服务器server.js

const http = require('http');

http.createServer(function(req,res){
var str = '';//接收数据
// data——每当有一段数据到达会发生时就会发生一次(很多次)
var i = 0;
req.on('data',function(data){
console.log(第${i++}次收到这个数据);
str += data;
});
// end——数据全部到达(一次)
req.on('end',function(){
console.log(str);
});
}).listen(8080);

image-20210722150234659

可以看到:

①POST数据和GET数据的格式一样;

②当POST数据量小时,一次就能发送完。

修改表单内容,添加一个文本框

<form action="http://localhost:8080/aaa" method="POST">
    用户:<input type="text" name="user"><br>
    密码:<input type="password" name="pass" ><br>
    <textarea name="textarea" cols="40" rows="8"></textarea>
    <input type="submit" value="提交">
  </form>

image-20210722151102701

可以看到当数据量变大时,它就会进行分段。

image-20210722151723020

POST数据解析

从结果看,已经知道POST数据和GET数据的格式一样,就可以用前面使用的模块解析POST数据。

  req.on('end',function(){
    var POST = querystring.parse(str);
    console.log(POST);

});

image-20210722152813874

阶段复习

将上面所学知识进行一个整合,从接收请求到读取文件重新搭建一遍服务器作为复习。

//引入模块
const http = require('http');
const fs = require('fs');
const querystring = require('querystring');//主要用来解析POST数据
const urlLib = require('url');//主要用来解析GET数据

var server = http.createServer(function(req,res){
//获取并解析GET数据
var obj = urlLib.parse(req.url,true);
//请求的url地址
var url = obj.pathname;
//GET数据
const GET = obj.query;
//获取POST数据
var str='';//空字符串存储POST数据(并不是标准做法)
req.on('data',function(data){
// 拼接字符串
str+=data;
});
req.on('end',function(){
//解析POST数据
const POST = querystring.parse(str);
})

/*
url —— 要什么 哪个文件
GET —— GET数据
POST —— POST数据
*/

//文件请求
// 获取文件名
var file_name='./www'+url;
// 打开文件
fs.readFile(file_name,function(err,data){
if(err){
res.write('404');
}else{
res.write(data);
}
res.end();
})
});
server.listen(8080);

应用:实现用户注册和登录

根据目前所学知识,实现用户注册和登录功能与服务器进行数据交互。实现之前必须先了解创建接口

例如:/user?act=reg&user=aaa&pass=123456

服务器则返回给前台一个JSON:{"ok":false,"msg":"原因"}

/user?act=login&user=aaa&pass=123456

服务器则返回给前台一个JSON:{"ok":false,"msg":"原因"}

需要准备一个ajax库来使用,放在www目录下ajax.js,用于前台ajax请求服务器。

function json2url(json){
    var arr=[];
    for(var name in json){
        arr.push(name+'='+json[name]);
    }
    return arr.join('&');
}

function ajax(json){
json=json || {};
if(!json.url)return;
json.data=json.data || {};
json.type=json.type || 'get';

var timer=null;

if(window.XMLHttpRequest){
    var oAjax=new XMLHttpRequest();
}else{
    var oAjax=new ActiveXObject(&#39;Microsoft.XMLHTTP&#39;);
}

switch(json.type){
    case &#39;get&#39;:
        oAjax.open(&#39;GET&#39;,json.url+&#39;?&#39;+json2url(json.data),true);
        oAjax.send();
        break;
    case &#39;post&#39;:
        oAjax.open(&#39;POST&#39;,json.url,true);
        oAjax.setRequestHeader(&#39;Content-Type&#39;,&#39;application/x-www-form-urlencoded&#39;);
        oAjax.send(json2url(json.data));
        break;
}

oAjax.onreadystatechange=function(){
    if(oAjax.readyState==4){
        clearTimeout(timer);
        if(oAjax.status&gt;=200 &amp;&amp; oAjax.status&lt;300 || oAjax.status==304){
            json.success &amp;&amp; json.success(oAjax.responseText);
        }else{
            json.error &amp;&amp; json.error(oAjax.status);
        }
    }
};

}

ajax主要有几个参数需要了解一下

ajax({
    url:'xxx',
    data:{xxx},
    type:'get/post'
    success:
    error:
})

初始化服务器server.js

const http = require('http');
const fs = require('fs');
const querystring=require('querystring');
const urlLib = require('url');

var server = http.createServer(function(req,res){
//解析数据
var str = '';
req.on('data',function(data){
str+=data;
});
req.on('end',function(){
var obj = urlLib.parse(req.url,true);

const url = obj.pathname;
const GET = obj.query;
const POST = querystring.parse(str)

// 读取文件
var file_name = &#39;./www&#39;+url;
fs.readFile(file_name,function(err,data){
  if(err){
    res.write(&#39;404&#39;);
  }else{
    res.write(data);
  }
  res.end();
});

})
})
server.listen(8080);

需要将访问分为两大类:

  1. 对文件的访问

    例如:http://localhost:8080/1.html

  2. 对接口的访问

    例如:http://localhost:8080/user?act=xx

这个应用只有一个user接口,所以可以这么写

 //区分访问——接口、文件
    if(url=='/user'){//接口
}else{//文件
  // 读取文件
  var file_name = &#39;./www&#39;+url;
  fs.readFile(file_name,function(err,data){
    if(err){
      res.write(&#39;404&#39;);
    }else{
      res.write(data);
    }
    res.end();
  });
}

因为是个简单应用,一切逻辑与操作从简,这里使用变量存储用户数据

var users={};//{"blue":"123456","zhangsan":"123456"}

当访问接口时,根据它的act作出不同的响应:是注册reg还是登录login或者其他……

if(url=='/user'){//接口
      switch(GET.act){
        case 'reg':
          //1.检查用户名是否已经有了
          if(users[GET.user]){
            res.write('{"ok":false,"msg":"此用户已存在"}');
          }else{
            //2.插入users
            users[GET.user] = GET.pass;
            res.write('{"ok":true,"msg":"注册成功"}');
          }
          break;
        case 'login':
          // 1.检查用户是否存在
          if(users[GET.user]== null){
            res.write('{"ok":false,"msg":"此用户不存在"}');
            // 2.检查用户密码
          }else if(users[GET.user]!=GET.pass){
            res.write('{"ok":false,"msg":"用户名或密码有误"}');
          }else{
            res.write('{"ok":true,"msg":"登录成功"}');
          }
          break;
        default:
          res.write('{"ok":false,"msg":"未知的act"}');
      }
      res.end();
    }

后台服务器就搭建完了,接下来做前台

<!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>
  <!-- 引入ajax -->
  <script src="ajax.js" charset="utf-8"></script>
  <!-- ajax不能跨域请求,得这样访问http://localhost:8080/user.html -->
  <script type="text/javascript">
    window.onload = function(){
      var oTxtUser = document.getElementById('user');
      var oTxtPass = document.getElementById('pass');
      var oBtnReg = document.getElementById('reg_btn');
      var oBtnLogin = document.getElementById('login_btn');
      // 登录
      oBtnLogin.onclick = function(){
        ajax({
          url:'/user',
          data:{act:'login',user:oTxtUser.value,pass:oTxtPass.value},
          type:'get',
          success:function(str){
            var json = eval('('+str+')');
            if(json.ok){
              alert('登录成功');
            }else{
              alert('登录失败:'+json.msg);
            }
          },
          error:function(){
            alert('通信错误');
          }
        })
      }
      // 注册
      oBtnReg.onclick=function(){
        ajax({
          url:'/user',
          data:{act:'reg',user:oTxtUser.value,pass:oTxtPass.value},
          type:'get',
          success:function(str){
            var json = eval('('+str+')');
            if(json.ok){
              alert('注册成功');
            }else{
              alert('注册失败:'+json.msg);
            }
          },
          error:function(){
            alert('通信错误');
          }
        })
      }
    }
  </script>
</head>
<body>
  用户:<input type="text" id="user"><br>
  密码:<input type="password" id="pass"><br>
  <input type="button" value="注册" id="reg_btn">
  <input type="button" value="登录" id="login_btn">
</body>
</html>

运行结果:😊😊😊😊

image-20210722174131186

image-20210722175422816

image-20210722175401081

image-20210722175437391

 

posted @ 2021-07-22 18:03  小风车吱呀转  阅读(79)  评论(0编辑  收藏  举报