AJAX学习笔记

Posted by Ivens on November 15, 2019

前言

在学习AJAX之前,有几个问题需要先花点时间弄明白

同步和异步的区别?

  • 同步方法调用一旦开始,调用者必须等到方法调用返回后,才能继续后续的行为。

  • 异步方法调用更像一个消息传递,一旦开始,方法调用就会立即返回,调用者就可以继续后续的操作。所以先开始执行的代码不一定执行完才会执行下一行代码.

什么时候需要异步:

(1)需要等待的时候,等待过程不能卡在这吧

(2)等待过程不像 alert 一样阻塞程序运行

(3)等待的情况都要异步

使用异步的场景:

(1)定时任务,setTimeoutsetInterval

(2)网络请求: AJAX请求,动态<img>加载

(3)事件绑定,点击等交互事件

JS是异步编程语言,这就是说JS代码的执行顺序并不是从上至下按部就班完成的。

为什么要使用回调函数?

先看下面的这段代码:

1
2
3
4
5
6
7
8
9
10
11
function A() {
    B(); 
    console.log('我是主函数');      
}

function B(){
    setTimeout("console.log('我是回调函数')", 3000);//模仿耗时操作  
}

//调用主函数
A();

再看一个常见的回调函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
//定义主函数,回调函数作为参数
function A(callback) {
    callback();  
    console.log('我是主函数');      
}

//定义回调函数
function B(){
    setTimeout("console.log('我是回调函数')", 3000);//模仿耗时操作  
}

//调用主函数,将函数B传进去
A(B);

输出结果:

1
2
我是主函数
我是回调函数

执行的顺序是:先进入A方法 → 执行callback()函数 → 执行log函数 → 结束

同样的结果,那我们为什么要使用回调函数?

因为这样并不完美,因为这么做就相当于将B()”焊死”A()里了,如果此处我最终想调用的函数不是B()而是别的其他函数咋整?难不成要写几个不同的A1().A2().A3()?而他们之间的区别仅仅是最后调用的那个函数不同.

所以为了代码的灵活性和复用性,我们决定使用回调函数。

大多数编程语言中,函数的形参总是由外往内向函数体传递参数,但在JS里如果形参是关键字”callback”则完全相反,它表示函数体在完成某种操作后由内向外调用某个外部函数

补充: 如果不想为要调用的函数再单独写个方法,我们可以使用匿名函数.那么代码则改成这样:

1
2
3
4
5
6
7
8
function A(callback){
    console.log("111");
    callback(123);
}

A(function(value){
    console.log(value);
})

结果为:

1
2
111
123

正文

什么是AJAX?

AJAX = Asynchronous JavaScript and XML(异步的 JavaScript 和 XML)。

AJAX 最大的优点是在不重新加载整个页面的情况下,可以与服务器交换数据并更新部分网页内容。传统的网页(不使用 AJAX)如果需要更新内容,必需重载整个网页

在 2005 年,Google 通过其 Google Suggest 使 AJAX 变得流行起来。
Google Suggest 使用 AJAX 创造出动态性极强的 web 界面:当您在谷歌的搜索框输入关键字时,JavaScript 会把这些字符发送到服务器,然后服务器会返回一个搜索建议的列表。

创建XHR对象

XMLHttpRequest 对象 (异步的与服务器交换数据)

1
2
3
4
5
6
7
8
9
10
11
var xmlhttp;
if (window.XMLHttpRequest)
{
    //  IE7+, Firefox, Chrome, Opera, Safari 浏览器执行代码
    xmlhttp=new XMLHttpRequest();
}
else
{
    // IE6, IE5 浏览器执行代码
    xmlhttp=new ActiveXObject("Microsoft.XMLHTTP");
}

XHR请求

如需将请求发送到服务器,我们使用 XMLHttpRequest 对象的 open() 和 send() 方法:

1
2
xmlhttp.open("GET","ajax_info.txt",true);
xmlhttp.send();

open()方法的三个参数:

  • method:请求的类型;GET 或 POST
  • url:文件在服务器上的位置
  • async:true(异步)或 false(同步)

POST和GET的选择?

与 POST 相比,GET 更简单也更快,并且在大部分情况下都能用。

然而,在以下情况中,请使用 POST 请求:

  • 无法使用缓存文件(更新服务器上的文件或数据库)
  • 向服务器发送大量数据(POST 没有数据量限制)
  • 发送包含未知字符的用户输入时,POST 比 GET 更稳定也更可靠

一个简单的 GET 请求:

1
2
xmlhttp.open("GET","/try/ajax/demo_get.php",true);
xmlhttp.send();

如果您希望通过 GET 方法发送信息,请向 URL 添加信息:

1
2
xmlhttp.open("GET","/try/ajax/demo_get2.php?fname=Henry&lname=Ford",true);
xmlhttp.send();

一个简单的 POST 请求:

1
2
xmlhttp.open("POST","/try/ajax/demo_post.php",true);
xmlhttp.send();

如果需要像 HTML 表单那样 POST 数据,请使用 setRequestHeader() 来添加 HTTP 头。然后在 send() 方法中规定您希望发送的数据:

1
2
3
xmlhttp.open("POST","/try/ajax/demo_post2.php",true);
xmlhttp.setRequestHeader("Content-type","application/x-www-form-urlencoded");       //HTTP头
xmlhttp.send("fname=Henry&lname=Ford");

setRequestHeader(header,value)—向请求添加 HTTP 头。

  • header: 规定头的名称
  • value: 规定头的值

服务器响应

当我们需要执行一些基于响应的任务时,需要用到onreadystatechange()函数.

readyState 属性存有 XMLHttpRequest 的状态信息。每当 readyState 改变时,就会触发 onreadystatechange 事件。

获取来自服务器的响应,请使用 XMLHttpRequest 对象的 responseText 或 responseXML 属性。

  • responseText—获得字符串形式的响应数据。
  • responseXML—获得 XML 形式的响应数据。

当 readyState 等于 4 且状态为 200 时,表示响应已就绪:

1
2
3
4
5
6
7
8
xmlhttp.onreadystatechange=function()
  {
  //onreadystatechange 事件被触发 5 次(0 - 4),对应着 readyState 的每个变化。
  if (xmlhttp.readyState==4 && xmlhttp.status==200)
    {
        document.getElementById("myDiv").innerHTML=xmlhttp.responseText;
    }
  }

什么是AJAX跨域?

这是因为浏览器的同源策略导致的。默认情况下,JavaScript在发送AJAX请求时,URL的域名必须和当前页面完全一致

完全一致的意思是:

  • 域名要相同(www.example.com和example.com不同)
  • 协议要相同(http和https不同)
  • 端口号要相同(默认是:80端口,它和:8080就不同)。有的浏览器口子松一点,允许端口不同,大多数浏览器都会严格遵守这个限制。

跨域的方法

一.通过Flash插件发送HTTP请求.

这种方式可以绕过浏览器的安全限制,但必须安装Flash,并且跟Flash交互。不过Flash用起来麻烦,而且现在用得也越来越少了。

二.通过在同源域名下架设一个代理服务器来转发.

JavaScript负责把请求发送到代理服务器.这种方式麻烦之处在于需要服务器端额外做开发。

三.JSONP

它有个限制,只能用GET请求,并且要求返回JavaScript。这种方式跨域实际上是利用了浏览器允许跨域引用JavaScript资源.

我们可以添加<script>标签去调用外部js,再从外部js中访问外域网站,获得返回值后使用回调函数.

以163的股票查询为例,对于URL:http://api.money.126.net/data/feed/0000001,1399001?callback=refreshPrice, (这是一个接口+一个回调函数) 你将得到如下返回:

1
refreshPrice({"0000001":{"code": "0000001", ... });

实现代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
    <script>
        function refreshPrice(data) {
                var p = document.getElementById('test-jsonp');
                p.innerHTML = '当前价格:' +
                    data['0000001'].name +': ' + 
                    data['0000001'].price + '' +
                    data['1399001'].name + ': ' +
                    data['1399001'].price;//③
            }

        function getPrice() {
            var
                js = document.createElement('script'),
                head = document.getElementsByTagName('head')[0];
            js.src = 'http://api.money.126.net/data/feed/0000001,1399001?callback=refreshPrice';
            head.appendChild(js);//②这里会先访问接口,然后把接口的返回值传给回调函数.
        }
    </script>
</head>
<body>
    <div>
        <p id="test-jsonp">当前价格:</p>
        <p><button type="button" onclick="getPrice()">刷新</button></p>//①
    </div>
</body>
</html>

四.CORS

CORS全称Cross-Origin Resource Sharing,是HTML5规范定义的如何跨域访问资源。

了解CORS前,我们先搞明白概念:

Origin表示本域,也就是浏览器当前页面的域。当JavaScript向外域(如sina.com)发起请求后,浏览器收到响应后,首先检查Access-Control-Allow-Origin是否包含本域,如果是,则此次跨域请求成功,如果不是,则请求失败,JavaScript将无法获取到响应的任何数据。

假设本域是my.com,外域是sina.com,只要响应头Access-Control-Allow-Originhttp://my.com,或者是*,本次请求就可以成功。

Access-Control-Allow-Origin的值是由对方服务器设定的.所以,跨域能否成功,取决于对方服务器是否愿意给你设置一个正确的Access-Control-Allow-Origin,决定权始终在对方手中。

当你引用了某个第三方CDN上的字体文件时:

1
2
3
4
@font-face {
  font-family: 'FontAwesome';
  src: url('http://cdn.com/fonts/fontawesome.ttf') format('truetype');
}

如果该CDN服务商未正确设置Access-Control-Allow-Origin,那么浏览器无法加载字体资源。

这种跨域请求,称之为“简单请求”。简单请求包括GETHEADPOST(POST的Content-Type类型 仅限application/x-www-form-urlencodedmultipart/form-datatext/plain),并且不能出现任何自定义头(例如,X-Custom: 12345),通常能满足90%的需求。

对于PUT、DELETE以及其他类型如application/json的POST请求:

  1. 在发送AJAX请求之前,浏览器会先发送一个OPTIONS请求(称为preflighted请求)到这个URL上,询问目标服务器是否接受.
  2. 服务器必须响应并明确指出允许的Method.
  3. 浏览器确认服务器响应的Access-Control-Allow-Methods头确实包含将要发送的AJAX请求的Method,才会继续发送AJAX,否则,抛出一个错误。