Ajax-day04
跨域介绍
同源策略
同源策略:是浏览器提供的一种安全机制,限制来自不同源的数据。 如果当前页面的URL地址和Ajax的请求地址不同源,浏览器不允许得到服务器的数据
- 协议、域名和端口都相同则同源,只要有一项不同就是不同源
- 跨域:不同源就是跨域
- 当前显示的页面中,如果触发了一个Ajax请求,请求的URL地址和当前页面的URL地址不同源就是跨域
- 浏览器不允许跨域获取数据,但是实际会经常有这个跨域获取数据需求
跨域解决方案
我们希望:浏览器可以通过Ajax跨域得到数据
- jsonp (需要前端和后端的配合才可以)
- 纯后端解决方案(如果后端实现了cors配置,那么前端就可以直接跨域获取对应的数据)
- cors(后端写代码实现)
- 服务端反向代理(后端安装软件实现Apache/Nginx)
- 跨域Ajax请求的错误提示(后端不支持跨域)
jsonp跨域原理分析
- jsonp的原理:利用script标签的src属性发送跨域请求,请求返回的结果是js代码【函数调用】,被调用的函数【回调函数】需要在发送请求之前进行定义
// jsonp的基本原理
// 虽然Ajax不支持跨域获取Ajax的数据,但是script标签发出的请求支持跨域获取数据
// 动态创建一个script标签
const st = document.createElement('script')
st.src = 'http://www.liulongbin.top:3006/api/jsonp?callback=hello'
document.body.appendChild(st)
// 回调函数(前端定义;后端接口返回的结果来调用)
function hello(ret) {
// 这里的ret就是跨域获取到的数据
console.log(ret)
}
// jsonp接口返回的结果是什么?函数调用
// 返回的结果 hello({"message":"JSONP请求测试成功","data":{}})
总结:
- script标签发送的请求本身支持跨域
- 发送请求必须携带参数callback=hello,他的作用是定制回调函数的名称
- 该动态创建的script标签必须添加到页面才能生效
jQuery对jsonp的支持
- $.ajax() 该方法可以发送jsonp请求,jsonp请求本质上不是Ajax
// 基于jQuery方式发送jsonp请求
// http://www.liulongbin.top:3006/api/jsonp?
// callback=jQuery3410722416410580281_1619945266328
// &_=1619945266329
// jQuery请求地址中自动添加了一个时间戳,作用是防止浏览器缓存
$.ajax({
type: 'get',
url: 'http://www.liulongbin.top:3006/api/jsonp',
// 设置响应的数据类型格式 text/json/jsonp
dataType: 'jsonp',
// 可以自定义回调函数的名称
jsonpCallback: 'hello',
// 定制等号左侧的名称
jsonp: 'callback',
success: function (ret) {
console.log(ret)
}
})
- jsonp相关的两个属性
- jsonp 属性的值为了迎合后端(后端根据该名称获取回调函数的名字),jsonp的值决定了等号左侧的名字
- jsonpCallback 决定等号右侧的内容(服务端返回的函数调用的名称)
- jQuery请求地址中自动添加了一个时间戳,作用是防止浏览器缓存
$.ajax({
// 请求地址
url: 'http://www.liulongbin.top:3006/api/jsonp',
// 请求参数
data: {
uname: 'lisi',
age: 12
},
// jsonp属性的值为了迎合后端(后端根据该名称获取回调函数的名字)
// jsonp的值决定了等号左侧的那个名字
// callback=jQuery34105174086151731971_1589093752115
// cb=jQuery34105174086151731971_1589093752115
jsonp: 'callback', // jsonp的默认值就是callback
// jsonpCallback决定等号右侧的内容(服务端返回的函数调用的名称)
// callback=jQuery34105174086151731971_1589093752115
// callback=nihao
jsonpCallback: 'jsonp564',
// 服务端返回的数据格式:json/text/jsonp
dataType: 'jsonp',
// 回调函数
success: function (data) {
console.log(data)
}
})
jsonp的缺点
- 仅支持get请求,不支持其他请求方式
- script标签发出请求本身就是get,不支持别的请求方式
- get请求传递的数据量有限制(URL地址的长度不超过8000个字符)
- post请求可以传递更多数据(post用请求体方式传递)
- 如果确实希望跨域post提交数据,怎么办?让后端去做CORS
淘宝搜索案例
- 案例图示如下所示
需求:类比淘宝搜索提交实现类似功能,接口基于淘宝的真实接口https://suggest.taobao.com/sug,请求参数:关键字名称是 q
获取用户输入关键词
- 绑定输入事件并获取输入的值
// 1、监听用户的输入事件(键盘抬起)
$('#keyword').keyup(function (e) {
// 2、获取输入的关键字
// 原生js中有一个字符串方法trim,作用是去掉两侧的空格
const kw = e.target.value.trim()
if (!kw) {
// 没有输入任何有效字符,终止后续代码
return
}
})
总结
- 去掉字符串两侧的空格:原生js的trim()方法
- 如果没有输入有效关键字,阻止发送请求
获取搜索列表数据
- 获取搜索列表结果(调用jsonp跨域接口)
$.ajax({
// 请求地址
url: 'https://suggest.taobao.com/sug‘
// 返回数据格式
dataType: 'jsonp',
// 请求参数
data: {
q: kw
},
// 回调函数
success: function (ret) {
console.log(ret.result)
}
})
总结:
- 调用jsonp接口获取搜索结果
- 需要额外传递get请求参数q
动态渲染列表
需求:展示搜索的列表数据
// 1、监听用户的输入事件(键盘抬起)
$('#keyword').keyup(function (e) {
// 2、获取输入的关键字
// 原生js中有一个字符串方法trim,作用是去掉两侧的空格
const kw = e.target.value.trim()
if (!kw) {
// 没有输入任何有效字符,终止后续代码
// 清空列表(empty方法用于清空DOM里面的内容)
$('#suggest-list').empty().hide()
return
}
// 把关键字发送接口
$.ajax({
url: 'https://suggest.taobao.com/sug',
data: {
q: kw
},
dataType: 'jsonp',
success: function (ret) {
// console.log(ret.result[0][0])
// 动态渲染数据
let listTags = ''
$.each(ret.result, function (index, item) {
listTags += '<div class="suggest-item">' + item[0] + '</div>'
})
$('#suggest-list').html(listTags).show()
}
})
})
总结:
- 基于jQuery动态渲染列表内容
- 退格时控制清空列表
- jQuery方法empty方法用于清空DOM里面的内容
总结
- 梳理
- CSS(Tab的静态布局)
- JS基础和WebAPI(面向过程功能)
- 面向对象概念(面向对象的功能)
- jQuery(基于jQuery方式实现功能)
- Ajax(基于接口实现功能)
- 跨域
- 同源策略和跨域
- 跨域解决方案:前后端配合JSONP;纯后端方案(cors、反向代理)
- JSONP的原理
- script标签可以发送跨域请求
- jsonp接口返回:【函数调用】
- 标签script必须添加到页面才可以
- callback参数的作用:定制回调函数的名称
- 基于jQuery的Ajax方法发送jsonp请求相关参数
- dataType 设置响应数据格式 jsonp
- jsonp 约定等号左侧的名称(该名称给后端用,用于获取回调函数的名称,名字由后端决定)
- jsonpCallback 约定回调函数的名称(不重要)
- 淘宝搜索案例
- 获取关键字
- 发送请求
- 渲染结果
异步编程
同步和异步
同步和异步是相对两个参与者来说的
- 张三找李四去吃饭,李四正则玩游戏
- 场景一:张三等待李四完成游戏任务,然后两个人一块去,此时对于张三来说是阻塞的(等待),这种模式称之为同步
- 场景二:张三通知李四一声然后自己直接去吃饭,等李四完成游戏任务再去找张三,此时,张三没有阻塞(不等待),这种模式称之为异步
- 浏览器向服务器发送请求
- 场景一:浏览器向服务器发送请求,但是服务器尚未返回是,客户端如果处于等待状态,那么这种模式叫同步
- 场景二:浏览器向服务器发送请求后直接去做别的事情,不用等待返回,当服务器返回数据后,通知浏览器去处理数据,这种模式叫异步
ajax异步代码执行流程
需求:初步理解程序的异步执行流程
<button id="btn">点击</button>
<script>
// 原生Ajax
function queryData() {
// 1、创建xhr实例对象
const xhr = new XMLHttpRequest()
// 2、准备请求参数
xhr.open('get', 'http://test.zjie.wang/tab')
// 3、执行发送的动作
xhr.send(null)
// 4、监听服务器返回的结果
let ret = null
// 浏览器发送完成请求后,仅仅是监听返回的结果,但是不会等到结果返回后再继续执行后续代码
// 而是,监听完成后,立刻运行后续代码(不会在这里阻塞),这种模式称之为【异步】
xhr.onreadystatechange = function () {
if (xhr.readyState === 4) {
if (xhr.status === 200) {
ret = xhr.responseText
}
}
}
// 所以这里打印的结果是null(因为此时尚未获取到服务器返回的数据)
console.log(ret)
}
const btn = document.getElementById('btn')
btn.onclick = function () {
queryData()
}
</script>
总结
- 浏览器发送完成请求后,仅仅是监听返回的结果,但是不会等到结果返回后再继续执行后续代码
,而是,监听完成后,立刻运行后续代码(不会在这里阻塞),这种模式称之为【异步】
获取异步结果的问题
- 异步的结果无法通过返回值获取
<button id="btn">点击</button>
<script>
// 原生Ajax
function queryData() {
// 1、创建xhr实例对象
const xhr = new XMLHttpRequest()
// 2、准备请求参数
xhr.open('get', 'http://test.zjie.wang/tab')
// 3、执行发送的动作
xhr.send(null)
// 4、监听服务器返回的结果
let ret = null
// 浏览器发送完成请求后,仅仅是监听返回的结果,但是不会等到结果返回后再继续执行后续代码
// 而是,监听完成后,立刻运行后续代码(不会在这里阻塞),这种模式称之为【异步】
xhr.onreadystatechange = function () {
if (xhr.readyState === 4) {
if (xhr.status === 200) {
ret = xhr.responseText
}
}
}
// 所以这里打印的结果是null(因为此时尚未获取到服务器返回的数据)
// console.log(ret)
return ret
}
const btn = document.getElementById('btn')
btn.onclick = function () {
// 我们需要在函数外面得到异步的结果
const ret = queryData()
console.log(ret)
}
</script>
注意:异步的结果无法通过返回值获取
获取异步结果的办法
- 必须通过回调函数获取异步的结果,不可以使用函数返回值
<button id="btn">点击</button> <script> // 原生Ajax function queryData(callback) { // 1、创建xhr实例对象 const xhr = new XMLHttpRequest() // 2、准备请求参数 xhr.open('get', 'http://test.zjie.wang/tab') // 3、执行发送的动作 xhr.send(null) // 4、监听服务器返回的结果 xhr.onreadystatechange = function () { if (xhr.readyState === 4) { if (xhr.status === 200) { let ret = xhr.responseText // 调用回调函数并且把结果传递回去 // 这个回调函数何时调用?服务器返回结果后才调用 callback(ret) } } } } const btn = document.getElementById('btn') btn.onclick = function () { // 我们需要在函数外面得到异步的结果 // const ret = queryData() // console.log(ret) // 异步的结果必须通过回调函数获取 // 空手而进,满载而归 queryData(function (ret) { console.log(ret) }) } </script>
- 异步嵌套问题
<script> // 异步回调的问题 function getData1(callback) { setTimeout(function () { let data = 'tom' callback(data) }, 1000) } function getData2(callback) { setTimeout(function () { let data = 'jerry' callback(data) }, 2000) } function getData3(callback) { setTimeout(function () { let data = 'spike' callback(data) }, 3000) } // 要求:先后打印 spike -> tom -> jerry // getData3(function (ret3) { // console.log(ret3) // }) // getData1(function (ret1) { // console.log(ret1) // }) // getData2(function (ret2) { // console.log(ret2) // }) // 第二次调用接口需要的参数来源于上次接口调用的返回结果 // 嵌套:如下的执行方式是串行的 // 如果这种嵌套有18层,就想到了地狱:回调地狱(callbakc hell) getData3(function (ret3) { console.log(ret3) getData1(function (ret1) { console.log(ret1) getData2(function (ret2) { console.log(ret2) }) }) }) // 为了改进上述问题、就诞生了一种新的技术Promise // 但是Promise也不是最好的方案 // 所以后来又诞生了一种新技术 Async函数 (async/await) </script>
总结
- 为了完成异步任务的顺序控制,必须进行回调的嵌套
- 但是嵌套过多会出现【回调地狱】问题
- 为了改进上述问题、就诞生了一种新的技术Promise
- 但是Promise也不是最好的方案,所以后来又诞生了一种新技术 Async函数 (async/await)
协议介绍
协议基本概念
协议:交流双发的一种约定(具体是一些通信规则)
- 电脑之间通信是否有规则?有的,就是通信协议(标准化组织规定的)
- HTTP协议
HTTP HyperText Transfer Protocol 超文本传输协议 网页中有超链接,点击之后可以跳转到另外一个页面,这种页面就是超文本
- 通信流程
- 客户端主动向服务器发送请求
- 服务器收到请求后返回内容
通信相关概念
- URL地址 http://www.baidu.com:80
- 协议
- 主机名:IP地址或者域名
- 端口
- IP地址:唯一标识互联网中某一台计算机 220.181.38.149
- 域名:IP地址的别名,方便记忆,多个域名可能会关联同一个IP地址
- DNS:用于维护IP地址和域名之间的对应关系
- 端口:取值范围(0-65535),唯一区分计算机上的某个网络应用程序
- url: http://ip/域名:端口
通信流程细节
请求(ppt)
- 请求状态行
- 请求头:客户端传递给服务器的相关配置信息
- 请求空行
- 请求体:客户端传递给服务器的相关参数(主要是POST提交)
响应(ppt)
- 响应状态行
- 响应头:服务器返回给客户端的配置信息
- 响应空行
- 响应体:服务器返回给客户端的具体数据
请求和响应发送的数据格式有专门的规定(http协议)
请求方法
- 如果接口文档明确要求使用指定的请求方式,前端必须严格遵守规则
- 对服务器资源进行的操作
- 增删改查(CRUD-create-read-update-delete)
- 规范的接口设计会明确告诉你使用哪种请求方式
- GET 查询
- POST 添加
- PUT 修改
- DELETE 删除
总结:发送请求时的方式由协议规定;对于后端接口来说,用那种请求方式由后端决定。
响应状态码
- 标识服务端响应的状态(服务器告诉客户端请求响应的结果是什么情况)
- 2XX 表示服务端返回的数据是正常的(常见的是200,201)
- 4XX 客户端发生了错误,因为服务器对应的资源不存在(常见的是404、401/403)
- 5XX 服务器发生错误,服务器代码出错了(常见的是500)
- 3XX 重定向,服务器收到请求后,返回一个地址,让浏览器重新请求到另外一个地址
总结:状态吗用于服务器通知浏览器返回结果的情况。