前后端交互
+ 客户端 和 服务端 进行通信
+ 目的: 如何向服务器发送一段信息, 得到服务器反馈回来的结果
=> 例如: 登录, 发送 用户名 和 密码, 接受返回 登录成功 或者 登录失败
=> 例子: 列表, 发送 第几页 和 一页多少条, 接受返回 商品数组
前端如何和后端交互(通讯)
+ 技术栈: ajax
=> async 异步
=> javascript
=> and 和
=> xml :严格的 html 格式数据
1. 创建 ajax 对象(专门用于发送 ajax 请求的 "工具")
=> 语法: const xhr = new XMLHttpRequest()
2. 配置本次的请求信息
=> 语法: xhr.open(请求方式, 请求地址, 是否异步)
-> 请求方式: 按照接口文档书写
-> 请求地址: 按照接口文档书写
-> 是否异步: 默认是 true 表示 是异步, 选填 false, 表示 非异步(同步)
3. 把请求发送出去
=> 语法: xhr.send()
4. 接受响应
=> 依赖事件触发的, 因为 ajax 是异步的
=> 语法: xhr.onload = function () {}
=> 时机: 当前这个 ajax 请求结束以后, 触发
=> 在 xhr 对象内, 有一个叫做 reponseText 的属性, 记录的就是后端返回的信息
// 1. 创建 ajax 对象
// const xhr = new XMLHttpRequest()
// // 2. 配置本次请求的信息
// xhr.open('GET', 'http://localhost:8888/test/first')
// // 3. 把请求发送出去
// xhr.send()
// // 4. 接受响应
// xhr.onload = function () {
// // 4-2. 打印后端返回的结果
// console.log(xhr.responseText)
// }
// 1. 创建 ajax 对象
const xhr = new XMLHttpRequest()
// 2. 配置本次请求的信息
xhr.open('GET', 'http://localhost:8888/test/second')
// 3. 把请求发送出去
xhr.send()
// 4. 接受响应
xhr.onload = function () {
// 4-2. 打印后端返回的结果
console.log(JSON.parse(xhr.responseText))
}
+ 约定了前后端交互的一种方式
+ 这个协议只能 前端主动发起请求, 接受后端响应
1. 基于 TCP/IP 协议的三次握手
+ 目的是为了建立 客户端 和 服务端 的连接, 确保道路畅通
2. 前端主动发送请求
+ 把你想和后端说的事情说出来(一次只能说一个事情)
+ 前端以请求报文的形式发送所有的内容
2-1. 请求报文行
=> 传输协议版本 http/1.1
=> 请求方式 get
=> 请求地址 /xxxx/yyyy
2-2. 请求报文头(对本次请求的描述信息)
=> userAgent 客户端信息
=> platform 客户端操作系统
=> host 客户端主机域名
=> cookie 当你的 cookie 空间内有信息, 会自动携带
=> content-type 前端请求体的数据格式
=> ...
2-3. 请求报文空行
=> 一个空白行
=> 区分请求头和请求体的空白行
2-4. 请求报文体
=> 前端传递给后端的真实信息
=> post 会有
=> get 一般没有, 都连接在地址一起了
3. 后端返回响应给前端
+ 后端准备好给前端的信息
+ 后端以响应报文的形式发送所有的内容
3-1. 响应状态行
=> 传输协议版本
=> 响应状态码 200
=> 简单描述 OK
3-2. 响应报文头(对本次响应的描述信息)
=> Server 服务器版本
=> Date 服务器时间
=> content-type 服务端响应体的数据格式
=> content-length 响应体长度
=> ...
3-3. 响应报文体
=> 后端返回给前端的真实信息
4. 基于 TCP/IP 协议的四次挥手
=> 为了确保正确且安全的断开
三次握手
1. 前端发送一个 "包" 给到后端
2. 后端接收到前端的 "包" 以后, 返回一个 "包+包" 给到前端
后端知道 前端可以正常发送
后端知道 后端可以正常接受
3. 前端接收到后端的 "包+包" 以后, 返回一个 "包+包+包" 给到后端
前端知道 前端可以正常发送
前端知道 前端可以正常接受
前端知道 后端可以正常接受
前端知道 后端可以正常发送
后端知道 前端可以正常接受
后端知道 后端可以正常发送
四次挥手
1. 前端发送一个 "包" 给到后端
=> 告诉他: "我接受到你的响应了, 可以准备断开了"
2. 后端接受到前端的 "包", 返回一个 "包+包1" 给回到前端
=> 告诉前端: "我知道你接收到我的信息了, 我准备断开了"
3. 后端再次返回一个 "包+包2" 给到前端
=> 告诉前端: "我已经准备好断开了, 当我再次受到消息会直接断开, 不在回复"
4. 前端接收到所有 "包" 以后, 返回一个 "最终包" 给到后端
=> 高度后端: "我已经断开连接了, 别回了
+ 以一个数字的形式表示本次请求的状态(成功/失败)
分成五类
1. 100 ~ 199 表示连接继续
2. 200 ~ 299 表示各种成功
3. 300 ~ 399 表示重定向
304 缓存
301 临时重定向
302 永久重定向
4. 400 ~ 499 表示各种客户端错误
404 请求地址不存在
403 请求地址权限不够
5. 500 ~ 599 表示各种服务端错误
+ 前后端传递信息的方式
+ 本质区别就是 语义化 的不一样
1. GET 偏向于获取的语义
2. POST 偏向于提交的语义
3. PUT 偏向于提交的语义(偏向于提交插入)
4. DELETE 偏向于获取的语义(偏向于删除)
5. PATCH 偏向于提交的语义(偏向于提交修改)
6. HEAD 偏向于获取的语义(就是为了获取响应头信息)
7. OPTIONS 偏向于获取的语义(获取服务器信息, 需要服务器允许)
8. CFONNECT 保留请求, 管道连接改为代理连接使用
9. ...
GET 和 POST 的区别(熟读并背诵全文, 领会精神)
1. 携带信息的位置
=> GET: 直接拼接在地址栏后面(以查询字符串的方式)
=> POST: 书写在请求体内
2. 携带信息的格式
=> GET: 只能是查询字符串格式
=> POST: 不限制任何格式, 但是你要在请求头内的 content-type 信息说明
-> 查询字符串: application/x-www-form-urlencoded
-> json字符串: application/json
-> 二进制流: mutlipart/form-data
3. 携带信息的大小
=> GET: 2KB
=> POST: 理论上不限制大小, 会被服务器限制
4. 安全
=> GET 相对不安全
=> POST 相对安全
+ 前后端交互的手段
+ 自己的固定步骤
1. 创建 ajax 对象
=> 语法: const xhr = new XMLHttpRequest()
2. 配置本次的请求信息
=> 语法: xhr.open(请求方式, 请求地址, 是否异步)
3. 把本次请求发送出去
=> 语法: xhr.send()
4. 接受本次的响应
=> 依赖事件接受
=> 事件: xhr.onload = function () {
console.log(xhr.responseText)
}
尝试 ajax
// const xhr = new XMLHttpRequest()
// xhr.open('GET', 'http://localhost:8888/test/first')
// xhr.send()
// xhr.onload = function () {
// console.log((xhr.responseText))
// }
在一个 ajax 内, 哪些代码是同步, 哪些代码是异步
1. const xhr = new XMLHttpRequest()
=> 同步代码
2. xhr.open(请求方式, 请求地址, 是否异步)
=> 同步代码
3. xhr.send()
=> 发送: 一定是同步
=> 接受: 根据 open 的第三个参数配置的
4. xhr.onload = function () {}
=> 事件绑定是 同步绑定
结论:
+ 当你发送异步请求的时候, 可以按照 1 2 3 4 或者 1 2 4 3 的顺序书写代码
+ 当你发送同步请求的时候, 必须按照 1 2 4 3 的顺序书写代码
+ 最终以后都是按照 1 2 4 3 的顺序书写代码
// 1. 一个异步请求
// const xhr = new XMLHttpRequest()
// xhr.open('GET', 'http://localhost:8888/test/first')
// xhr.send()
// xhr.onload = function () {
// console.log(xhr.responseText)
// }
代码执行
1. 创建 ajax 对象
2. 配置信息(异步)
3. 同步发送请求出去
4. 同步绑定事件(当请求完成时触发)
5. 所有同步代码执行完毕, 响应回来了
6. 请求完成了, 因为此时已经绑定了事件, 会触发 onload
// 2. 一个同步请求
const xhr = new XMLHttpRequest()
xhr.open('GET', 'http://localhost:8888/test/first', false)
xhr.send()
xhr.onload = function () {
console.log(xhr.responseText)
}
代码执行
1. 创建 ajax 对象
2. 配置信息(同步)
3. 同步发送请求出去
4. 同步等待响应回来
5. 响应回到浏览器了, 本次请求完成了, 但是此时没有事件, 不会执行任何事件处理函数
6. 同步绑定事件, 此时绑定了请求完成时触发的事件
ajax 状态码
+ 是用一个数字来表示一个 ajax 生命周期的各个过程
如何获取 ajax 状态码
+ xhe.readyState 获取到的就是 ajax 的状态码
0。 表示创建 ajax 完成
1。表示配置本次请求信息完成
2。 发送请求, 并且响应已经回到浏览器的瞬间(响应报文来到了浏览器, 但是浏览器还没有开始解析响应报文)
3 。浏览器正在解析响应报文, 并且逐步把响应报文内的内容填充在 xhr.responseText 内 4 浏览器解析响应报文结束, 并且已经把响应体全部填充到 xhr.responseText 内, 结束本次请求
一个事件(也能用来处理兼容问题)
+ onload 这个事件只在标准浏览器内才有
+ 在 IE 低版本是没有 onload 事件
+ IE 低版本使用的是 readystatechange 事件
const xhr = new XMLHttpRequest()
xhr.open('GET', 'http://localhost:8888/test/first')
xhr.onreadystatechange = function () {
// console.log(xhr.readyState)
// if (xhr.readyState === 2) {
// console.log(xhr.responseText)
// }
// if (xhr.readyState === 3) {
// console.log(xhr.responseText)
// }
if (xhr.readyState === 4) {
console.log(xhr.responseText)
}
}
xhr.send()
get 请求的参数是直接在地址后面以查询字符串的形式拼接
const xhr = new XMLHttpRequest()
xhr.open('GET', 'http://localhost:8888/test/third?name=前端小灰狼&age=13')
xhr.onload = function () {
console.log(JSON.parse(xhr.responseText))
}
xhr.send()
+ 因为 post 请求是在请求体内传递参数
+ xhr.send() 后面的 () 内就是书写请求体的位置
+ 使用 post 方式携带参数的时候, 可以携带多种格式
=> 你要在请求头内的 content-type 字段说明你是什么格式的数据
+ 注意: 如果你发送 post 请求, 需要在 send 之前设置请求头信息
const xhr = new XMLHttpRequest()
xhr.open('POST', 'http://localhost:8888/test/fourth')
xhr.onload = function () {
console.log(JSON.parse(xhr.responseText))
}
// 设置请求头内 content-type 信息
// 语法: xhr.setRequestHeader(字段名称, 值)
xhr.setRequestHeader('content-type', 'application/x-www-form-urlencoded')
xhr.send('name=guoxiang&age=18')
Document
1. 哪些内容是参数
=> 请求方式 method
=> 请求地址 url
=> 是否异步 async
=> 携带给后端的数据 data
=> 需要设置的请求头 headers
=> 时候解析结果 dataType
=> 请求成功后执行的函数 success
=> 执行失败后的函数 error
2. 哪些参数有默认值
=> url 必填
=> method 选填默认使用 GET
=> async 选填默认使用 true
=> data 选填默认使用 ''
=> headers 选填默认使用 { 'content-type': 'application/x-www-form-urlencoded', aaa: 100 }
=> dataType 选填默认使用 不解析
-> 约定 'string' 表示不解析
-> 约定 'json' 表示解析
=> success 选填默认使用 空函数
=> error 选填默认使用 空函数
3. 如何设计函数(如何设计参数位置)
=> function ajax(url, async, data, method) {}
=> function ajax(options) {}
-> 以一个对象的方式传递参数
-> ajax({ url: 'asdasd', method: 'POST' })
// const xhr = new XMLHttpRequest()
// xhr.open('GET', 'sdddddd', true)
// xhr.onload = function () {
// console.log('请求完成')
// }
// xhr.send()
// 需要发送请求
// ajax({
// url: 'http://localhost:8888/test/third',
// data: { name: 'Jack', age: 18 },
// dataType: 'json',
// success: function (res) {
// console.log(res)
// }
// })
=> 当你需要封装异步代码的时候, 我们需要使用回调函数的形式来进行封装
回调函数
+ 一种为了进行异步封装而出现的技术手段
ajax({
success (res) {}
})
Promise
+ 一种为了进行异步封装而出现的技术手段
ajax().then(res => console.log(res))
*/
// 1. 创建 Promise 对象
// 为了帮你执行异步代码的
// function fn() {
// const p = new Promise(function (resolve, reject) {
// const n = Math.random()
// setTimeout(() => {
// if (n >= 0.5) {
// // 成功
// resolve(n)
// } else {
// // 失败
// reject(n)
// }
// }, 1000)
// })
// // 2. 注册成功时要做的事情
// p.then(res => console.log('成功', res))
// // 3. 注册失败时要做的事情
// p.catch(err => console.log('失败', err))
// }
// fn()
// function fn() {
// const p = new Promise(function (resolve, reject) {
// const n = Math.random()
// setTimeout(() => {
// if (n >= 0.5) {
// // 成功
// resolve(n)
// } else {
// // 失败
// reject(n)
// }
// }, 1000)
// })
// return p
// }
// 全局变量 sjl 和 fn 函数内的 局部变量p 存储的是一个 空间地址
// 函数内 p 能做的, sjl 也能做
// const sjl = fn()
// fn()
// .then(function (res) { console.log('成功', res) })
// .catch(err => console.log('失败 ', err))
pAjax({ /* ... */ })
.then(res => {
console.log(res)
return pAjax({ /* ... */ })
})
.then(res => {
console.log(res)
return pAjax({ /* ... */ })
})
.then(res => console.log(res))
fn()
async function fn() {
const r1 = await pAjax({ /* ... */ })
console.log(r1)
const r2 = await pAjax({ /* ... */ })
console.log(r2)
const r3 = await pAjax({ /* ... */ })
console.log(r3)
}
+ 当回调函数嵌套过多的时候, 会出现回调地狱
异步代码的封装方案
+ 方案1: 回调函数形式
+ 方案2: Promise(ES6 提出的解决方案)
=> 目的: 就是为了解决回调地狱
Promise 的主要作用:
+ 对异步代码进行封装的方案, 为了解决回调地狱
需求:
1. 发送一个 ajax 请求到 second 接口, 把结果打印在控制台
2. 发送一个 ajax 请求到 third 接口, 要求在第一个请求完成后发送, 把结果打印在控制台
3. 发送一个 ajax 请求到 fourth 接口, 要求在第二个请求完成后发送, 把结果打印在控制台
// ajax({
// url: 'http://localhost:8888/test/second',
// dataType: 'json',
// // 对象内函数的简写方式
// // 当对象的某个 key 是 函数, 并且不是 箭头函数 的时候
// // 可以省略 function 和 : 不写
// success (res) {
// console.log(res)
// // 这个位置的代码会在第一个请求结束后触发
// // 把第二个请求书写在这里
// ajax({
// url: 'http://localhost:8888/test/third',
// dataType: 'json',
// data: { name: 'Jack', age: 18 },
// success (res) {
// console.log(res)
// ajax({
// url: 'http://localhost:8888/test/fourth',
// dataType: 'json',
// method: 'POST',
// data: { name: 'Jack', age: 18 },
// success (res) {
// console.log(res)
// }
// })
// }
// })
// }
// })
ajax({
url: 'http://localhost:8888/test/second',
dataType: 'json',
success (res) {
console.log(res)
ajax({
url: 'http://localhost:8888/test/third',
dataType: 'json',
data: { name: 'Jack', age: 18 },
success (res) {
console.log(res)
ajax({
url: 'http://localhost:8888/test/fourth',
dataType: 'json',
method: 'POST',
data: { name: 'Jack', age: 18 },
success (res) {
console.log(res)
}
})
}
})
}
})
一个承诺多少个状态 ?
1. 持续 pending
2. 成功 fulfilled
3. 失败 rejected
+ 注意: 一个 Promise 的状态转换只有两种
1. 持续 => 成功
2. 持续 => 失败
Promise 基本语法
1. 创建 Promise
语法: const p = new Promise(function (第一个形参, 第二个形参) { 异步代码 })
-> 第一个形参(resolve) 能把当前 Promise 的状态转换为 成功
-> 第二个形参(reject) 能把当前 Promise 的状态转换为 失败
2. 状态转换为成功是, 触发的函数
语法: p.then(function () {})
3. 状态转换为失败时, 触发的函数
语法: p.catch(function () {})
// 1. 创建 Promise
// const p = new Promise(function (resolve, reject) {
// const time = Math.round(Math.random() * 5000)
// setTimeout(function () {
// if (time <= 2500) {
// // 失败
// reject(time) // 把当前 Promise 的状态转换为失败, 就会触发 catch 注册的函数
// } else {
// // 成功
// resolve(time) // 把当前 Promise 的状态转换为成功, 就会触发 then 注册的函数
// }
// }, time)
// })
// // 2. 状态转换为成功时, 触发的函数
// p.then(function (res) {
// console.log('该 Promise 成功了', res)
// })
// // 3. 状态转换为失败时, 触发的函数
// p.catch(function (err) {
// console.log('该 Promise 失败了', err)
// })
// function jiehun() {
// const p = new Promise(function (resolve, reject) {
// const time = Math.round(Math.random() * 5000)
// setTimeout(function () {
// if (time <= 2500) {
// // 失败
// reject(time) // 把当前 Promise 的状态转换为失败, 就会触发 catch 注册的函数
// } else {
// // 成功
// resolve(time) // 把当前 Promise 的状态转换为成功, 就会触发 then 注册的函数
// }
// }, time)
// })
// 把 Promise 对象返回出去
// return p
// }
// jiehun()
// .then(res => {
// console.log('成功了, 要立一个牌坊')
// })
// .catch(err => {
// console.log('失败了, 炸死你 boom')
// })