美文网首页
JavaScript基础篇(四) AJAX

JavaScript基础篇(四) AJAX

作者: 橙色流年 | 来源:发表于2020-09-26 20:26 被阅读0次

Ajax 没出现前,JavaScript 在大多数开发者眼里应该还是个玩具语言,毕竟在那个刀耕火种的年代,压根就没有前端开发师这个角色,不是个全栈你都不好意思称呼自己是个程序猿,所以在那个年代出现的大牛也比较多。AjaxNode 发布之后,随着时代的发展,人们对页面的 UI 设计和交互体验也越来看重,前端所需要的承担的职责也越来越多,导致前后端的界限也越来越明显,进而衍生出了前端工程师这个职位!当然前端生态现在也是欣欣向荣,例如服务端、可视化、3D动画、小程序、APP等都可以通过 JavaScript 来实现。说了这么多,其实还是想突出 Ajax 的重要性,毕竟在前端历史上有着承上启下的作用。

说起 Ajax ,感觉里程碑式的 Jquery 真的必须留下姓名,我在两年前写过一篇为什么github放弃jQuery,但是我们不得不承认 Jquey 在前端历史上的地位。历史的车轮纵然是滚滚向前,但是照亮行业的星辰还是值得每一个人肃然起敬。煽情完毕,进入主题:

Ajax 的作用不过多赘述了,主要是连接前后端通信和数据传输用的,像我们在 jQuery 中写的 $.ajaxvue 中使用的 axios,还有基于传统语法优化后的 fetch,其实都是 ajax 的一种封装应用。

原生 AJAX 请求的五个步骤:


  • 创建一个XMLHttpRequest异步对象
  • 设置请求方式和请求地址
  • 接着,用send发送请求
  • 监听状态变化
  • 最后,接收返回的数据

结合上面的五个步骤,我们先来手写一下代码:

const xhr = new XMLHttpRequest()
xhr.open('GET', './data/test.json', true)
xhr.onreadystatechange = function () {
  if (xhr.readyState === 4) {
    if (xhr.status === 200) {
      console.log(JSON.parse(xhr.responseText))
    } else {
      console.log('其它情况...')
    }
  }
}
xhr.send()

代码结合上述步骤看,应该很清晰,我这里就罗列一下你可能会觉得奇怪的地方:

  • xhr.open('GET', './data/test.json', true) 第三个参数 true 代表啥?

第三个参数为 true,则表示 JavaScript 异步执行,不等待后台返回直接向下执行。而为 false 的时候,表示同步执行,等待返回后再执行下一步。

  • xhr.readyState === 4 是什么鬼,为啥不是等于其他值偏偏等于 4 呢?

0:(未初始化),还没有调用 send() 方法。
1:(载入) 已调用 send() 方法,正在发送请求。
2:(载入完成) send() 方法执行完成,已经接收到全部响应内容。
3:(交互) 正在解析响应内容。
4:(完成) 响应内容解析完成,可以在客户端调用。

  • xhr.status == 200 又代表啥呢,有没有其它可选值?

2xx:一般代表请求成功并正确返回后端传给我们的值,例如:200
3xx:需要重定向,浏览器直接跳转,例如:301、302、304
4xx:客户端请求错误,例如:404、403
5xx:服务端错误

跨域


了解跨域之前,我们先认识一下 同源策略 这个概念:

  • ajax 请求时,浏览器要求当前网页和 server 必须同源(保证安全)
  • 同源的定义:协议、域名、端口,三者必须完全一致

举个栗子:

前端:http://a.com:8080/
server: https://b.com/api/xxx

上述两个链接,协议不同(一个 http 协议,一个 https 协议),端口不同(一个 8080 端口,一个没写端口号继承默认的 80 端口),域名也不同(一个 a.com,另一个 b.com)。

但是加载图片或者 css/js 可无视同源策略。即我们做的网站可以直接使用淘宝的图片,或者直接引用淘宝线上的 css 样式。向我们使用 cdn 基本就是这个道理。

认识跨域:

1、如果我们的 ajax 请求不满足同源策略规定的任意一项,即可被认为存在跨域请求行为。
2、所有的跨域,都必须经 server 端允许和配合 。
3、未经 server 端允许就实现跨域,说明浏览器有漏洞,危险信号。

认识 JSONP

jsonp 应该算是以前处理跨域的一种方法,并且存在很多限制(比如说只能处理 get 请求的跨域)。其实现在我们用的 vue 这些框架来写的话跨域的配置基本都是使用代理形式,也比较简单易懂,这里就不展开。jsonp 的实现原理其实也很简单,就是利用我们前面说的 css/js 可无视同源策略,干巴巴的文字肯定不如代码来的痛快:

a.html => 'http://192.168.0.105:8000/ajax.html' // a.html的url
<script src="http://192.168.0.105:8080/c.js"></script> // a.html 里面写的内容
c.js => 'http://192.168.0.105:8080/c.js' // c.js 的url
alert(111) // c.js 里面写的内容

如果我们要在 a.html 里面用 ajax 请求 c.js 里面返回的数据,受制于同源策略两者之间访问肯定是存在跨域行为的(它们俩端口号不一样),所以导致它们俩无法直接通信。但是如果我们通过 server 端的帮助,将要返回的数据放入到一个新的脚本文件中,然后把脚本文件的地址返回给我们,我们在将这个脚本地址手动添加到 html 页面上,那么我们就能直接获取到这个文件里面的数据了。例如上述代码,a.html 可以直接读取 'c.js' 中的内容并正确弹出 111

我们先来看一段跨域的代码及报错提示:

// 请求代码就用我们最初手写的 (文件路径:http://192.168.0.105:8000/ajax.html)
const xhr = new XMLHttpRequest()
xhr.open('GET', 'http://192.168.0.105:8080/d.json', true)
xhr.onreadystatechange = function () {
  if (xhr.readyState === 4) {
    if (xhr.status === 200) {
      console.log(JSON.parse(xhr.responseText))
    } else {
      console.log('其它情况...')
    }
  }
}
xhr.send()

// d.json(文件路径:http://192.168.0.105:8080/d.json)
{"name": "zhangsan"}

具体的报错提示一般如下:


跨域报错.png

其实就是两者端口号不同,所以导致跨域,接下来我们用我们刚刚说的 jsonp 的原理实现跨域正常请求(这里我们将后端模拟返回的数据放到 c.js 中):

// a.html
<script>
  function abc(data) {
    console.log(data.message) // zhangsan
  }
</script>
<script src="http://192.168.0.105:8080/c.js"></script>
// c.js
<script>
abc({
  message: 'zhangsan'
})
</script>

看懂了吗?后端处理要返回的 json 数据,然后放入到 abc 函数中,前端就可以通过 abc 函数直接拿到后端给我们返回的对象。这其实就是 JSONP 的简单实现模式,或者说是 JSONP 的原型:创建一个回调函数,然后在远程服务上调用这个函数并且将 JSON 数据形式作为参数传递,完成回调。将 JSON 数据填充进回调函数。

当然,一般情况下来说 <script src="http://192.168.0.105:8080/c.js"></script> 这句话肯定是动态添加的,因为这个 src 的地址我们提前是不知道的,只有后端告诉我们了,我们在将它动态添加到页面上,所以一般向下面的代码这样写:

<script>
  function abc(data) {
    console.log(data.message); // zhangsan
  }
  //添加<script>标签的方法
  function addScriptTag(src) {
    var script = document.createElement('script');
    script.setAttribute("type", "text/javascript");
    script.src = src;
    document.body.appendChild(script);
  }
  window.onload = function () {
    addScriptTag("http://192.168.0.105:8080/c.js");
  }
</script>

上面的例子是最简单的JSONP的实现模型,不过它还算不上一个真正的JSONP服务。我们来看一下真正的JSONP服务是怎么样的,比如Google的ajax搜索接口:http://ajax.googleapis.com/ajax/services/search/web?v=1.0&q=?&callback=,看这个请求,你会发现后面有一个 ?q= 这个问号是表示你要搜索的内容,最重要的是第二个 callback=? 这个是正如其名表示回调函数的名称,也就是将你自己在客户端定义的回调函数的函数名传送给服务端,服务端则会返回以你定义的回调函数名的方法,将获取的json数据传入这个方法完成回调。

<script>
  window.onload = function () {
    // 这里的 q 可以为我们查询的关键字,而 callback 为我们前端约定给后端返回的回调函数方法名
    addScriptTag("http://192.168.0.105:8080/c.js?q=aaa&callback=abc");
  }
</script>

当然,我们也可以直接使用 jquery ,因为它里面已经帮我们做了 jsonp 请求的代码封装,在这里我们在将上面的代码用 jquery 在重新实现一遍:

<script src="https://cdn.bootcdn.net/ajax/libs/jquery/1.7.2/jquery.min.js"></script>
<script>
  $.ajax({
    url: "http://192.168.0.105:8080/c.js",
    type: "GET",
    dataType: "jsonp", //指定服务器返回的数据类型
    jsonpCallback: 'abc', // 这里服务端返回的回调函数方法名
    success: function (data) {
      console.log(data) // {message: "zhangsan"}
    }
  });
</script>

其实看上面的代码我们就知道,jsonp 限制不少,根本不好用,前端用的不舒服,后端更是觉得麻烦,所以我们大致了解实现的过程就行了,因为现在基本不会采用 jsonp 的方式来解决跨域。像现在比较流行的前端框架都有自己处理跨域的成熟方法,也可以通过服务器端配置来做跨域设置,说实话无论哪一种真的都完爆 jsonp

了解 cors,服务器设置 http header 实现跨域请求

// 第二个参数填写允许跨域的域名称,* 表示所有域名都可以,不建议直接用 *
response.setHeader('Access-Control-Allow-Origin', 'http://192.168.0.105:8080 || *')
response.setHeader('Access-Control-Allow-Headers', 'X-Requested-With')
response.setHeader('Access-Control-Allow-Methods', 'PUT,GET,POST,DELETE')
// 接收跨域的 cookie
response.setHeader('Access-Control-Allow-Credentials', 'true')

这种主要还是后端进行配置,一次配置再无跨域烦恼,我们毕竟是前端工程师,简单了解就好了。

结语


其实应该用 promise 手写一个简易的 ajax 来做总结的,但是 promiseasync/await 我都写过类似的文章了,这里就不在赘述了!因为像现在非常流行的 axios 基本也都是 promise 封装 XMLHttpRequest 对象进而实现的!!!对 Promise 感兴趣但是了解又不深入的可以直接看我写的 Promise基础详解。如果文中有不对的地方或者理解有误的地方欢迎大家提出并指正。每一天都要相对前一天进步一点,加油!!!

相关文章

网友评论

      本文标题:JavaScript基础篇(四) AJAX

      本文链接:https://www.haomeiwen.com/subject/hzbcuktx.html