好程序员web前端培训分享node学习笔记系列之四十一一、手动封装body-parser中间件 cookie-parser中间件

1、封装axios请求方式

body-parser

constqs=require("querystring");varbodyparse=(functionbodyparse(){

functioncommon(type){

return(req,res,next)=>{

letcontentType=req.headers["content-type"];

if(contentType=="application/json"||contentType=="application/x-www-form-urlencoded"){

letstr="";

req.on("data",(data)=>{

str+=data;

})

req.on("end",()=>{

if(contentType=="application/json"){

req.body=JSON.parse(str);

next();

}elseif(contentType=="application/x-www-form-urlencoded"){

req.body=qs.parse(str);

next();

}

next();

})

}else{

next();

}

}

}

//解析appliaction/jsonfunctionjson(){

lettype="json"

returncommon(type)

}

//解析appliaction/x-www-form-urlencodedfunctionurlencoded(){

lettype="urlencoded";

returncommon(type)

}

return{json,urlencoded}})()module.exports=bodyparse;

cookie-parser

constcookieparse=function(){

return(req,res,next)=>{

letcookies=req.headers.cookie;

letobj=cookies.split("; ").reduce((prev,curr)=>{

varkey=curr.split("=")[0];

varval=curr.split("=")[1];

prev[key]=val;

returnprev;

},{})

req.headers.cookie=obj;

next();

}}module.exports=cookieparse;

二、express原理解析

1、app.use的作用以及实现

consthttp=require("http")consturl=require("url");constpath=require("path");constfs=require("fs");constexpress=()=>{

//将所有注册的事件添加到routesMap中去varroutesMap={

get:{

},

post:{

}

}

//app这个函数中做的执行varapp=function(req,res){

//获取用户请求的路径varpathname=url.parse(req.url).pathname;

//获取用户请求的方式varmethod=req.method.toLowerCase();

if(pathname!=="/favicon.ico"){

//next函数用来执行下一个中间件varnext=function(){

varhandle;

//执行是没有路径的函数while(handle=routesMap.all.shift()){

handle(req,res,next)

}

}

next();

//执行app.use带路径的函数 for(varkeyinroutesMap){

//判断路径是否与用户请求的路径相同if(key===pathname){

routesMap[key](req,res,next);

break;

}

}

//判断用户是否是get请求if(method=="get"){

//如果是get请求则找到get请求相对应得路径 执行对应的函数for(varkeyinroutesMap.get){

if(key==pathname){

routesMap.get[key](req,res,next);

break;

}

}

}elseif(method=="post"){

}

}

}

//注册事件app.use=function(){

//判断use中的第一个参数是路径还是一个函数if(typeofarguments[0]==="string"){

routesMap[arguments[0]]=arguments[1]

}elseif(typeofarguments[0]==="function"){

//建议all这个key值写成Symbolif(!routesMap.all){

routesMap.all=[];

}

routesMap.all.push(arguments[0]);

}

}

//注册事件app.get=function(path,callback){

routesMap.get[path]=callback;

}

//创建服务app.listen=function(port,callback){

http.createServer(app).listen(port,callback)

}

returnapp;}//静态资源访问express.static=function(staticPath){

functiongetFile(filePath){

returnfs.readFileSync(filePath);

}

return(req,res)=>{

var{pathname}=url.parse(req.url,true);

//获取文件的后缀varext=pathname.substring(pathname.lastIndexOf("."));

if(pathname==="/"){

res.writeHead(200,{"content-type":"text/html;charset=utf8"});

res.end(getFile(path.join(staticPath,"index.html")))

}elseif(ext===".css"){

res.writeHead(200,{"content-type":"text/css;charset=utf8"});

res.end(getFile(path.join(staticPath,pathname)));

}elseif(ext===".js"){

res.writeHead(200,{"content-type":"application/x-javascript;charset=utf8"});

res.end(getFile(path.join(staticPath,pathname)));

}elseif(/.*\.(jpg|png|gif)/.test(ext)){

res.writeHead(200,{"content-type":`image/${RegExp.$1};charset=utf8`});

res.end(getFile(path.join(staticPath,pathname)));

}elseif(ext===".html"){

res.writeHead(200,{"content-type":"text/html;charset=utf8"});

res.end(getFile(path.join(staticPath,pathname)));

}

}}module.exports=express;

三、express-generator生成器以及代码解析

1、app.js解析

2、www文件解析

3、ejs的基本使用

用<% … %> 开始符和结束符之间写js代码用<%= … %>输出变量数据 变量若包含 '<' '>' '&'等字符 会被转义用<%- … %>输出变量(html的解析前面需要加-) 不转义用<%- include('user/show') %>引入其他模板包含 ./user/show.ejs用<%# some comments %>来注释,不执行不输出<%% 转义为 '<%'<% ... -%> 删除新的空白行模式?<%_ … _%> 删除空白符模式开始符和结束符内部不可以嵌套

四、前端渲染和后端渲染的区别

后端渲染HTML的情况下,浏览器会直接接收到经过服务器计算之后的呈现给用户的最终的HTML字符串,这里的计算就是服务器经过解析存放在服务器端的模板文件来完成的,在这种情况下,浏览器只进行了HTML的解析,以及通过操作系统提供的操纵显示器显示内容的系统调用在显示器上把HTML所代表的图像显示给用户。前端渲染就是指浏览器会从后端得到一些信息,这些信息或许是适用于题主所说的angularjs的模板文件,亦或是JSON等各种数据交换格式所包装的数据,甚至是直接的合法的HTML字符串。这些形式都不重要,重要的是,将这些信息组织排列形成最终可读的HTML字符串是由浏览器来完成的,在形成了HTML字符串之后,再进行显示

五、用所学的express生成器+ejs做一个简单的列表页六 、socket的基本使用

1、什么是socket?

网络上两个程序通过一个双向的通信连接实现数据交换,这个连接的一端称为socket

2、http请求与socket的区别

1、在以前我们实现数据交换已经有了HTTP协议,为什么还要学习socket? 回顾:当输出www.baidu.com的时候浏览器执行了那些操作?2、http通信的特点:1、连接属于非持久性连接:TCP的三次握手2、客户端只能访问服务端,服务端无法访问客户端,属于单项通信TCP三次握手: TCP三次握手过程中不传递数据,只为同步连接双方的序列号和确认号传递数据,在握手后服务端和客户端才开始传输数据,在理想状态下,TCP连接一旦建立,在通信的双方中任何一方主动断开连接之前TCP连接会一直保持下去。socket通信特点:1、持久性连接2、双向通信,客户端能访问服务端,服务端也能访问客户端socket是对TCP/IP协议的封装,socket只是一个接口而不是一个协议,通过Socket我们才能使用TCP/IP/UDP协议

3、通过node内置模块net实现简单版聊天

1、socket连接需要由2个节点:(1)clientSocket(2)serverSocket2、文本流:readline

constnet=require("net");constserver=net.createServer();constclients=[];//当有人连接服务端的时候回触发当前函数server.on("connection",(client)=>{

//给每一个用户添加一个唯一的标识client.id=clients.length;

clients.push(client);

//将客户端的消息转发给所有的用户client.on("data",(data)=>{

clients.forEach(item=>{

//判断用户是否存在if(item)item.write("服务端:"+data);

})

})

//判断用户是否退出群聊client.on("close",()=>{

clients[client.id]=null;

})})server.listen(9000);constnet=require("net");constclient=newnet.Socket();//创建I/o模型 创建出入和输出流constreadline=require("readline");//创建constrl=readline.Interface({

input:process.stdin,

output:process.stdout})//客户端连接服务端client.connect(9000,()=>{

client.on("data",(data)=>{

console.log(data.toString());

})})//获取终端的内容将内容转发到服务端rl.on("line",(val)=>{

client.write(val);})

七、webSocket的基本使用

constWebSocket=require('ws');//创建服务constserver=newWebSocket.Server({port:9000});//监控用户连接server.on('connection',(client,req)=>{

varip=req.connection.remoteAddress;

//监听客户端传递到服务端的消息client.on('message',(data)=>{

//将消息转发给所有的用户server.clients.forEach((item)=>{

//判断用户是否是连接的状态if(item.readyState===WebSocket.OPEN){

item.send(ip+":"+data);

}

});

});});

<!DOCTYPEhtml><htmllang="en"><head>

<metacharset="UTF-8">

<metaname="viewport"content="width=device-width, initial-scale=1.0">

<metahttp-equiv="X-UA-Compatible"content="ie=edge">

<title>Document</title></head><body>

<inputtype="text"id="txt">

<buttonid="btn">点击</button>

<ulid="list"></ul></body></html><script>

varclient=newWebSocket("ws://10.60.15.150:9000");

vartxt=document.getElementById("txt");

varlist=document.getElementById("list");

varbtn=document.getElementById("btn");

//接受服务端消息 onmessageclient.onmessage=function(e){

varli=document.createElement("li");

li.innerText=e.data;

list.appendChild(li);

li.scrollIntoView();

}

//向服务端发送消息 sendbtn.onclick=function(){

varval=txt.value;

client.send(val);

txt.value="";

}</script>

八、http://socket.io的基本使用

varexpress=require('express')varapp=express();//文件在服务器运行varhttp=require('http')varserver=http.createServer(app);varpath=require("path")//因为我们要做持久性通信 因此不能够用http连接 http连接与socket.io进相关联vario=require("socket.io")(server);app.use(express.static(path.join(__dirname,"./public")))//当用户连接的时候触发的函数io.on('connection',(socket)=>{

console.log('a user connected');

//接受客户端消息 将消息转发给所有用户socket.on("sendMessage",(mes)=>{

io.emit("message",mes)

})

//当用户断开连接的时候socket.on('disconnect',function(){

console.log('user disconnected');

});});server.listen(3000,function(){

console.log('listening on *:3000');});

九、利用websocket实现一个聊天室

1、多人聊天2、图片发送3、表情发送

varexpress=require('express')varapp=express();//文件在服务器运行varhttp=require('http')varserver=http.createServer(app);varpath=require("path")//因为我们要做持久性通信 因此不能够用http连接 http连接与socket.io进相关联vario=require("socket.io")(server);app.use(express.static(path.join(__dirname,"./public")))varusers=[];//当用户连接的时候触发的函数io.on('connection',(socket)=>{

//监听用户连接socket.on("system",(username,type)=>{

if(users.includes(username)){

socket.emit("login",0)

}else{

socket.userIndex=users.length;

socket.username=username;

users.push(username);

io.emit("login",1,username)

}

})

socket.emit("userList",users);

socket.on("ClientsendMessage",(usename,message)=>{

socket.broadcast.emit("serverSendMessage",usename,message)

})

socket.on("sendImg",(username,base64Img)=>{

socket.broadcast.emit("serverSendImg",usename,base64Img)

})});server.listen(3000,function(){

console.log('listening on *:3000');});<!doctypehtml><html><head>

<title>Socket.IOchat</title>

<linkrel="stylesheet"href="./css/iconfont/iconfont.css">

<style>

*{margin:0;padding:0;box-sizing:border-box;}

ul,li{list-style:none;}

html,body{font:13pxHelvetica,Arial;height:100%;}

form{background:#000;padding:3px;position:fixed;bottom:0;width:100%;}

forminput{border:0;padding:10px;width:90%;margin-right:.5%;}

formbutton{width:9%;background:rgb(130,224,255);border:none;padding:10px;}

#messages{list-style-type:none;margin:0;padding:0;}

#messagesli{padding:5px10px;}

#messagesli:nth-child(odd){background:#eee;}

#action{width:100%;height:30px;display:flex;align-items:center;}

#actioninput[type='color']{width:40px;height:30px;}

#upload,#fontColor{width:40px;height:30px;position:relative;}

#actioninput[type='file'],

#fontColorinput[type="color"]{width:40px;height:30px;position:absolute;left:0%;top:0;opacity:0;z-index:5;}

#actioni,#fontColori{width:100%;height:100%;position:absolute;left:0;top:0;color:#fff;font-size:20px;}

#mask{width:100%;height:100%;background:rgba(0,0,0,.3);position:absolute;z-index:10;}

#content{width:100%;height:100%;display:flex;justify-content:space-between;}

#contentul:nth-child(2){width:200px;height:100%;border-left:1pxsolid#ccc;}

#userList{overflow:scroll;}

#userListli{line-height:30px;border-bottom:1pxsolid#bbb;width:100%;}

.userDate{color:green;line-height:20px;font-size:18px;}

.userInfo{color:#000;line-height:20px;font-size:14px;}

#messages>div{min-height:60px;}

#system{color:#c33;font-size:18px;}

</style></head><body>

<divid="mask"></div>

<divid="content">

<ulid="messages"></ul>

<ulid="userList">

<li>用户列表</li>

</ul>

</div>

<formid="form">

<divid="action">

<divid="fontColor">

<inputtype="color">

<iclass="iconfont">&#xec85;</i>

</div>

<divid="upload">

<inputtype="file"id="file">

<iclass="iconfont">&#xe674;</i>

</div>

<ul></ul>

</div>

<inputid="m"autocomplete="off"/>

<inputtype="submit"value="提交">

</form></body></html><scriptsrc="/socket.io/socket.io.js"></script><scriptsrc="https://code.jquery.com/jquery-1.11.1.js"></script><scriptsrc="./js/index.js"></script>

index.js

classSocketAlley{

constructor(){

this.mask=$("#mask");

this.userListEl=$("#userList");

this.messages=$("#messages");

this.socket=io();

this.init();

}

init(){

if(!sessionStorage.getItem("status")){

this.username=window.prompt("请输入您的姓名");

if(this.username){

sessionStorage.setItem("status",this.username);

//当用户连接进来的时候通知服务器this.socket.emit("system",this.username,"login")

//检测是否连接成功this.socket.on("login",(data,username)=>{

if(data==1){

alert("连接成功");

this.mask.hide();

//全局通知vardiv=$("<div></div>");

varstr=username+"进入聊天室"

div.text(str);

this.messages.append(div);

}else{

alert("用户名重复请求重新编写");

}

})

}

}else{

this.mask.hide();

this.username=sessionStorage.getItem("status");

this.socket.on("login",(data,username)=>{

//全局通知vardiv=$("<div></div>");

varstr=username+"进入聊天室"

div.text(str);

this.messages.append(div);

})

}

this.userList();

this.serverMessage();

this.userSendInfo();

this.sendImg();

}

userList(){

this.socket.on("userList",this.handleUserListSucc.bind(this))

}

handleUserListSucc(data){

data.forEach(this.handleUserListEach.bind(this))

}

handleUserListEach(item){

varli=$("<li></li>");

li.text(item);

this.userListEl.append(li);

}

userSendInfo(){

$("#form").on("submit",this.handleUserSendInfo.bind(this))

}

handleUserSendInfo(e){

e.preventDefault();

varval=$("#m").val();

this.infoStyle(this.username,val);

//向服务端发送消息this.socket.emit("ClientsendMessage",this.username,val);

}

serverMessage(){

this.socket.on("serverSendMessage",(username,message)=>{

this.infoStyle(username,message);

})

this.socket.on("serverSendImg",(username,message)=>{

this.infoImg(username,message);

})

}

infoImg(username,message){

varparentDiv=$("<div></div>");

varchildDiv=$("<div></div>");

varcontentDiv=$("<div></div>");

vard=newDate();

if(/(\d{2}:\d{2}:\d{2})/.test(d)){

childDiv.text(username+RegExp.$1);

varimg=$("<img/>");

img.attr("src",message);

contentDiv.append(img);

parentDiv.append(childDiv);

parentDiv.append(contentDiv)

this.messages.append(parentDiv);

parentDiv[0].scrollIntoView();

}

}

infoStyle(username,message){

varparentDiv=$("<div></div>");

varchildDiv=$("<div></div>");

varcontentDiv=$("<div></div>");

vard=newDate();

if(/(\d{2}:\d{2}:\d{2})/.test(d)){

childDiv.text(username+RegExp.$1);

contentDiv.text(message);

parentDiv.append(childDiv);

parentDiv.append(contentDiv)

this.messages.append(parentDiv);

parentDiv[0].scrollIntoView();

}

}

sendImg(){

$("#file").on("change",this.sendImgCb.bind(this))

}

sendImgCb(){

var_this=this;

//只能从原生JS中拿到file对象varfile=$("#file")[0].files[0];

//将file对象转换dataurl(base64的文件形式)varfileReader=newFileReader()

fileReader.onload=function(e){

_this.socket.emit("sendImg",_this.username,e.target.result);

_this.infoImg(_this.username,e.target.result)

}

//将file转换成dataUrl的形式fileReader.readAsDataURL(file);

}}newSocketAlley();

十、利用socket实现一个简单版的多人点餐功能

constexpress=require("express");constapp=express();consthttp=require("http");constserver=http.createServer(app);constio=require("socket.io")(server);constpath=require("path");app.use(express.static(path.join(__dirname,"./public")))io.on("connection",(socket)=>{

socket.on('add',(data)=>{

socket.broadcast.emit("serverAdd",data);

})

socket.on('reducer',(data)=>{

socket.broadcast.emit("serverReducer",data);

})})server.listen(9000)<!DOCTYPEhtml><htmllang="en"><head>

<metacharset="UTF-8">

<metaname="viewport"content="width=device-width, initial-scale=1.0">

<metahttp-equiv="X-UA-Compatible"content="ie=edge">

<title>Document</title>

<linkrel="stylesheet"href="./css/reset.css">

<style>

#goodsList{

width:100%;

height:100%;

padding:.1rem;

}

.goodsItem{

display:flex;

width:100%;

padding:.2rem;

}

.goodsItem>div:nth-child(1){

margin-right:.2rem;

}

.goodsItemimg{

width:2rem;

height:2.5rem;

}

.goodsDes{

flex:1;

display:flex;

justify-content:space-between;

flex-direction:column;

}

.goodsDes>div:nth-child(3){

display:flex;

}

.goodsDes>div:nth-child(3).reducer{

width:30px;

height:30px;

text-align:center;

line-height:30px;

background:#ccc;

color:#fff;

font-size:16px;

}

.goodsDes>div:nth-child(3).add{

width:30px;

height:30px;

text-align:center;

line-height:30px;

background:#ccc;

color:#fff;

font-size:16px;

}

.goodsDes>div:nth-child(3)input{

width:80px;

height:30px;

border:0;

}

</style></head><body>

<!--

1、需求评审

2、(需求肯定出来了)前后端开会->定义接口(后端为主前端为辅)你写代码的速度*需求的难易程度*1.5=模块开发的时间cnpminstalljson-server-gjson-server中的增删改查查:GET增:POST删:delete

改:patch-->

<divid="goodsList">

</div></body></html><scriptsrc="/socket.io/socket.io.js"></script><scriptsrc="https://cdn.bootcss.com/jquery/1.11.3/jquery.min.js"></script><script>

classGoods{

constructor(){

this.socket=io();

}

init(){

this.getGoods();

this.watchServer();

}

getGoods(){

$.ajax({

type:"get",

url:"http://localhost:3000/data",

success:this.handleGetGoodsSucc.bind(this)

})

}

handleGetGoodsSucc(data){

varstr="";

for(vari=0;i<data.length;i++){

str+=`<div data-id="${data[i].id}"> <div> <img src="${data[i].goodsPic}" > </div> <div> <div>${data[i].goodsName}</div> <div>单价:<span data-price="${data[i].price}">${data[i].price}</span>$</div> <div> <div>-</div> <input type="text" value="${data[i].num}"> <div>+</div> </div> </div> </div> `

}

$("#goodsList").html(str);

this.add();

this.reducer();

}

add(){

$(".add").each(this.handleAddEach.bind(this))

}

handleAddEach(index){

$(".add").eq(index).on("click",this.handleAddCb.bind(this,index))

}

handleAddCb(index){

varn=$(".add").eq(index).prev().attr("value");

varid=$(".add").eq(index).parent().parent().parent().attr("data-id");

n++;

$(".add").eq(index).prev().attr("value",n);

varprice=$(".add").eq(index).parent().parent().find("div").eq(1).find("span").attr("data-price");

$(".add").eq(index).parent().parent().find("div").eq(1).find("span").text(price*n)

//socket部分this.socket.emit("add",{id:id,num:n});

}

reducer(){

$(".reducer").each(this.handleReducerEach.bind(this))

}

handleReducerEach(index){

$(".reducer").eq(index).on("click",this.handleReducerCb.bind(this,index))

}

handleReducerCb(index){

varn=$(".reducer").eq(index).next().attr("value");

varid=$(".reducer").eq(index).parent().parent().parent().attr("data-id");

if(n==1){

n=1;

}else{

--n;

this.socket.emit("reducer",{id:id,num:n});

}

$(".reducer").eq(index).next().attr("value",n);

varprice=$(".reducer").eq(index).parent().parent().find("div").eq(1).find("span").attr("data-price");

$(".reducer").eq(index).parent().parent().find("div").eq(1).find("span").text(price*n)

}

watchServer(){

this.socket.on("serverAdd",this.handleServerAddSucc.bind(this));

this.socket.on("serverReducer",this.handleServerReducerSucc.bind(this))

}

handleServerAddSucc(data){

$(".add").each(this.handleAddEachServer.bind(this,data))

}

handleAddEachServer(data,index){

varid=$(".add").eq(index).parent().parent().parent().attr("data-id");

if(id==data.id){

varval=$(".add").eq(index).prev().val();

$(".add").eq(index).prev().val(Number(data.num));

}

}

handleServerReducerSucc(data){

$(".reducer").each(this.handleReducerEachServer.bind(this,data))

}

handleReducerEachServer(data,index){

varid=$(".reducer").eq(index).parent().parent().parent().attr("data-id");

if(id==data.id){

varval=$(".reducer").eq(index).next().val();

$(".reducer").eq(index).next().val(Number(data.num));

}

}

}

newGoods().init();</script>