【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.query
;post => 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 -D
(2)在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>