当前位置: 首页 > article >正文

【Node】什么是Node,及基础使用

  • 使用
  • 搭建
  • app.js ( 服务器文件 )
  • express ( node.js框架 )
  • /xxx.do ( 发起请求 )
  • route ( 路由拦截 )
  • controller ( 业务逻辑 )
  • config ( 配置文件 )
  • 404 ( 错误页面 )
  • 自动更新工具
  • ajax ( 前后台数据交互 )
  • ajax ( 原生ajax的封装 )
  • 增删改查 ( 多条件查询 )
  • 分页 ( 极简版 )
  • dao ( 数据库交互 ) 、promise ( 对象 )
  • ajax ( jQuery的ajax请求 )
  • ejs ( 视图模板 )
  • ‌cookie、session、token ( 身份识别验证 )
  • multer ( node.js中间件 )
  • websocket ( 实时通信 )
  • localStorage / sessionStorage ( 本地存储 )

概念: node.js是一个服务器端的JavaScript解释器

特点:JavaScript运行环境;依赖Chrome V8引擎进行代码解释;非阻塞I/O;事件驱动;轻量;单线程(一个人干很多事情只要安排合理互相不影响就是单线程非阻塞)

缺点:不适合CPU密集型的应用(比如超大循环);只支持单核CPU
在这里插入图片描述

使用

官网下载node:(尽量不要最新版的msi,不稳定且周边支持可能还没跟上,一键安装即可)https://nodejs.cn/download/
使用指令:

node --version  (cmd中输入指令,如果能跳出版本号就是安装成功)
where node    (查看node安装得盘符及位置)
npm uninstall node    (卸载node,或者下载新版本同位置安装就直接覆盖)

npm是包管理器:可通过 package.json文件中的下载记录,通过 npm install 安装恢复 node-modules文件夹

尝试下一个模块试试:npm install qs --save-dev

搭建

(1) 首先创建package.json文件

cmd中去,盘符加冒号e: ,表示去到哪个盘;然后cd 路径,去到项目文件夹位置

npm init (所有都默认回车都行)

(2) 搭建项目架构

public  -----用于存放所有静态资源文件(html、css、img、font......)
    css
    js
    font
    page:.html
    ....
routes  -----路由文件(js文件,分发)
views   -----存放视图文件
dist    -----里面是发布版的文件,放的生产文件而不是源码(比如有注释的)
app.js(或者叫server.js)   -----核心文件,服务器文件
config   -----放配置文件的
controller   -----放业务逻辑文件的
dao   -----进行数据连接的,发请求之类的

工作流程:
在这里插入图片描述

app.js ( 服务器文件 )

app.js不是静态资源,它是服务器文件是不会引入界面的

如何打开服务器文件app.js:
    (1)在cmd中cd到项目根目录文件夹,执行node app命令(app.js文件是用node来执行的)
    
    (2)或在编辑器的终端命令行中,在项目文件夹路径下执行node app命令
    
    (3)或在"运行"->"编辑配置"->点"+"号后选择node(因为app.js是需要node去开);配置:名
    称->工作目录(项目根目录文件夹)->js文件(app.js);运行(绿三角形),停止(红正方形)

注意:
	console.log(222) 但凡服务器文件打印出来的东西,都在代码编辑器命令行或者cmd命令行中去查看,而页面(静态资源)上的js在浏览器的控制台看

引入模块 http:在服务器中引入模块都会使用关键词 require (请求)

//http是一个对象用花括号{}括起来的,是对象必然有属于自己的属性和方法
const http=require("http");
// console.log(http);

//这个fs模块是专门读取文件的,读到内存中,它也是个对象{}
const fs=require("fs")
// console.log(fs)



//调用http对象的方法createServer搭建服务;自带两个参数request,responese:请求对象(请求相关的所有东西都会保存在这个对象里面)、响应对象
const server=http.createServer((request,response)=> {
    console.log("启动")

    //请求对象
    // console.log(request)
    //拿到请求对象后想请求什么东西
    console.log(request.url)
    //如:index.html,关键是这个后缀它要的是什么类型的文件,再去设置不同的请求头
    let newPath=request.url.split(".")
    console.log(newPath)

    if (newPath[1]=="html"){
        fs.readFile("public"+request.url,(error,data)=>{
            // console.log(data)
            // console.log(error)
            //把读出来的东西data返回给用户,读到什么返回什么
            response.writeHeader(200,{"content-type":"text/html;charset=utf-8"})
            response.write(data);
            response.end()
        })
    }
    else if (newPath[1]=="css"){
            fs.readFile("public"+request.url,(error,data)=>{
                response.writeHeader(200,{"content-type":"text/css;charset=utf-8"})
                response.write(data)
                response.end()
            })
    }
    else if (newPath[1]=="js"){
        fs.readFile("public"+request.url,(error,data)=>{
            response.writeHeader(200,{"content-type":"text/javascript;charset=utf-8"})
            response.write(data)
            response.end()
        })
    }
    //图片有不同后缀类型的图片文件png、jpg......
    else if (newPath[1]=="jpg" || newPath[1]=="png"){
        fs.readFile("public"+request.url,(error,data)=>{
            // response.writeHeader(200,{"content-type":"images/jpg"})
            response.writeHeader(200,{"content-type":"images/"+newPath[1]})
            response.write(data)
            response.end()
        })
    }


    //防止资源解析有问题不能正常显示,不同的响应头的设计
    //response.writeHeader(200,{"content-type":"text/html;charset=utf-8"})
    //response.write("<h1> hello lmy月 </h1>")
    // console.log(response)
    //响应完之后告诉他我要挂电话了,不然它会一直等
    //response.end()
})



//访问用ip+端口号(http://192.168.0.110:8888),现在只能局域网内的访问,需要放到公开的域名上大家才能访问到。若自己访问自己通过localhost:8888(localhost代表本机的ip地址)
server.listen(8888);

搭建简单服务器的方式:

const http=require("http");
const server=http.createServer(function () {
        console.log("启动")
})
server.listen(8888);

访问方式: ip + 端口号 ( http://ip:8888 )

express ( node.js框架 )

(1) 下载

npm install express -g   (全局安装-g)

(2) 引用、配置、使用框架的功能

/* app.js 文件 */

//(引入模块放在最上方)第一步引入express,不能直接用它本身虽然是对象它也是方法
const express=require("express")
//morgan---任务日志模块,可以看到每一个请求
const  logger=require("morgan")
//favicon---小图标模块
const favicon=require("serve-favicon")
//post请求模块
const bodyParser=require("body-parser")
//引入自己写的路由模块
const route=require("./routes/indexRouter")



//执行express全局函数,返回express的服务器对象
const app=express()
// console.log(app)



//使用日志模块(日志一定放在上方),配置为dev(开发模式)
app.use(logger("dev"))

//使用小图标模块(网上找-在线生成icon图标工具)
app.use(favicon(__dirname+"/public/images/favicon.ico"))

//配置post请求必须写在路由上方(false:表示使用系统模块的querystring来处理,官方推荐;true:表示使用第三方模块qs来处理;从功能上qs比querystring要更强大)
app.use(bodyParser.urlencoded({extended: false}))
//使用post的什么数据格式
app.use(bodyParser.json())

//使用自己写的路由模块(放在静态资源路径配置之前)
app.use(route)

//-----------------------------配置-----------------------------------

//静态资源路径的配置(static静态,如果请求了静态资源到去哪里找)
app.use(express.static(__dirname+"/public"))   //告诉它静态资源在哪里就可以了,因为html中的路径是理清楚了的
app.use(express.static(__dirname+"/public/html"))   //不是所有html文件都在public下的


//监听端口搭建服务器
app.listen(8888,()=>{
    console.log("服务器启动")
})

搭建项目的流程:

    npm install express -g    (全局安装express,只需要安装1次)
    
    1.首先在项目路径下安装package.json文件 (进入项目根目录   cd E:\笔记\zcode\node\day2)
        npm init
        npm install express

    2.引入express搭建简单服务器
        const express=require("express")
        const app=express()
        app.listen(8888,()=>{
            console.log("服务器启动")
        })

    3.其它模块
        日志模块:npm install morgan --save
        小图标:npm install serve-favicon --save
        数据库模块:npm install mysql
        处理Post请求的模块:npm install body-parser
        处理路径的path模块:npm install path --save-dev

    __dirname:指向当前文件的根目录

整个流程: 页面请求 --> 服务器app.js --> 路由 --> 控制层逻辑处理 --> send内容

/xxx.do ( 发起请求 )

如何书写请求:

<!-- /login.do是请求的名字(向服务器提交的地址),这样店小二(router)才能根据名字去分发;get是地址栏提交(东西太多就会装不下) -->
<form action="/login.do" method="get">
<!--<form action="/login.do" method="post">-->
    user:<input type="text" name="username">
    <br>
    password:<input type="text" name="pwd">
    <br>
    <input type="submit" value="登录">
    <!-- <button>提交</button> 这两种button是可以提交表单的-->
</form>

<hr>

<form action="/reg.do" method="post">
    user:<input type="text" name="username">
    <br>
    password:<input type="text" name="pwd">
    <br>
    <input type="submit" value="注册">
</form>
<form action="/addStudent.do"> <!--不写method:默认get请求-->
    学生姓名:<input type="text" name="stuname">
    <br>
    联系方式:<input type="text" name="phone">
    <br>
    性别:<input type="text" name="sex">
    <br>
    classID:<input type="text" name="classid">
    <br>
    年龄:<input type="text" name="age">
    <br>
    成绩:<input type="text" name="garde">
    <br>
    <button>新增学生</button>
</form>

route ( 路由拦截 )

//引入express来实现路由功能
const express=require("express");
//引入控制层文件
const userController=require("../controller/userController")
const studentController=require("../controller/studentController")


//调用express对象提供的路由方法,返回路由对象
const router=express.Router();

//拦截(请求),分发(给谁处理)
router.get("/login.do",userController.getUser)
router.post("/login.do",userController.getUser)
router.post("/reg.do",userController.addUser)
router.get("/addStudent.do",studentController.addStudent)


//但凡向服务器发的请求都可以拦截
// router.get("/student.html",function () {
// 	console.log("拦截到静态资源请求")
// })


//公开路由文件(导出文件:任何文件都可引入,使文件之间产生关联)
module.exports=router;

controller ( 业务逻辑 )

参数在哪里get => req.querypost => req.body

//使用数据库连接的配置文件
const mydb=require("../config/dbconfig"); //用require引进来的东西都是以对象{}的形式存在
// console.log(mydb)

//接收到小二(路由)分发过来的请求
const userController={
	// getUser:function (){
	//
	// },
	//等同于下面的写法:

	//登录
	getUser(req,resp){
		//查询数据库是否有该用户,返回结果
		//sql语句:select * from t_user where username=req.query.username and password=req.query.pwd

		let username=req.query.username || req.body.username;
		let pwd=req.query.pwd || req.body.pwd;
		// console.log(req,username,pwd)

		//1.创建一个连接对象
		let db=mydb.dbconfig()

		//2.发起连接
		db.connect()

		//3.查询
		db.query("select * from t_user where username=? and pwd=?",
				[username,pwd],
				(err,data)=>{
						console.log(err);
						console.log(data);
						if(!err){
							if(data.length>0){
								resp.send("登录成功")
								//重定向:redirect
								// resp.redirect("success.html")
							}else{
								resp.send("用户名或密码错误!!!")
							}
						}else {
							resp.send(err);
						}
		})

	},


	//注册
	addUser(req,resp){
		let username=req.query.username || req.body.username;
		let pwd=req.query.pwd || req.body.pwd;
		//1.创建一个连接对象
		let db=mydb.dbconfig()

		//2.发起连接
		db.connect()

		//3.查询
		db.query("insert into t_user values(?,?,?)",
			[null,username,pwd],
			(err,data)=>{
				console.log(err);
				console.log(data);
				if(!err){
					resp.send("注册成功")
				}else {
					resp.send(err);
				}
			})
	}

}


//公开文件
module.exports=userController;

使用数据库创建连接池,就可以不用每次都创建连接对象了

const mysql=require("mysql");
//引入封装好的连接池
const mydbpool=require("../config/dbpoolconfig");


const userController={
	//登录
	getUser(req,resp){
		let username=req.query.username || req.body.username;
		let pwd=req.query.pwd || req.body.pwd;

		//1.创建一个连接池
		const pool=mysql.createPool({
			//主机地址
			host:"localhost",
			//数据库端口号
			port:"3306",
			//数据库用户名、密码
			user:"root",
			password:"root",
			//哪个数据库
			database:"beike"
		});

		//2.发起连接
		pool.getConnection((err,connection)=>{  //err:连接池连接失败的信息,connection:连接池连接成功的对象
			console.log(err);  //没有错误信息null
			console.log(connection); //对象

			//3.发起sql语句
			connection.query("select * from t_user where username=? and pwd=?",
				[username,pwd],
				(err,data)=>{
					console.log(err); //如果数据库出错err就有值,否则为null
					console.log(data);
					if(!err){
						//数据库语句执行成功之后会做的事情,事情不是定死的(可能是函数:用户自己定的)
						if(data.length>0){
							resp.send("登录成功")
						}else{
							resp.send("用户名或密码错误!!!")
						}
					}else {
						resp.send(err);
					}
				}
			)

			//4.释放本次连接
			connection.release();

		})
	},


	//注册(使用封装好的连接池)
	addUser(req,resp){
		let username=req.query.username || req.body.username;
		let pwd=req.query.pwd || req.body.pwd;

		mydbpool.connect("insert into t_user values(?,?,?)",
			[null,username,pwd],
			(err,data)=>{
				if(!err){
					resp.send("注册成功")
				}else {
					resp.send(err);
				}
			}
		)
	}


}



//公开文件
module.exports=userController;
const mydb=require("../config/dbconfig")

const studentController={

	addStudent(req,resp){
		//拿到输入框中输入的内容
		let stuname=req.query.stuname;
		let phone=req.query.phone;
		let sex=req.query.sex;
		let classid=req.query.classid;
		let age=req.query.age;
		let garde=req.query.garde;
		// console.log(req.query,stuname,phone,sex,classid,age,garde)

		//创建连接对象
		let db=mydb.dbconfig(); //return db
		//发起连接
		db.connect();
		//查询
		db.query(
			//参数1:SQL语句
			"insert into t_student values(?,?,?,?,?,?,?)",
			//参数2:什么数据
			[null,stuname,phone,sex,classid,age,garde],
			//参数3:函数
			(err,data)=>{
				console.log(err);
				console.log(data);
				if(!err){
					resp.send("新增学生成功!!")
				}else{
					resp.send(err)
				}
			}
		)
	}

}

module.exports = studentController;

config ( 配置文件 )

数据库连接封装:dbconfig.js

const mysql=require("mysql");

module.exports.dbconfig=function () {

	const db=mysql.createConnection({
		//连谁的数据库(ip地址)
		host:"localhost",
		//数据库端口号
		port:"3306",
		//数据库用户名、密码
		user:"root",
		password:"root",
		//哪个数据库
		database:"beike"
	});

	return db;

}

数据库连接池封装:dbpoolconfig.js
在这里插入图片描述

const mysql=require("mysql");

const dbpool={
	//连接池本身是个对象
	pool:{},
	//池子的配置
	config:{
		host:"localhost",
		port:"3306",
		user:"root",
		password:"root",
		database:"beike"
	},

	//1.创建连接池
	create(){
		this.pool=mysql.createPool(this.config)
	},

	//需要提供:数据库语句,sql参数,回调函数做什么
	connect(sql,arr,fn){
		//2.发起连接
		this.pool.getConnection((err,connection)=>{
			//3.发起query查询
			connection.query(sql,arr,fn)

			//4.释放连接
			connection.release()
		})
	}

}

//先自己调一下,把连接池建好(有连接了就用池子里的连接就好了,不需要重复创建池子)
dbpool.create()

//对外公开
module.exports = dbpool;

404 ( 错误页面 )

404.html

<h1>404页面</h1>

<script>
    //过3秒后跳回主页
    window.onload=function () {
        setTimeout(()=>{
			location.href="index.html"
        },3000)
	}
</script>

app.js

//path(针对低版本node)---路径处理模块
const path=require("path")



//返回404(路由没有拦截,静态资源里也没找到),以下内容写在静态资源配置之后
app.use((req,resp)=>{
    //状态码
    resp.status(404);
    //发送一个文件给你,而不是跳转页面
    // resp.sendFile(__dirname+"/public/404.html");

    //如果遇到低版本的node,路径处理(上面的路径语法就会出问题,解决方式:引入path模块 npm install path --save-dev)
    resp.sendFile(path.join(__dirname,"public","404.html"))

    // resp.redirect("404.html")  //redirect重定向,状态码:302(它不是错误,现在要的是状态码:404)
})

自动更新工具

修改服务器的文件后不需要重启:

npm install -g nodemon

项目运行的指令:

nodemon app.js  或  nodemon app (当前项目根目录下)

停止运行:

Ctrl + C  (终端命令行)

ajax ( 前后台数据交互 )

所有的请求从哪里发出去,响应的内容就原路返回哪里( ajax:Asynchronous javascipt and XML )
在这里插入图片描述

<!-- form表单有默认提交行为,并且提交整个页面;ajax不会提交整个页面 -->
<style>
    #msg,#msgP{
        color: red;
    }
</style>

<form action="/register.do" method="get">
    <h1>get请求</h1>
    用户名:<input type="text" id="username" onblur="checkUser()" name="username">
    <span id="msg"></span> <!--提示正确或者错误的信息-->
    密码:<input type="text" name="password">
    <button>提交</button>
</form>

<form action="/register.do" method="post">
    <h1>post请求</h1>
    用户名:<input type="text" id="usernameP" onblur="checkUserP()" name="username">
    <span id="msgP"></span> <!--提示正确或者错误的信息-->
    密码:<input type="text" name="password">
    <button>提交</button>
</form>

<script src="js/day4.js"></script>
//get请求
function checkUser() {
	//获取输入框的值
	let usernameV=document.getElementById("username").value;

	//1.创建XMLHttpRequest对象,xhr
	let xhr=new XMLHttpRequest() || new ActiveXObject("Microsoft.XMLHTTP");

	//2.注册回调函数
	xhr.onreadystatechange=function (){  //当状态发生改变时
		//readyState:0-未初始化  1-读取中  2-已读取  3-交互中  4-完成
		console.log(xhr.readyState)

		if(xhr.readyState==4){ //与服务器交互完毕
			console.log(xhr.responseText)
			document.getElementById("msg").innerHTML=xhr.responseText
		}
	}

	//3.向服务器发起连接
	xhr.open("get","/checkUser.do?username="+usernameV);

	//4.发送参数(post)
	xhr.send(null)
}




//post请求
function checkUserP() {
	let usernameV=document.getElementById("usernameP").value;

	let xhr=new XMLHttpRequest() || new ActiveXObject("Microsoft.XMLHTTP");

	xhr.onreadystatechange=function (){
		console.log(xhr.readyState)

		if(xhr.readyState==4){
			console.log(xhr.responseText)
			document.getElementById("msgP").innerHTML=xhr.responseText
		}
	}

	//向服务器发起连接
	//xhr.open(请求方法,请求地址,布尔值)  true-异步请求,false-同步请求
	xhr.open("post","/checkUserP.do",true);

	//post请求需要设置请求头(如果是form表单提交的话,请求头是自动添加的)
	xhr.setRequestHeader("Content-type","application/x-www-form-urlencoded")

	//post请求的参数设置
	xhr.send("username="+usernameV)

}

XMLHttpRequest对象:
(1)可以在不向服务器提交整个页面的情况下,实现局部更新网页
(2)在页面已加载后从服务器请求数据
(3)在页面已加载后从服务器接收数据
(4)在后台向服务器发送数据

使用ajax的步骤:
(1)客户端事件发生,调用js函数编写事件处理程序
(2)在事件的处理程序中创建XMLHttpRequest对象
(3)配置XMLHttpRequest对象
(4)XMLHttpRequest对象会发送一个异步的http请求到服务器
(5)服务器接收到请求,处理请求,返回响应
(6)XMLHttpRequest对象的回调函数接收并处理结果
(7)更新页面dom

阻塞与非阻塞:js是阻塞加载方式 ( 同步 ),在服务器使用ajax可以实现非阻塞 ( 异步 )

<input type="text" placeholder="请输入用户名" id="search">
<button onclick="searchUser()">搜索</button>

<!--	<button οnclick="getAllUser()">查询</button>-->
	<table cellpadding="0" cellspacing="0" border="1" width="300" align="center">
		<thead>
			<tr>
				<th>用户名</th>
				<th>密码</th>
				<th>操作</th>
			</tr>
		</thead>
		<tbody  id="userTable">

		</tbody>
	</table>

<script src="js/table.js"></script>
window.onload=function () {
	getAllUser();
}

//查询所有用户
function getAllUser() {
	//清空
	document.getElementById("userTable").innerHTML="";

	//1.创建xhr对象
	let xhr=new XMLHttpRequest();
	//2.注册回调函数
	xhr.onreadystatechange=function () {
		console.log(xhr.responseText)

		//拿到响应回来的数据之后要干什么
		if(xhr.readyState==4 && xhr.status==200){

			console.log(typeof xhr.responseText)  //一堆长得像数组和对象的字符串
			let data=JSON.parse(xhr.responseText) //转为json对象
			console.log(data[0])

			for(let i=0;i<data.length;i++){
				document.getElementById("userTable").innerHTML+=
					`<tr>
						<td>${data[i].username}</td>
						<td>${data[i].pwd}</td>
						<td><button>删除</button></td>
					</tr>`
			}
		}
	}
	//3.发送请求-连接服务器
	xhr.open("get","/getAllUser.do")
	//4.发送参数
	xhr.send(null)
}


//搜索用户
function searchUser() {
	document.getElementById("userTable").innerHTML="";

	let username=document.getElementById("search").value;
	// console.log(username)

	let xhr=new XMLHttpRequest();

	xhr.onreadystatechange=function () {
		if(xhr.readyState==4 && xhr.status==200){

			let data=JSON.parse(xhr.responseText)

			for(let i=0;i<data.length;i++){
				document.getElementById("userTable").innerHTML+=
					`<tr>
						<td>${data[i].username}</td>
						<td>${data[i].pwd}</td>
						<td><button>删除</button></td>
					</tr>`
			}
		}
	}

	xhr.open("post","/searchUser.do")
	//请求头的设置
	xhr.setRequestHeader("Content-type","application/x-www-form-urlencoded")
	//发参数(自己拼)
	xhr.send("myUsername="+username)
}

在这里插入图片描述

indexRouter.js文件

//引入express来实现路由功能
const express=require("express");
//引入控制层
const userController=require("../controller/userController")


//调用express对象提供的路由方法,返回路由对象
const router=express.Router();

router.get("/checkUser.do",userController.checkUser)
router.post("/checkUserP.do",userController.checkUserP)
//获取所有数据
router.get("/getAllUser.do",userController.getAllUser)
//搜索
router.post("/searchUser.do",userController.searchUser)


//公开路由
module.exports=router;

userController.js文件

const dbpool=require("../config/dbpoolconfig")

const userController={
	//get请求
	checkUser(req,resp){
		// console.log(req.query.username)

		dbpool.connect("select * from t_user where username=?",
			[req.query.username],
			(err,data)=>{
				if(!err){ //数据库的sql语句没有问题
					if(data.length>0){ //查到了
						resp.send("用户名已被注册")
					}else{  //没查到
						resp.send("用户名可以使用")
					}
				}else{  //sql语句有错
					resp.send(err)
				}
			})
	},


	//post请求
	checkUserP(req,resp){
		// console.log(req.body.username)

		dbpool.connect("select * from t_user where username=?",
			[req.body.username],
			(err,data)=>{
				if(!err){ //数据库的sql语句没有问题
					if(data.length>0){ //查到了
						// resp.redirect("success.html")---在ajax中的错误操作(1.请求响应是一问一答式,不是一问两答;2.更新页面局部细节,不是更新整个页面)
						resp.send("用户名已被注册")
					}else{  //没查到
						resp.send("用户名可以使用")
					}
				}else{  //sql语句有错
					resp.send(err)
				}
			})
	},


	//获取所有数据
	getAllUser(req,resp){
		dbpool.connect("select * from t_user",[],(err,data)=>{
			if(!err){
				resp.send(data) //send只能返回字符串数据
			}else{
				resp.send(err)
			}
		})
	},


	//搜索
	searchUser(req,resp){
		console.log(req.body.myUsername);
		dbpool.connect("select * from t_user where username like ?",
			["%"+req.body.myUsername+"%"],
			(err,data)=>{
				if(!err){
					resp.send(data)
				}else{
					resp.send(err)
				}
			})
	}
}

module.exports = userController;

ajax ( 原生ajax的封装 )

ajax.js

//1.创建对象(写到外面-全局对象,其它地方才使用)
let xhr=new XMLHttpRequest();

//ajax请求
function myAjax(method,url,params,callback,async) {  //请求类型,请求地址,参数,回调函数

	//同步 or 异步
	if(async==undefined){
		async=true; //默认异步
	}

	//2.回调函数
	xhr.onreadystatechange=function () {
		if(xhr.readyState==4 && xhr.status==200){

			//从服务器得到响应 (拿到数据xhr.responseText) 之后会做什么
			callback();
		}
	}

	//3.打开连接,发送参数
	if(method=="get"){
		xhr.open(method,url+"?"+params,async)  //原样:"/getUser.do?unsrname=123&id=123"
		xhr.send(null)

	}else if(method=="post"){
		xhr.open(method,url,async)
		//请求头
		xhr.setRequestHeader("Content-type","Content-type","application/x-www-form-urlencoded")
		xhr.send(params)
	}
}

增删改查 ( 多条件查询 )

table.html

<body>
	<a href="addNew.html"><button>新增</button></a>

	<table cellpadding="0" cellspacing="0" border="1" width="300" align="center">
		<thead>
			<tr>
				<th>用户名</th>
				<th>密码</th>
				<th>操作</th>
			</tr>
		</thead>
		<tbody  id="userTable">

		</tbody>
	</table>


<script src="js/table.js"></script>
</body>

table.js 查询所有用户数据 / 删除

window.onload=function () {
	getAllUser();
}

//查询所有用户
function getAllUser() {
	//清空
	document.getElementById("userTable").innerHTML="";

	//1.创建xhr对象
	let xhr=new XMLHttpRequest();
	//2.注册回调函数
	xhr.onreadystatechange=function () {
		console.log(xhr.responseText) // 返回的内容

		//拿到响应回来的数据之后要干什么
		if(xhr.readyState==4 && xhr.status==200){

			if(xhr.responseText!="没有查到该数据"){

				console.log(typeof xhr.responseText)  //一堆长得像数组和对象的字符串
				let data=JSON.parse(xhr.responseText) //转为json对象
				console.log(data[0])

				for(let i=0;i<data.length;i++){
					document.getElementById("userTable").innerHTML+=
					`<tr>
						<td>${data[i].username}</td>
						<td>${data[i].pwd}</td>
						<td>
							<button οnclick="delUser(${data[i].id})">删除</button>
							<a href="detail.html?id=${data[i].id}">
								<button>修改</button>
							</a>
						</td>
					</tr>`
				}

			}else{
				//在表格中展示从服务器返回的文字:没有查到该数据
				document.getElementById("userTable").innerHTML=
					`<tr>
						<td colspan="3">${xhr.responseText}</td>
					</tr>`
			}

		}
	}
	//3.发送请求-连接服务器
	xhr.open("get","/getAllUser.do")
	//4.发送参数
	xhr.send(null)
}



//删除
function delUser(obj){
	let id=obj;

	let xhr=new XMLHttpRequest();
	xhr.onreadystatechange=function () {
		if(xhr.readyState==4 && xhr.status==200){
			alert(xhr.responseText)

			// 刷新页面
			getAllUser();
		}
	}

	xhr.open("get","/delUser.do?id="+id)
	xhr.send(null)
}

addNew.html 新增

<body>
	<h1>新增用户</h1>

	用户名:<input type="text" name="addUsername">
	密码:<input type="text" name="addPassword">

	<button onclick="addNew()">新增</button>


<script>
	
	function addNew() {
		let myUsername=document.getElementsByName("addUsername")[0].value
		let myPassword=document.getElementsByName("addPassword")[0].value

		let xhr=new XMLHttpRequest();

		xhr.onreadystatechange=function () {
			console.log(xhr.responseText)

			if(xhr.readyState==4 && xhr.status==200){
				alert(xhr.responseText)

				//新增成功后页面跳转
				location.href="table.html"
			}
		}

		xhr.open("post","/addNew.do",true)

		//post请求需要设置请求头(如果是form表单提交的话,请求头是自动添加的)
		xhr.setRequestHeader("Content-type","application/x-www-form-urlencoded")

		//post请求的参数设置
		xhr.send("username="+myUsername+"&pwd="+myPassword)
	}

</script>
</body>

detail.html 修改

<body>
	<h1>用户详情页</h1>
	用户名:<input type="text" id="username">
	密码:<input type="text" id="password">
	<button onclick="changeUser()">修改</button>

	<script>
		window.onload=function () {
			getUser()
		}

		//获取用户详情-需要发ajax请求
		function getUser(){
			console.log(location) //通过js跳转页面跟地址栏url相关:location

			//创建对象
			let xhr=new XMLHttpRequest();
			//回调函数
			xhr.onreadystatechange=function () {
				if(xhr.readyState==4 && xhr.status==200){
					console.log(xhr.responseText)
					//转为对象
					let data=JSON.parse(xhr.responseText)

					//找节点
					document.getElementById("username").value=data[0].username;
					document.getElementById("password").value=data[0].pwd;
				}
			}
			//发请求-open打开和服务器的连接
			xhr.open("get","/getUser.do"+location.search)
			//参数
			xhr.send(null)
		}



		//修改后提交到数据库
		function changeUser(){
			//找节点获取input框中重新输入的值
			let username=document.getElementById("username").value;
			let pwd=document.getElementById("password").value;

			let xhr=new XMLHttpRequest();
			xhr.onreadystatechange=function () {
				if (xhr.readyState == 4 && xhr.status == 200) {
					alert(xhr.responseText)

					//修改成功后页面跳转
					location.href="table.html"

				}
			}
			xhr.open("get","/changeUser.do"+location.search+"&username="+username+"&pwd="+pwd)
			xhr.send(null)
		}

	</script>
</body>

indexRotue.js

//1.引入express来实现路由功能
const express=require("express");
//引入控制层(分发到控制层,去处理具体的业务逻辑)
const userController=require("../controller/userController")
const studentController=require("../controller/studentController")
const studentPagingController=require("../controller/studentPagingController")


//2.调用express对象提供的路由方法,返回路由对象
const router=express.Router();


//-----------------------------路由拦截--------------------------
//获取所有用户数据
router.get("/getAllUser.do",userController.getAllUser)
//修改时获取指定用户的详情信息
router.get("/getUser.do",userController.getUser)
//修改
router.get("/changeUser.do",userController.changeUser)
//新增
router.post("/addNew.do",userController.addNew)
//删除
router.get("/delUser.do",userController.delUser)


//获取所有学生信息
router.get("/getAllStudent.do",studentController.getAllStudent)
//搜索学生-多条件查询
router.get("/mySearch.do",studentController.mySearch)


//获取总共有多少页
router.get("/getPageTotal.do",studentPagingController.getPageTotal)
//获取当前页的学生
router.get("/getCurrentPageStudent.do",studentPagingController.getCurrentPageStudent)



//3.公开路由
module.exports=router;

userController.js

const dbpool=require("../config/dbpoolconfig")

const userController={

	//获取所有数据
	getAllUser(req,resp){
		dbpool.connect("select * from t_user",[],(err,data)=>{
			//err:sql语句是否出错
			if(!err){
				if(data.length>0){  //查到人
					resp.send(data) //send只能返回字符串数据
				}else{ //没有查到数据
					// resp.send("[]") 因为要转为json对象所以返回的是数组
					resp.send("没有查到该数据")
				}

			}else{
				resp.send(err)  //sql语句出错
			}
		})
	},




	//修改用户的时候,获取指定用户
	getUser(req,resp){
		console.log(req.query.id)

		dbpool.connect("select * from t_user where id=?",
			[req.query.id],
			(err,data)=> {
				if(!err){
					if(data.length>0){
						resp.send(data)
					}
				}else{
					resp.send(err)
				}
		})

	},



	//修改
	changeUser(req,resp){
		console.log(req.query)

		//修改数据库数据
		dbpool.connect("update t_user set username=?,pwd=? where id=?",
			[req.query.username,req.query.pwd,req.query.id],
			(err,data)=>{
				if(!err){
					resp.send("修改成功")
				}else{
					resp.send(err)
				}
			})
	},



	//新增
	addNew(req,resp){
		console.log(req.body)

		dbpool.connect("insert into t_user values(?,?,?)",
			[null,req.body.username,req.body.pwd],
			(err,data)=>{
				if(!err){
					resp.send("新增成功")
				}else{
					resp.send(err)
				}
			})

	},



	//删除
	delUser(req,resp){
		console.log(req.query.id)

		dbpool.connect("delete from t_user where id=?",
			[req.query.id],
			(err,data)=>{
				if(!err){
					resp.send("删除成功")
				}else{
					resp.send(err)
				}
			})
	}

}

module.exports = userController;

student.html 多条件查询

<body>
	<h1>搜索-多条件</h1>
	<p>
		姓名:<input type="text" id="stuname"><!--模糊查询-->
		学生编号:<input type="text" id="stuid"><!--精准查询-->
		分数:<input type="text" id="grade">
		性别:<input type="text" id="sex">

		<button onclick="searchStudent()">搜索</button>
	</p>


	<table cellspacing="0" cellpadding="0" border="1" width="500" align="center">
		<thead>
			<tr>
				<th>学生编号</th>
				<th>学生姓名</th>
				<th>学生分数</th>
				<th>班级id</th>
				<th>性别</th>
			</tr>
		</thead>
		<tbody align="center" id="studentTable">

		</tbody>
	</table>

	<script src="../js/ajax.js"></script>
	<script>
		window.onload=function () {
			getAllStudent()
		}

		//获取所有学生
		function getAllStudent() {
			//发起一个ajax请求
			myAjax("get","/getAllStudent.do","",showStudent);
		}

		//要干什么事情-------展示数据
		function showStudent() {
			console.log(xhr.responseText)
			//清空
			document.getElementById("studentTable").innerHTML=""

			if(xhr.responseText!="没有查到该数据"){
				//转格式(转为json对象)
				let data=JSON.parse(xhr.responseText)

				//循环  {"stuid":1,"stuname":"拉拉","phone":"1235","sex":"女","classid":2,"age":20,"grade":11}
				for(let i=0;i<data.length;i++){
					document.getElementById("studentTable").innerHTML+=
						`<tr>
							<td>${data[i].stuid}</td>
							<td>${data[i].stuname}</td>
							<td>${data[i].grade}</td>
							<td>${data[i].classid}</td>
							<td>${data[i].sex}</td>
						</tr>`
				}

			}else{
				document.getElementById("studentTable").innerHTML=
					`<tr>
							<td colspan="5">${xhr.responseText}</td>
					</tr>`
			}
		}




		//搜索学生
		function searchStudent(){
			//获取每个输入框中的值
			let stuname=document.getElementById("stuname").value
			let stuid=document.getElementById("stuid").value
			let grade=document.getElementById("grade").value
			let sex=document.getElementById("sex").value
			// console.log("stuname="+stuname+"&stuid="+stuid+"&grade="+grade+"&sex="+sex)

			//发起ajax请求
			myAjax("get","/mySearch.do","stuname="+stuname+"&stuid="+stuid+"&grade="+grade+"&sex="+sex,showStudent)
		}
		
	</script>
</body>

studentController.js 多条件查询的数据库语句

const dbpool=require("../config/dbpoolconfig")

const studentController={

	//获取所有学生
	getAllStudent(req,resp){
		console.log(req.query)

		dbpool.connect("select * from t_student",
			[],
			(err,data)=>{

				if(!err){
					if(data.length>0){
						resp.send(data)
					}else{
						resp.send("没有查到该数据")
					}

				}else {
					resp.send(err)
				}
			})
	},




	//搜索学生--多条件查询
	mySearch(req,resp){
		console.log(req.query)
		let stuname=req.query.stuname;
		let stuid=req.query.stuid;
		let grade=req.query.grade;
		let sex=req.query.sex;
		// console.log(stuname,stuid,grade,sex)

		//输入框中不一定有内容导致sql语句不确定,变量个数也不确定
		let sql="select * from t_student where 1=1 ";
		let params=[];

		if(stuname!=""){
			sql+="and stuname like ?";
			stuname="%"+stuname+"%";  //模糊查询:%张%
			params.push(stuname);
		}
		if(stuid!=""){
			sql+=" and stuid=?";
			params.push(stuid);
		}
		if(grade!=""){
			sql+=" and grade=?";
			params.push(grade);
		}
		if(sex!=""){
			sql+=" and sex=?";
			params.push(sex);
		}
		console.log(sql,params)


		dbpool.connect(sql,params,
			(err,data)=>{
					if(!err){
						if(data.length>0){
							resp.send(data)
						}else{
							resp.send("没有查到该数据")
						}
					}else{
						resp.send(err)
					}
			})
	}

}

module.exports = studentController;

分页 ( 极简版 )

效果:

在这里插入图片描述
studentPaging.html

<body>
	<table cellspacing="0" cellpadding="0" border="1" width="500" align="center">
		<thead>
			<tr>
				<th>学生编号</th>
				<th>学生姓名</th>
				<th>学生分数</th>
				<th>班级id</th>
				<th>性别</th>
			</tr>
		</thead>
		<tbody id="studentTable" align="center">
					<!--具体数据-->
		</tbody>
	</table>

	<div>
		<button onclick="prevPage()">上一页</button>
		<button onclick="nextPage()">下一页</button>

		<span>总共 <span id="totalPage"></span></span>
	</div>


<script src="../js/ajax.js"></script>
<script>
	let currentPage=1; //当前在第几页
	let pageTotal=0; //总页数


	window.onload=function () {
		//当前页的学生数据
		getCurrentPageStudent();
		//获取总共多少页
		getPageTotal();  /*这里会出问题,因为ajax是单线程,无法同时并进发两条请求*/
	}



	//获取当前页的学生数据
	function getCurrentPageStudent() {
		myAjax("get","/getCurrentPageStudent.do","currentPage="+currentPage,showStudent,false);  /*因为ajax是单线程所以写false,让它是同步请求,完成这个请求再去完成其它的*/
	}



	//总页数
	function getPageTotal() {
		let totalPage=document.getElementById("totalPage")
		// let numberBlock=document.getElementById("numberBlock")

		myAjax("get","/getPageTotal.do","",function () {
			console.log(xhr.responseText);
			//拿到服务器返回的总页数,给总页数变量赋值
			pageTotal=xhr.responseText;

			totalPage.innerHTML=currentPage+"/"+pageTotal;
		})
	}




	let totalPage=document.getElementById("totalPage")
	//上一页
	function prevPage(){
		currentPage--;

		if(currentPage<1){
			alert("这是第一页")
			currentPage=1;
		}else{
			getCurrentPageStudent();
			// showPageBtn(currentPage);
			totalPage.innerHTML=currentPage+"/"+pageTotal;
		}
	}



	//下一页
	function nextPage(){
		currentPage++; //2

		if(currentPage>pageTotal){ //是否超出最大页
			alert("这是最后一页");
			currentPage=pageTotal;
		}else{
			getCurrentPageStudent();
			// showPageBtn(currentPage);
			totalPage.innerHTML=currentPage+"/"+pageTotal;
		}
	}



	//展示数据
	function showStudent() {
		console.log(xhr.responseText)
		//清空
		document.getElementById("studentTable").innerHTML=""

		if(xhr.responseText!="没有查到该数据"){
			//转格式(转为json对象)
			let data=JSON.parse(xhr.responseText)

			//循环  {"stuid":1,"stuname":"拉拉","phone":"1235","sex":"女","classid":2,"age":20,"grade":11}
			for(let i=0;i<data.length;i++){
				document.getElementById("studentTable").innerHTML+=
					`<tr>
							<td>${data[i].stuid}</td>
							<td>${data[i].stuname}</td>
							<td>${data[i].grade}</td>
							<td>${data[i].classid}</td>
							<td>${data[i].sex}</td>
						</tr>`
			}

		}else{
			document.getElementById("studentTable").innerHTML=
					`<tr>
							<td colspan="5">${xhr.responseText}</td>
					</tr>`
		}
	}

</script>
</body>

studentPagingController.js

const dbpool=require("../config/dbpoolconfig")

const studentPagingController={

	//获取总页数
	getPageTotal(req,resp){
		//每页展示多少条
		let pageCount=3;

		//查条数
		dbpool.connect("select count(*) as totalCount from t_student",[],(err,data)=>{

			//算页数
			let pageNumber=Math.ceil(data[0].totalCount/pageCount)
			pageNumber=String(pageNumber) //转为字符串
			console.log(pageNumber)

			if(!err){
					resp.send(pageNumber)  //服务器返回不能随便放数字,它会认为是状态码
			}else{
					resp.send(err)
			}
		})

	},



	//获取当前页的学生数据
	getCurrentPageStudent(req,resp){
		//当前在哪页
		let currentPage=req.query.currentPage;
		//每页展示多少条
		let pageCount=3;
		/*
		*	limit 从第几条开始但不包括本身,需要多少条
		*
		* 	1-----0,3
		* 	2-----3,3
		* 	3-----6,3
		* */

		dbpool.connect("select * from t_student limit ?,?",
			[(currentPage-1)*pageCount,pageCount],
			(err,data)=>{

				if(!err){
					if(data.length>0){
						resp.send(data)
					}else{
						resp.send("没有查到该数据")
					}

				}else {
					resp.send(err)
				}
			})
	},


}


module.exports = studentPagingController;

dao ( 数据库交互 ) 、promise ( 对象 )

服务器分层:Dao

<form action="/regUser.do" method="post">
	<h1>未分层Dao</h1>
	用户名:<input type="text" name="username">
	密码:<input type="text" name="password">
	<button>注册</button>
</form>

<form action="/regUserDao.do" method="post">
	<h1>分层Dao</h1>
	用户名:<input type="text" name="usernameDao">
	密码:<input type="text" name="passwordDao">
	<button>注册</button>
</form>

promise.html

<body>
	<!-- promise对象-它可以获取异步操作的消息 -->
	<form action="login.do" method="post">
		用户名:<input type="text" name="username">
		密码:<input type="text" name="password">
		<button>登录</button>
	</form>

<script>
	/*Promise:构造函数,reject:异步操作执行错误时的方法,resolve:异步操作执行成功时的方法*/
	console.dir(Promise);
	//实例化Promise对象
	/*new Promise(
		function (resolve,reject) {
			if(!err){
				resolve(data)
			}else{
				reject(err)
			}
		}
	)*/

	

	/*then:然后做什么

	缺点:1.无法取消Promise,一旦新建它就会立即执行,无法中途取消
		 2.如果不设置回调函数,Promise内部抛出的错误,不会反应到外部
		 3.当处于pending状态时,无法得知当前进展到哪一个阶段(刚刚开始还是即将完成)
	 */
	/*let p=new Promise(function (resolve, reject) {
		
	}).then(function (data) {
		//返回你想要返回的值
	}).then(function (data1) {
		//拿到上一次返回的值
		//做点儿什么
	}).then(function (data2) {

	})*/





	//多层嵌套回调函数
	/*function fun1(arr1,function(data1){

		function fun2(arr2,function(data2){
			//导致回调深渊,眼睛都要看花,所以用.then
		})
	})*/



	/*function plane(){
		//写属性
		this.x=7;
		this.y=9;
	}
	plane.prototype.move=function () {
		//给构造函数添加方法
	}
	console.dir(plane)

	let pl=new plane()
	pl.move()*/

</script>
</body>

indexRoute.js

//1.引入express来实现路由功能
const express=require("express");
//2.调用express对象提供的路由方法,返回路由对象
const router=express.Router();

//引入控制层(分发到控制层,去处理具体的业务逻辑)
const userController=require("../controller/userController")


//-----------------------------路由拦截--------------------------
//注册-未分层Dao
router.post("/regUser.do",userController.regUser)
//注册-分层Dao
router.post("/regUserDao.do",userController.regUserDao)


//jquery的Ajax请求
router.post("/subJqueryAjax.do",userController.regUser)

//登录(Promise)
router.post("/login.do",userController.checkUserPromise)


//3.公开路由
module.exports=router;

userController.js

const dbpool=require("../config/dbpoolconfig")
//引入Dao层的文件
const userModel=require("../dao/userDao")

const userController={

	//未分层Dao
	regUser(req,resp){
		dbpool.connect("insert into t_user values(?,?,?)",
			[null,req.body.username,req.body.password],
			(err,data)=>{
				if(!err){
					resp.send("注册成功")
				}else{
					resp.send(err)
				}
		})
	},


	//分层Dao
	regUserDao(req,resp){
		//使用Dao文件中的方法
		userModel.daoAddUser([null,req.body.usernameDao,req.body.passwordDao],
			function (result) {
						// console.log(result)

						//等待数据库sql语句的执行结果,可以对结果做些处理再resp.send回去
						resp.send("注册成功");
			}
		)

	}




	//登录(不使用Promise)
	checkUser(req,resp){
		userModel.daoCheckUser([req.body.username,req.body.password],
			function (data) {
				console.log(data)
				resp.send("登录成功")
			})
	},



	//登录(使用Promise)
	checkUserPromise(req,resp){
		//第1次查询(返回Promise对象)
		userModel.daoCheckUserPromise([req.body.username,req.body.password])
			.then(function (data) {
				console.log("==Promise查到了一堆数据,拿到第1个用户的id去return==")
				// console.log(data)

				return data[0].id
			})
			.catch(function (err) {
				//有错的情况下会执行catch,Promise出错的话服务器不会挂掉它还在那儿,不会影响Promise外部其它内容的执行
				console.log(err)
				resp.send(err)
			})
			.then(function (data2) {
				// console.log(data2)

				//在第1次查询的基础上,进行第2次查询
				userModel.daoCheckUserPromise2([data2])
					.then(function (data3) {
						// console.log(data3)

						return data3[0].username
					})
					.then(function (data4) {
						// console.log(data4)

						//在第2次查询的基础上,进行第3次查询
						userModel.daoCheckUserPromise3([data4])
							.then(function (data5) {
								// console.log(data5)

								resp.send(data5)
							})
					})
			})
	},


}

module.exports = userController;

userDao.js

const dbpool=require("../config/dbpoolconfig")

const userModel={

	//注册(Dao)
	daoAddUser(params,callback){    //params是数组  callback是函数

		//发起数据库语句添加一个用户
		/*dbpool.connect("insert into t_user values(?,?,?)",
						params,
						(err,data)=>{
							if(!err){
								//拿到数据库返回的结果之后要做什么事情:函数
								callback(data);
							}else{
								callback(err);
							}
						}
		)*/

		dbpool.connect("insert into t_user values(?,?,?)",
						params,
						(err,data)=>{
								if(!err){
									callback(data);  //把查到的数据(data)作参数传回去
								}
						}
		)

	},
	


	//登录(不使用Promise)
	daoCheckUser(params,callback){
		dbpool.connect("select * from t_user where username=? and pwd=?",
			params,
			(err,data)=>{
				if(!err){
					callback(data)
				}
			})
	},


	//登录(使用Promise)第1次查
	daoCheckUserPromise(params){
		//返回的是Promise对象
		return new Promise(function (resolve, reject) {
			//数据库连接
			dbpool.connect("select * from t_user where username=? and pwd=?",
				params,
				(err,data)=>{
					if(!err){
						resolve(data)
					}else{
						reject(err)
					}
			})

		})
	},

	//登录(使用Promise)第2次查
	daoCheckUserPromise2(params){
		return new Promise(function (resolve, reject) {
			dbpool.connect("select * from t_user where id=?",
				params,
				(err,data)=>{
					if(!err){
						resolve(data)
					}else{
						reject(err)
					}
				})

		})
	},

	//登录(使用Promise)第3次查
	daoCheckUserPromise3(params){
		return new Promise(function (resolve, reject) {
			dbpool.connect("select * from t_user where username=?",
				params,
				(err,data)=>{
					if(!err){
						resolve(data)
					}else{
						reject(err)
					}
				})

		})
	}

}

module.exports=userModel;

ajax ( jQuery的ajax请求 )

jqueryAjax.html

<body>
	<form id="myForm">
		用户名:<input type="text" name="username">
		密码:<input type="text" name="password">

		<!--不带提交功能的按钮-->
		<button type="button" onclick="subJqueryAjax()">提交表单</button>
		<button type="button" onclick="getSerial()">获取表单所有数据(序列化数据)</button>
	</form>


	<script src="../js/jquery.3.6.js"></script>
	<script>

		//通过使用jQuery发送ajax请求(不常用,因为jQuery代码太多了集成了很多用不上的东西,引入jQuery只为了用它的ajax功能,会让项目变得臃肿)
		function subJqueryAjax() {

			$.ajax({
				type:"post",
				url:"/subJqueryAjax.do",

				// data:{
				// 	username:$("[name='username']").val(),
				// 	password:$("[name='password']").val(),
				// },
				data:$("#myForm").serialize(),


				//成功返回的data是数据库返回的data或err
				success:function (data) {
					console.log("请求成功,获取到服务器的响应")
					console.log(data)
				},

				//失败返回的error是服务器的错误(如:请求地址不正确)
				error(xhr,status,error){
					console.log("请求失败,未获取到服务器的响应")
					console.log(error)
				}
			})

		}



		//表单序列化-找到表单里的所有东西
		function getSerial(){
			let data=$("#myForm").serialize()
			console.log(data)
		}

		//某个元素的序列化
		$("input").blur(function () {
			console.log($(this).serialize())
		})


	</script>
</body>

ejs ( 视图模板 )

(1) Embedded Javascript可以在html里面直接写js,是一个第三方模块:

npm install ejs --save

类似于MVC架构:
				M -> model模型
				V -> view视图(渲染数据,样式美化)真正的动态视图
				C -> controller控制器

(2) app.js中去配置(不需要require引入):

//path(针对低版本node)---路径处理模块
const path=require("path")


//EJS模块的配置 --- 视图文件的路径
// app.set("views",__dirname+"/views")
app.set("views",path.join(__dirname,"views"))
//后缀是.ejs的文件浏览器无法解析,需要用到专用引擎从服务器解析.ejs的文件
app.set("view engine","ejs")

(3) ejs支持在html标签中:

写变量:	<%=变量名%>

引入:	<%-include('引入的文件')%>

test.ejs

<head>
	<!--引入css样式-->
	<link rel="stylesheet" href="css/headerEjs.css">
</head>
<body>
	<!--引入公共部分-->
	<%-include('common/header.ejs')%>
	
	
	<h1>欢迎光临,请随意挑选!!!</h1>
	<div><%=username%></div>
	<div><%=mytext%></div>
</body>

header.ejs

<!--公共部分,不需要完整的html文件结构-->
<header>
    <h1>导航条</h1>
    <ul>
        <li>导航1</li>
        <li>导航2</li>
        <li>导航3</li>
    </ul>
</header>

indexEjsRoute.js

//1.引入express来实现路由功能
const express=require("express");
//2.调用express对象提供的路由方法,返回路由对象
const router=express.Router();
//引入控制层(分发到控制层,去处理具体的业务逻辑)
const studentEjsController=require("../controller/studentEjsController")
const productEjsController=require("../controller/productEjsController")


//-----------------------------路由拦截--------------------------

//对应请求名称,render返回对应的ejs视图页面、数据
router.get("/testEjs",(req,resp)=>{
	let u_name="月月";

	resp.render("test",{username:u_name,mytext:"测试文字"})
})


//页面数据
router.get("/studentListEjs",studentEjsController.getAllStudent)

//详情
router.get("/studentDetail.do",studentEjsController.getStudentDetail)



//主页
router.get("/indexEjs",productEjsController.getIndex)

//购买页
router.get("/buy.do",productEjsController.getBuy)


//3.公开路由
module.exports=router;

studentListEjs.ejs

<!--引入导航条-->
<%-include('common/header.ejs')%>

<!--表格-->
<table>
    <thead>
        <tr>
            <th>学生姓名</th>
            <th>学生性别</th>
            <th>学生成绩</th>
            <th>操作</th>
        </tr>
    </thead>
    <tboday>
    
        <!--ejs循环语法:开头-->
        <%for(let i=0;i<list.length;i++){%>
            <tr>
                <td><%=list[i].stuname%></td>
                <td><%=list[i].sex%></td>
                <td><%=list[i].grade%></td>
                <td>
                    <a href="/studentDetail.do?id=<%=list[i].stuid%>">详情</a>  <!--跳页面的同时把数据查出来-->
                </td>
            </tr>
        <%}%>
        <!--结尾-->
        
    </tboday>
</table>

studentDetail.ejs

<%-include('common/header.ejs')%>

<!--这是详情页面-->
<p>学生姓名:<%=username%></p>

studentEjsController.js

//引入Dao层的文件
const studentEjsModel=require("../dao/studentEjsDao")

const studentEjsController={

	//页面数据
	getAllStudent(req,resp){
		studentEjsModel.getAllStudentDao([]) //拿到dao中的getStudentList函数返回的Promise对象
			.then(function (data) {
				console.log(data)

				// if(data.length>0){
					resp.render("studentList",{list:data})
				// }
			})
	},


	//详情
	getStudentDetail(req,resp){
		console.log(req.query.id)
		studentEjsModel.getStudentDetailDao([req.query.id])
			.then((data)=>{
				console.log(data)

				resp.render("studentDetail",{username:data[0].stuname})
			})
	}

}

module.exports = studentEjsController;

studentEjsDao.js

const dbpool=require("../config/dbpoolconfig")

const studentEjsModel={

	//页面数据-Dao
	getAllStudentDao(params){
		return new Promise(function (resolve,reject) {
			dbpool.connect("select * from t_student",
				params,
				(err,data)=>{
					if(!err){
						resolve(data) //执行resolve可以在.then中获取到data
					}else{
						reject(err)
					}
				})
		})
	},


	//详情-Dao
	getStudentDetailDao(params){
		return new Promise(function (resolve, reject) {
			dbpool.connect("select * from t_student where stuid=?",
				params,
				(err,data)=>{
					if(!err){
						resolve(data)
					}else{
						reject(err)
					}
				})
		})
	}

}

module.exports=studentEjsModel;

index.ejs

<h1>index主页</h1>

<div>
    <a href="/buy.do">购买页</a>
</div>

buy.ejs

<h1>buy购买页</h1>

<%for(let i=0;i<product.length;i++){%>
    <div>
        <h2><%=product[i].p_name%></h2>
        <img src="<%=product[i].p_url%>" alt="">
        <p><%=product[i].p_des%></p>
    </div>
<%}%>

productEjsController.js

//引入Dao层的文件
const productEjsModel=require("../dao/productEjsDao")

const productEjsController={

	//主页
	getIndex(req,resp){
		resp.render("index")
	},

	//购买页
	getBuy(req,resp){
		productEjsModel.getBuyDao([])
			.then((data)=>{
				console.log(data)

				resp.render("buy",{product:data})
			})
	}

}

module.exports = productEjsController;

productEjsDao.js

const dbpool=require("../config/dbpoolconfig")

const productEjsModel={

	//购买页
	getBuyDao(params){
		return new Promise(function (resolve, reject) {
			dbpool.connect("select * from t_product",
				params,
				(err,data)=>{
					if(!err){
						resolve(data)
					}else{
						reject(err)
					}
				})
		})
	}

}

module.exports=productEjsModel;

‌cookie、session、token ( 身份识别验证 )

HTTP协议是无状态协议。web应用程序使用http协议传输数据 ( 本次请求响应完毕之后,客户端和服务器之间的连接就会关闭,下次再发请求需要重新建立新的连接,意味着服务器无法从连接上去跟踪会话 )

在这里插入图片描述
Cookie‌是一种由服务器发送到用户浏览器并存储在那里的小型文本数据。每次浏览器向服务器发送请求时,都会自动附带相应的Cookie信息(可通过设置生命时长maxAge,解决cookie无法删除问题)。Cookie存储在客户端,每个域的Cookie数量和大小有限制,存在被截取的风险主要用于识别用户和存储用户偏好设置

Session‌是在服务器端为每个用户创建的一个存储空间,用来保存用户会话所需的信息。每个Session都有一个唯一的标识符(Session ID),通常通过Cookie传递给客户端。Session存储在服务器端,不受容量限制,但会增加服务器负担并且不支持跨域主要用于处理用户认证和跨页面保持状态的应用场景

在这里插入图片描述
Token‌(尤其是JSON Web Tokens, JWT)是一种安全的认证方式,通过数字签名保证内容不被篡改Token存储在客户端,服务器只需验证Token的有效性,无需存储详细信息,适用于API认证和分布式系统中的身份验证。Token具有无状态性,减少了服务器资源消耗,并且支持跨域认证

在这里插入图片描述

cookie

<script>

	/*添加cookie的方法:多条cookie添加时必须一条一条的存*/
	document.cookie="user=lmy";
	document.cookie="pwd=123";

	//查看cookie
	console.log(document.cookie)

</script>

	<!--cookie设置后,浏览器会自动在请求头上添加cookie,格式如下:-->
	Cookie:Webstorm-2715bece=733540fb-d903-43eb-a62a-0518b5b0bde7;pwd=123;user=lmy


	<!--删除cookie:不能直接代码操作,因为cookie的api中没有提供直接将cookie删除的方法-->
	1.打开浏览器去手动删除

	2.通过设置有效时间来对cookie删除

查看某个网站给我的Cookie:(1) 按F12->选"应用"->“存储”->Cookie;(2) 按F12在控制台输入:javascript:alert(document.cookie)

浏览器一般只允许存放300个Cookie,每个站点(域名)最多存放20个Cookie,每个Cookie的大小限制为4KB

在这里插入图片描述
在这里插入图片描述

session

(1) 使用session要下载模块,并且cookie是搭配session使用,所以cookie模块也要下载:

npm install express-session -D
npm install cookie-parser -D

(2) 到服务器app.js文件中配置:

<!--引入模块-->
const session=require("express-session");
const cookieParser=require("cookie-parser");


<!--配置session and cookie(位置不限至少在日志后面)-->
app.use(cookieParser());
app.use(session({
	name:"demo",  <!--可不写,默认是name:"connect.sid"-->
	secret:"123456",  <!--秘钥,它会结合其它加密方式重新再生成一个别人无法解析的secret-->
	cookie:{
		maxAge:1000*60*60*24*30,  <!--cookie的有效期30天(单位:毫秒)-->
	},
	resave:true,  <!--更新session-cookie失效时间-->
	rolling:true,  <!--更新保存-->
	saveUninitialized:true,  <!--未初始化的cookie要不要保存(无论有没有session和cookie,每次请求都需要设置一个session和cookie)-->
}));


<!--不一定每次接待你的都是同一个服务器,为防止别的服务器不认识你,携带token去访问-->

清除浏览器历史记录会清空Cookie中的信息:

在这里插入图片描述
login.html

<h1>登录,如果登录成功的话就进入主页(index.ejs)</h1>

<form action="/login.do" method="post">
	<input type="text" name="username">
	<input type="password" name="password">
	<button>Login</button>
</form>

index.ejs

<h1>index主页</h1>
<div><%=username%></div>

<div>
    <a href="/buy.do">购买页</a>
</div>

<!-- 注销登录 => 清除用户登录信息 -->
<div>
    <a href="/logOut.do">注销登录</a>
</div>

vip.html

<body>
	<h1>vip页面非登录不得访问</h1>
</body>

indexEjsCookieSessionTokenRoute.js

//登录
router.post("/login.do",userCookieController.login)
//主页
router.get("/indexEjs",productEjsController.getIndex)
//注销登录
router.get("/logOut.do",userCookieController.logOut)

//vip页面,非登录不得访问和返回这个页面
router.get("/vip.html",productEjsController.myVip)
//购买页
router.get("/buy.do",productEjsController.getBuy)


/*通用拦截*/
// router.get("/",function (req,resp) {
// 	console.log("所有的请求都会被拦截")
// 	//判断它有没有req.session或者token,再进行下一步next()
// })

userCookieController.js

const userCookieModel=require("../dao/userCookieDao")

const userCookieController={

	//登录
	login(req,resp){
		let username=req.body.username;
		let password=req.body.password;
		// console.log(username,password)

		userCookieModel.checkUser([username,password])
			.then((data)=>{
				console.log(data)

				if(data.length>0){ //登录成功

					//存session(把它存到req对象里有个属性session中)
					req.session.username=data[0].username;

					//返回主页index.ejs页面
					resp.render("index",{username:data[0].username})
				}else {
					resp.send("登录失败")
				}
			})
	},


	//注销登录
	logOut(req,resp){
		//虽然将值设置为空,但是username属性依然存在
		//req.session.username=null;
		//session提供了一个方法来彻底删除,释放内存空间
		req.session.destroy();

		resp.redirect("login.html")
	}

	
}

module.exports = userCookieController;

productEjsController.js

const productEjsModel=require("../dao/productEjsDao")
//引入path解决redirect次数过多的问题
//const path=require("path")

const productEjsController={

	//主页
	getIndex(req,resp){
		console.log(req.session.username);

		//判断是否登陆过,看req.session.username是否有内容
		if(req.session.username){
				resp.render("index",{username:req.session.username})
		}else{
				resp.redirect("login.html")
		}
	},


	//vip页面,非登录不能访问
	// myVip(req,resp){
	// 	if(req.session.username==undefined){ //该用户没有登陆
	// 		resp.redirect("login.html")
	// 	}else{ //登录了
	// 		resp.sendFile(path.join(__dirname),"../public/vip.html")  //本身拦截的就是vip.html,再redirect("vip.html")导致重定向次数过多
	// 	}
	// },
	//重定向次数过多解决方式2
	myVip(req,resp,next){
		if(req.session.username==undefined){ //该用户没有登陆
			resp.redirect("login.html")
		}else{ //登录了
			next() //表示路由下一件要做的事情(继续执行下一个中间件或路由处理函数)
		}
	},


	//购买页
	getBuy(req,resp){
		productEjsModel.getBuyDao([])
			.then((data)=>{
				console.log(data)

				//登录时在req.session中是存了值,在其它页面也能拿到
				resp.render("buy",{product:data,username:req.session.username})
			})
	}
}

module.exports = productEjsController;

token

JWT: Json Web Token (基于json的公开规范),允许我们使用JWT在用户和服务器之间传递安全可靠的信息使用场景: 认证和数据交换 ( 通过token来代表用户身份 )

下载:

npm install jsonwebtoken  ( 不需要在app.js中去配置它,使用的时候直接用 )

流程:

(1) 用户登录成功后,产生一个唯一的token返回客户端
		//引入token模块
		const  myJwt=require("jsonwebtoken")
		//产生身份令牌
		let token=myJwt.sign({username:data[0].username},"lmynldv46d",{expiresIn:60*60*10});
		//返回身份令牌
		resp.render("index",{username:data[0].username,token});


(2) 在客户端(localstorage)保存服务器返回的唯一身份令牌token
		localStorage.setItem("mytoken",mytoken)


(3) 下次请求时就去获取存在本地的身份令牌,发起请求
		$.ajax({
			type:"get",
			url:"/testToken.do",
			headers:{
				"token":localStorage.getItem("mytoken") -----获取身份令牌,设置到headers中
			},
			success:function (data) {
				console.log(data);
			}
		})


(4) 服务器接收到请求,验证身份令牌token是否通过
		//取出headers中携带的身份令牌
		let token=req.headers.token;
		//验证身份令牌
		myJwt.verify(token,"lmynldv46d",function (err,data) {
			if(!err){
				//查数据、返回、渲染都可以
				resp.send("检测成功")
			}
		})

login.html

<h1>登录,如果登录成功的话就进入主页(index.ejs)</h1>

<form action="/login.do" method="post">
	<input type="text" name="username">
	<input type="password" name="password">
	<button>Login</button>
</form>

index.ejs

<h1>index主页</h1>
<button onclick="testToken()">测试token</button>

<script src="js/jquery.3.6.js"></script><!-- 路径书写:静态文件都回到public下面去找 -->
<script>

    //把从服务器拿到的令牌token存到本地
	let mytoken="<%=token%>"
	console.log(mytoken)

    window.onload=function () {
        localStorage.setItem("mytoken",mytoken)
	}

	//取出token(发请求时带上令牌)
    function testToken() {
        //ajax请求(为了方便不拼请求的参数,这里就用jqueryAjax,设置请求头即可)
		console.log(localStorage.getItem("mytoken"))
        $.ajax({
            type:"get",
            url:"/testToken.do",
            headers:{
                "token":localStorage.getItem("mytoken")
            },
            //放在headers还是data与后端协商,看对方写的是req.headers.token还是req.query.token接收
			// data:{
			// 	"token":localStorage.getItem("mytoken")
			// },
            success:function (data) {
				console.log(data)
			}
        })
	}

</script>

indexEjsCookieSessionTokenRoute.js

//登录
router.post("/login.do",userCookieController.login)
//测试token
router.get("/testToken.do",userCookieController.testToken)

userCookieController.js

//token模块
const  myJwt=require("jsonwebtoken")
//引入Dao层的文件
const userCookieModel=require("../dao/userCookieDao")

const userCookieController={

	//登录
	login(req,resp){
		let username=req.body.username;
		let password=req.body.password;
		// console.log(username,password)

		userCookieModel.checkUser([username,password])
			.then((data)=>{
				console.log(data)

				if(data.length>0){ //登录成功
		
					//存token(生成唯一的身份令牌)
					/*
						sign(数据,秘钥,配置)
						数据:谁在登录
						秘钥:加密的密文,越乱越好(实际中后端给的秘钥是专门有个模块生成的)
						配置:expiresIn设置令牌的有效时间(默认单位:秒),60*60、"2h"、"7days"
					*/
					let token=myJwt.sign({username:data[0].username},"lmynldv46d",{expiresIn: 60*60*10})
					console.log("token-----------"+token)

					//返回主页index.ejs页面
					resp.render("index",{username:data[0].username,token})

				}else {
					resp.send("登录失败")
				}
			})
	},



	//token
	testToken(req,resp){
		// console.log(req.headers.token)

		//验证token
		let token=req.headers.token;  //取出携带的身份令牌
		myJwt.verify(token,"lmynldv46d",function (err,data) {  //验证身份令牌是否正确verify()
			console.log(err) //null
			console.log(data)

			if(!err){
				//要查数据就查数据,要返回就返回,要渲染页面就渲染页面
				resp.send("检测成功")
			}
		})
	}

}

module.exports = userCookieController;

multer ( node.js中间件 )

Multer‌ 是一个 Node.js 中间件,专门用于处理 multipart/form-data 类型的表单数据,主要用于上传文件

上传模块:

npm install multer --save     ( 不需要去app.js配置 )

流程:

1)页面的表单index.html进行文件上传

			表单属性:enctype="multipart/form-data"
			单文件:name="myfile"
			多文件:name="myfiles" multiple="multiple"2)路由拦截请求

			(2.1)路由拦截请求后,首先到文件模块uploadconfig.js去进行上传地址、文件名的配置
				   router.post("/uploadFile.do",upload.single("myfile"),uploadController.uploadFileSingle)3)到控制层去处理,连接数据库将上传的文件存入数据库

文件上传 ( 单 / 多 )

index.html

<h1>上传文件</h1>
<!--
	enctype:必须写,规定了在发送到服务器之前应该如何对表单里的数据进行编码

	enctype="multipart/form-data"表示对字符不进行任何转码操作
-->
<form action="/uploadFile.do" method="post" enctype="multipart/form-data">
	<input type="file" name="myfile">
	<button>点击上传</button>
</form>



<h1>多文件上传</h1>
<form action="/uploadFiles.do" method="post" enctype="multipart/form-data">
	<input type="file" name="myfiles" multiple="multiple">   <!--多文件需要加上的属性multiple="multiple"-->
	<button>点击上传</button>
</form>

indexUploadRoute.js

//1.引入express来实现路由功能
const express=require("express");
//2.调用express对象提供的路由方法,返回路由对象
const router=express.Router();


//引入文件上传模块(config配置文件)
const upload=require("../config/uploadconfig")

//引入控制层(分发到控制层,去处理具体的业务逻辑)
const uploadController=require("../controller/uploadController")

//-----------------------------路由拦截--------------------------
//单文件上传
router.post("/uploadFile.do",upload.single("myfile"),uploadController.uploadFileSingle)

//多文件上传
router.post("/uploadFiles.do",upload.array("myfiles"),uploadController.uploadFilesArray)

//下载文件
router.get("/fileDownload.do",uploadController.fileDownload)


//3.公开路由
module.exports=router;

uploadconfig.js

const multer=require("multer")

//multer模块的配置
const storage=multer.diskStorage({

	//设置上传后文件在服务器保存的路径(必须)
	destination:function (req,file,cb) {
		console.log("==============uploadconfig.js文件=================")
		console.log(file);

		cb(null,"./public/uploads");  //在public中创建uploads文件夹,用于保存上传的文件
	},


	//配置文件类型
	filename:function (req,file,cb) {
		let fileFormat=(file.originalname).split(".");  //[文件名,文件后缀],但是文件名有可能是jQuery.3.3.6.js,所以要取数组最后一位

		let originalFileName=fileFormat.slice(0,-1).join(".");  //取出来的文件名为jQuery.3.3.6
		console.log("文件原名:"+originalFileName)

		//对上传的文件重命名(防重名)
		cb(null,originalFileName+"-"+Date.now()+"."+fileFormat[fileFormat.length-1]);  
		//Date.now()时间戳:表示方法执行时从格林威治时间到此刻的总毫秒数
	}

})


const upload=multer({
	storage:storage
})


module.exports=upload;

在这里插入图片描述
在这里插入图片描述

uploadController.js

//引入Dao层的文件
const uploadModel=require("../dao/uploadDao")


const uploadController={

	//单文件上传
	uploadFileSingle(req,resp){
		console.log("===============uploadController.js文件(单文件)=================")
		// console.log(req.file);  //{}

		//是否有文件上传
		if(req.file!=undefined){
			//文件地址Src,文件名与服务器对等
			let pathName="uploads/"+req.file.filename;
			//文件名
			let imgName=req.file.originalname;

			//存到数据库中
			uploadModel.uploadFileSingleDao([null,imgName,pathName])
				.then((data)=>{
					resp.send(data)
				})


		}else{
			//没有文件
			resp.send("未选择上传的文件")
		}
	},



	//多文件上传(包含了单个文件上传功能)
	uploadFilesArray(req,resp){
		console.log("===============uploadController.js文件(多文件)=================")
		console.log(req.files);  //[{},{}]

		if(req.files.length!=0){
			//存放多个文件的地址和文件名的数组
			let allfiles=[];
			for(let i=0;i<req.files.length;i++){
				//文件对象
				let fileObj={
					id:null,
					//文件地址
					pathName:"uploads/"+req.files[i].filename,
					//文件名
					imgName:req.files[i].originalname
				};
				//把每个文件对象放入数组
				allfiles.push(fileObj)
			}
			console.log(allfiles)


			//存到数据库中(多个文件)
			uploadModel.uploadFilesArrayDao(allfiles)
				.then((data)=>{
					resp.send(data)
				})
		}else{
			resp.send("未选择上传的文件")
		}
	}
	

}

module.exports = uploadController;

uploadDao.js

const dbpool=require("../config/dbpoolconfig")

const uploadModel={

	//单文件上传
	uploadFileSingleDao(params){
		return new Promise(function (resolve, reject) {
			dbpool.connect("insert into img values(?,?,?)",
				params,
				(err,data)=>{
					if(!err){
						resolve("上传成功")
					}else{
						reject(err)
					}
				})
		})
	},


	//多文件上传
	uploadFilesArrayDao(params){
		return new Promise(function (resolve, reject) {
			//根据文件个数,多次写入
			for(let i=0;i<params.length;i++){
				dbpool.connect("insert into img values(?,?,?)",
					[params[i].id,params[i].imgName,params[i].pathName],
					(err,data)=>{
						if(!err){
							resolve("多文件上传成功")
						}else{
							reject(err)
						}
					})
			}
		})
	}


}


module.exports=uploadModel;

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

文件下载

index.html

<h1>文件下载</h1>
<!--简版:href下载路径,download下载后叫什么名字-->
<!--<a href="uploads/xiongmao.777.lmy.hhh-1710220937725.jpg" download="xiongmao.jpg">点击下载文件</a>-->


<a href="/fileDownload.do">
	<button>点击下载文件</button>
</a>

uploadController.js

//文件下载
fileDownload(req,resp){
	// download(下载后放的文件位置,下载后叫什么名字)
	// resp.download("./public/uploads/vue.docx","vue.docx")
	
	resp.download("./public/uploads/xiongmao-1716706083607.jpg","xiongmao-1716706083607.jpg")
}

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

多文件上传 ( jQueryAjax、原生Ajax )

index.html

<body>
	<h1>多文件(jQueryAjax、原生Ajax)上传</h1>
	<form>
		<input id="inputFiles" type="file" name="myfiles" multiple="multiple">
		<button type="button" onclick="jqueryAjaxFiles()">点击上传</button>
	</form>

</body>
<script src="js/jquery.3.6.js"></script>
<script src="js/ajax.js"></script>
<script>
	/*jQueryAjax怎样上传文件、原生ajax怎样上传文件(post请求头如何手动设置:enctype="multipart/form-data")*/



	//jQuery-Ajax上传
	// function jqueryAjaxFiles(){
	// 	let formData=new FormData();
	//
	// 	//只能一个接着一个的append添加
	// 	for(let i=0;i<$("#inputFiles")[0].files.length;i++){
	// 		formData.append('myfiles',  $("#inputFiles")[0].files[i])  /*一定注意'myfiles'是input框的name属性的值*/
	// 		console.log(formData.getAll("myfiles")[i])
	// 	}
	//
	// 	$.ajax({
	// 		type: "post",
	// 		url: "/uploadFiles.do",
	// 		data: formData,
	// 		processData: false,
	// 		//contentType设置为false。因为是由<form>表单构造的FormData对象,且已经声明了属性enctype="multipart/form-data"
	// 		contentType: false,
	// 		headers:{
	// 			enctype:"multipart/form-data",
	// 		},
	// 		success: function (data) {
	// 			console.log("上传成功");
	// 		},
	// 		error: function (e) {
	// 			console.log(e);
	// 		}
	// 	})
	// }
	



	//原生-ajax上传
	function jqueryAjaxFiles(){
		//参数
		let formData=new FormData();
		//只能一个接着一个的append添加
		for(let i=0;i<$("#inputFiles")[0].files.length;i++){
			formData.append('myfiles',  $("#inputFiles")[0].files[i])  /*一定注意'myfiles'是input框的name属性值*/
			console.log(formData.getAll("myfiles"))
		}
		console.log(formData)

		//不能使用封装好的ajax,因为请求头不同,需要单独写一个原生ajax请求
		let xhr=new XMLHttpRequest();
		xhr.onreadystatechange=function () {
			if (xhr.readyState == 4 && xhr.status == 200) {
				console.log(xhr.responseText)
			}
		}
		xhr.open("post","/uploadFiles.do")
		xhr.setRequestHeader("enctype","multipart/form-data")
		xhr.send(formData)
	}

</script>

websocket ( 实时通信 )

http是一种通信协议,服务器不会主动给客户端发送消息
在这里插入图片描述
websocket也是一种通信协议 ( WebSocket与HTTP虽然都位于OSI模型的应用层,并且都依赖底层的TCP协议,但它们有不同的协议格式和应用场景。WebSocket在建立连接时依赖于HTTP协议进行握手过程,但在连接建立后,WebSocket协议本身不依赖于HTTP )

在这里插入图片描述
在这里插入图片描述
在node中想要实现实时通信使用ws模块:

1)下载:npm install ws --save-dev  缩写  npm i ws -D2)在app.js中去引入和配置websocket服务器:

								const ws=require("ws")
			
								let wss=new ws.Server({
									host:"192.168.2.107",  //本机ip
									port:8080,  //端口
								})
			
								......

index.html

<!DOCTYPE html>
<html lang="en">
<head>
	<meta charset="UTF-8">
	<title>websocket通信协议</title>
	<style>
		#box{
			margin: 0 auto;
			text-align: center;
			width: 500px;
		}
		#con{
			width: 500px;
			height: 400px;
			border: 1px solid yellowgreen;
		}
	</style>
</head>
<body>
<div id="box">
	<h1>欢迎来到lmy的聊天室</h1>

	<div id="con"> <!--聊天内容展示框-->
		<!--<p>
			<i>月月:</i>
			<b>哈哈哈哈</b>
		</p>-->
	</div>

	<p>
		<input type="text" name="msg" id="msg"> <!--发言框-->
		<input type="button" value="send" id="btn"> <!--发送按钮-->
	</p>

	<input type="button" value="exit" id="exit"> <!--退出按钮-->
</div>

</body>
<script src="js/jquery.3.6.js"></script>
<script>

	//1.建立与websocket服务器的连接
	let socket=new WebSocket("ws://192.168.43.250:8080");
	socket.onopen=function (ev) {

		//3.接收到服务器发过来的消息
		socket.onmessage=function (ev) {
			console.log(ev.data)
			//websocket的数据格式默认是Blob对象,可以使用blob对象自带的text()方法读取数据,读取后返回promise对象

			ev.data.text()  //返回promise对象
				.then((data)=>{
					console.log(data)
					console.log(typeof data)  //string
					let mydata=JSON.parse(data)  // 转成对象
					//展示数据
					$("#con").append(`<p>
										<i>${mydata.name}:</i>
										<b>${mydata.msg}</b>
									</p>`)
			})
		}
	}


	//2.发送消息
	$("#btn").click(function () {
		//获取输入框的内容向服务器发送(socket主要能够识别的数据格式:1.字符串  2.字符流-二进制流)
		let msg={
			"name":"月月",  //取决于当前登录的人是谁
			"msg":$("#msg").val()
		}

		socket.send(JSON.stringify(msg));  //转成字符串
	})


	//4.退出聊天室
	$("#exit").click(function () {
		socket.close()
	})

</script>
</html>

app.js

const express=require("express")
const  logger=require("morgan")
const favicon=require("serve-favicon")
const bodyParser=require("body-parser")
//websocket
const ws=require("ws")


const route=require("./routes/indexRoute") 
//执行express全局函数,返回express的服务器对象
const app=express()
//---------------------------------------配置----------------------------
app.use(logger("dev"))

app.use(favicon(__dirname+"/public/images/favicon.ico"))

//配置post请求必须写在路由上方(false:表示使用系统模块的querystring来处理,官方推荐;true:表示使用第三方模块qs来处理;从功能上qs比querystring要更强大)
app.use(bodyParser.urlencoded({extended: false}))
//使用post的什么数据格式
app.use(bodyParser.json())

//使用自己写的路由模块(放在静态资源路径配置之前)
app.use(route)

//静态资源路径的配置(static静态,如果请求了静态资源到去哪里找)
app.use(express.static(__dirname+"/public"))   //告诉它静态资源在哪里就可以了,因为html中的路径是理清楚了的
app.use(express.static(__dirname+"/public/pages"))   //不是所有html文件都在public下的

//监听端口搭建服务器
app.listen(8888,()=>{
	console.log("服务器启动")
})






//搭建ws服务器(这两个服务器各司其职)
let wss=new ws.Server({
	host:"192.168.43.250",  //服务器ip(此例子中是本机ip)
	port:8080,  //端口
})

//数组,用于存放连接到服务器的用户(声明应放到上方,放此处为了查看方便)
let arr=[];

wss.on("connection",function (ws) {  //ws代表websocket对象,指向当前连接的用户
	console.log("有人来了")
	// console.log(ws)
	arr.push(ws)

	ws.on("message",function (data) {  //data就是用户发送到服务器的消息
		console.log(data)
		for(let i=0;i<arr.length;i++){
			arr[i].send(data); //把当前消息发送出去
		}
	})

	ws.on("close",function () {
		for(let i=0;i<arr.length;i++){
			if(arr[i]==ws){
				//移除当前用户
				arr.splice(i,1);
				break;
			}
		}
	})
	
})

在这里插入图片描述

localStorage / sessionStorage ( 本地存储 )

localStorage:持久化的本地存储,没有时间限制,除非主动删除数据,储存小于5MB的数据

sessionStorage:储存在本地,语法和localstorage类似,但是不是持久化的存储,不需要删除 (窗口关了就没了)。有关联的页面才能相互使用存储的sessionStorage

以上2种本地存储都只能够存储字符串

sessionStorage

storage.html

<form action="form.html">
	用户名:<input type="text" id="username">
	<button type="button" onclick="getName()">sessionStorage提交</button>
</form>

<a href="a.html">aTest</a>

<script>
	//sessionStorage存储
	function getName(){
		let username=document.getElementById("username").value;
		// sessionStorage.myUser=username; 不规范的写法
		sessionStorage.setItem("myUser",username);
		//提交表单
		document.forms[0].submit()
	}
</script>

在这里插入图片描述
form.html

<h1>提交表单成功的页面(有关联的页面才能拿到sessionStorage数据)</h1>
<p id="username"></p>

<script>
	console.log(sessionStorage.getItem("myUser"))

	document.getElementById("username").innerHTML=sessionStorage.getItem("myUser")
</script>

在这里插入图片描述
a.html

<h1>a标签sessionStorage测试页面</h1>

在这里插入图片描述

localStorage

storage.html

<form action="form.html">
	用户名:<input type="text" id="username">
	<button type="button" onclick="localTest()">localStorage提交</button>
</form>

<button onclick="getLocalStorage()">提取localStorage数据</button>
<button onclick="delLocalStorage()">删除localStorage数据</button>

<script>

	//localStorage存储
	function localTest() {
		let username=document.getElementById("username").value;
		localStorage.setItem("myUser2",username)
		localStorage.setItem("myUser3","刘亦菲")
		localStorage.setItem("myUser4","郭麒麟")
		
		let obj={
			"myname":"月亮",
			"sex":"女"
		}
		localStorage.setItem("myObj",JSON.stringify(obj))  //转成string
	}


	//提取localStorage数据
	function getLocalStorage() {
		console.log(localStorage.getItem("myObj"))
		let obj=JSON.parse(localStorage.getItem("myObj"))  //转成obj

		console.log(obj.myname)  //可以把每次提交的都放到数组中:[{}{}{}]
	}


	//删除localStorage数据
	function delLocalStorage(){
		localStorage.removeItem("myObj");  //删除指定项

		// localStorage.clear();  //删除所有
	}


	console.log("localStorage数据条数:"+localStorage.length)
	console.log("localStorage数据的键名:"+localStorage.key(0))

</script>

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述


http://www.kler.cn/a/458780.html

相关文章:

  • pip安装paddle失败
  • 基于AT89C51单片机的可暂停八路抢答器设计
  • Mac电脑python多版本环境安装与切换
  • latex 尖括号怎么写 编译出来是问号
  • C++设计模式:状态模式(自动售货机)
  • 黑森矩阵(Hessian matrix)
  • redis用途都有哪些
  • 《SwiftUI 实现点击按钮播放 MP3 音频》
  • 低空经济与数据资产入表的联系
  • llamafactory报错:双卡4090GPU,训练qwen2.5:7B、14B时报错GPU显存不足(out of memory),轻松搞定~~~
  • Android 学习小记1
  • plantuml 信号时序图
  • 重装操作系统后 Oracle 11g 数据库数据还原
  • 深入理解Android中的ImageReader与JNI交互
  • Android笔试面试题AI答之Android基础(6)
  • 【深度学习基础之多尺度特征提取】多尺度图像增强(Multi-Scale Image Augmentation)是如何在深度学习网络中提取多尺度特征的?附代码
  • DAY176内网对抗-信息收集篇SPN扫描DC定位角色区域定性服务探针安全防护凭据获取
  • 《C++设计模式》策略模式
  • 应用层1——C/S、P2P、DNS域名系统
  • 【Rust自学】5.2. struct使用例(加打印调试信息)
  • 使用 Spring Boot 和 GraalVM 的原生镜像
  • Ubuntu 22.04 升级 24.04 问题记录
  • 使用 OpenCV 在图像中添加文字
  • oscp备战系列-Kioptrix2014
  • Oracle Managed Files(OMF)
  • sqlite3 python 如何使用