Ajax-day04

跨域介绍

同源策略

同源策略:是浏览器提供的一种安全机制,限制来自不同源的数据。 如果当前页面的URL地址和Ajax的请求地址不同源,浏览器不允许得到服务器的数据

  • 协议、域名和端口都相同则同源,只要有一项不同就是不同源

image.png

  • 跨域:不同源就是跨域
    • 当前显示的页面中,如果触发了一个Ajax请求,请求的URL地址和当前页面的URL地址不同源就是跨域
    • 浏览器不允许跨域获取数据,但是实际会经常有这个跨域获取数据需求

跨域解决方案

我们希望:浏览器可以通过Ajax跨域得到数据

  • jsonp (需要前端和后端的配合才可以)
  • 纯后端解决方案(如果后端实现了cors配置,那么前端就可以直接跨域获取对应的数据)
    • cors(后端写代码实现)
    • 服务端反向代理(后端安装软件实现Apache/Nginx)
  • 跨域Ajax请求的错误提示(后端不支持跨域)

image.png

jsonp跨域原理分析

  • jsonp的原理:利用script标签的src属性发送跨域请求,请求返回的结果是js代码【函数调用】,被调用的函数【回调函数】需要在发送请求之前进行定义
  1. // jsonp的基本原理
  2. // 虽然Ajax不支持跨域获取Ajax的数据,但是script标签发出的请求支持跨域获取数据
  3. // 动态创建一个script标签
  4. const st = document.createElement('script')
  5. st.src = 'http://www.liulongbin.top:3006/api/jsonp?callback=hello'
  6. document.body.appendChild(st)
  7. // 回调函数(前端定义;后端接口返回的结果来调用)
  8. function hello(ret) {
  9. // 这里的ret就是跨域获取到的数据
  10. console.log(ret)
  11. }
  12. // jsonp接口返回的结果是什么?函数调用
  13. // 返回的结果 hello({"message":"JSONP请求测试成功","data":{}})

总结:

  1. script标签发送的请求本身支持跨域
  2. 发送请求必须携带参数callback=hello,他的作用是定制回调函数的名称
  3. 该动态创建的script标签必须添加到页面才能生效

jQuery对jsonp的支持

  • $.ajax() 该方法可以发送jsonp请求,jsonp请求本质上不是Ajax
  1. // 基于jQuery方式发送jsonp请求
  2. // http://www.liulongbin.top:3006/api/jsonp?
  3. // callback=jQuery3410722416410580281_1619945266328
  4. // &_=1619945266329
  5. // jQuery请求地址中自动添加了一个时间戳,作用是防止浏览器缓存
  6. $.ajax({
  7. type: 'get',
  8. url: 'http://www.liulongbin.top:3006/api/jsonp',
  9. // 设置响应的数据类型格式 text/json/jsonp
  10. dataType: 'jsonp',
  11. // 可以自定义回调函数的名称
  12. jsonpCallback: 'hello',
  13. // 定制等号左侧的名称
  14. jsonp: 'callback',
  15. success: function (ret) {
  16. console.log(ret)
  17. }
  18. })
  • jsonp相关的两个属性
    • jsonp 属性的值为了迎合后端(后端根据该名称获取回调函数的名字),jsonp的值决定了等号左侧的名字
    • jsonpCallback 决定等号右侧的内容(服务端返回的函数调用的名称)
    • jQuery请求地址中自动添加了一个时间戳,作用是防止浏览器缓存
  1. $.ajax({
  2. // 请求地址
  3. url: 'http://www.liulongbin.top:3006/api/jsonp',
  4. // 请求参数
  5. data: {
  6. uname: 'lisi',
  7. age: 12
  8. },
  9. // jsonp属性的值为了迎合后端(后端根据该名称获取回调函数的名字)
  10. // jsonp的值决定了等号左侧的那个名字
  11. // callback=jQuery34105174086151731971_1589093752115
  12. // cb=jQuery34105174086151731971_1589093752115
  13. jsonp: 'callback', // jsonp的默认值就是callback
  14. // jsonpCallback决定等号右侧的内容(服务端返回的函数调用的名称)
  15. // callback=jQuery34105174086151731971_1589093752115
  16. // callback=nihao
  17. jsonpCallback: 'jsonp564',
  18. // 服务端返回的数据格式:json/text/jsonp
  19. dataType: 'jsonp',
  20. // 回调函数
  21. success: function (data) {
  22. console.log(data)
  23. }
  24. })

jsonp的缺点

  • 仅支持get请求,不支持其他请求方式
    • script标签发出请求本身就是get,不支持别的请求方式
    • get请求传递的数据量有限制(URL地址的长度不超过8000个字符)
    • post请求可以传递更多数据(post用请求体方式传递)
    • 如果确实希望跨域post提交数据,怎么办?让后端去做CORS

淘宝搜索案例

  • 案例图示如下所示

image.png

需求:类比淘宝搜索提交实现类似功能,接口基于淘宝的真实接口https://suggest.taobao.com/sug,请求参数:关键字名称是 q

获取用户输入关键词

  • 绑定输入事件并获取输入的值
  1. // 1、监听用户的输入事件(键盘抬起)
  2. $('#keyword').keyup(function (e) {
  3. // 2、获取输入的关键字
  4. // 原生js中有一个字符串方法trim,作用是去掉两侧的空格
  5. const kw = e.target.value.trim()
  6. if (!kw) {
  7. // 没有输入任何有效字符,终止后续代码
  8. return
  9. }
  10. })

总结

  1. 去掉字符串两侧的空格:原生js的trim()方法
  2. 如果没有输入有效关键字,阻止发送请求

获取搜索列表数据

  • 获取搜索列表结果(调用jsonp跨域接口)
  1. $.ajax({
  2. // 请求地址
  3. url: 'https://suggest.taobao.com/sug‘
  4. // 返回数据格式
  5. dataType: 'jsonp',
  6. // 请求参数
  7. data: {
  8. q: kw
  9. },
  10. // 回调函数
  11. success: function (ret) {
  12. console.log(ret.result)
  13. }
  14. })

总结:

  1. 调用jsonp接口获取搜索结果
  2. 需要额外传递get请求参数q

动态渲染列表

需求:展示搜索的列表数据

  1. // 1、监听用户的输入事件(键盘抬起)
  2. $('#keyword').keyup(function (e) {
  3. // 2、获取输入的关键字
  4. // 原生js中有一个字符串方法trim,作用是去掉两侧的空格
  5. const kw = e.target.value.trim()
  6. if (!kw) {
  7. // 没有输入任何有效字符,终止后续代码
  8. // 清空列表(empty方法用于清空DOM里面的内容)
  9. $('#suggest-list').empty().hide()
  10. return
  11. }
  12. // 把关键字发送接口
  13. $.ajax({
  14. url: 'https://suggest.taobao.com/sug',
  15. data: {
  16. q: kw
  17. },
  18. dataType: 'jsonp',
  19. success: function (ret) {
  20. // console.log(ret.result[0][0])
  21. // 动态渲染数据
  22. let listTags = ''
  23. $.each(ret.result, function (index, item) {
  24. listTags += '<div class="suggest-item">' + item[0] + '</div>'
  25. })
  26. $('#suggest-list').html(listTags).show()
  27. }
  28. })
  29. })

总结:

  1. 基于jQuery动态渲染列表内容
  2. 退格时控制清空列表
  3. 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异步代码执行流程

需求:初步理解程序的异步执行流程

  1. <button id="btn">点击</button>
  2. <script>
  3. // 原生Ajax
  4. function queryData() {
  5. // 1、创建xhr实例对象
  6. const xhr = new XMLHttpRequest()
  7. // 2、准备请求参数
  8. xhr.open('get', 'http://test.zjie.wang/tab')
  9. // 3、执行发送的动作
  10. xhr.send(null)
  11. // 4、监听服务器返回的结果
  12. let ret = null
  13. // 浏览器发送完成请求后,仅仅是监听返回的结果,但是不会等到结果返回后再继续执行后续代码
  14. // 而是,监听完成后,立刻运行后续代码(不会在这里阻塞),这种模式称之为【异步】
  15. xhr.onreadystatechange = function () {
  16. if (xhr.readyState === 4) {
  17. if (xhr.status === 200) {
  18. ret = xhr.responseText
  19. }
  20. }
  21. }
  22. // 所以这里打印的结果是null(因为此时尚未获取到服务器返回的数据)
  23. console.log(ret)
  24. }
  25. const btn = document.getElementById('btn')
  26. btn.onclick = function () {
  27. queryData()
  28. }
  29. </script>

总结

  1. 浏览器发送完成请求后,仅仅是监听返回的结果,但是不会等到结果返回后再继续执行后续代码
    ,而是,监听完成后,立刻运行后续代码(不会在这里阻塞),这种模式称之为【异步】

获取异步结果的问题

  • 异步的结果无法通过返回值获取
  1. <button id="btn">点击</button>
  2. <script>
  3. // 原生Ajax
  4. function queryData() {
  5. // 1、创建xhr实例对象
  6. const xhr = new XMLHttpRequest()
  7. // 2、准备请求参数
  8. xhr.open('get', 'http://test.zjie.wang/tab')
  9. // 3、执行发送的动作
  10. xhr.send(null)
  11. // 4、监听服务器返回的结果
  12. let ret = null
  13. // 浏览器发送完成请求后,仅仅是监听返回的结果,但是不会等到结果返回后再继续执行后续代码
  14. // 而是,监听完成后,立刻运行后续代码(不会在这里阻塞),这种模式称之为【异步】
  15. xhr.onreadystatechange = function () {
  16. if (xhr.readyState === 4) {
  17. if (xhr.status === 200) {
  18. ret = xhr.responseText
  19. }
  20. }
  21. }
  22. // 所以这里打印的结果是null(因为此时尚未获取到服务器返回的数据)
  23. // console.log(ret)
  24. return ret
  25. }
  26. const btn = document.getElementById('btn')
  27. btn.onclick = function () {
  28. // 我们需要在函数外面得到异步的结果
  29. const ret = queryData()
  30. console.log(ret)
  31. }
  32. </script>

注意:异步的结果无法通过返回值获取

获取异步结果的办法

  • 必须通过回调函数获取异步的结果,不可以使用函数返回值
  1. <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>
  • 异步嵌套问题
  1. <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>

总结

  1. 为了完成异步任务的顺序控制,必须进行回调的嵌套
  2. 但是嵌套过多会出现【回调地狱】问题
  3. 为了改进上述问题、就诞生了一种新的技术Promise
  4. 但是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 删除

image.png

总结:发送请求时的方式由协议规定;对于后端接口来说,用那种请求方式由后端决定。

响应状态码

  • 标识服务端响应的状态(服务器告诉客户端请求响应的结果是什么情况)

image.png

  • 2XX 表示服务端返回的数据是正常的(常见的是200,201)
  • 4XX 客户端发生了错误,因为服务器对应的资源不存在(常见的是404、401/403)
  • 5XX 服务器发生错误,服务器代码出错了(常见的是500)
  • 3XX 重定向,服务器收到请求后,返回一个地址,让浏览器重新请求到另外一个地址

总结:状态吗用于服务器通知浏览器返回结果的情况。