Ajax异步通信

Posted 葡萄藤IT技能树

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Ajax异步通信相关的知识,希望对你有一定的参考价值。


修真院Web工程师零基础全能课


本节课内容

Ajax异步通信


主讲人介绍

沁修,葡萄藤技术总监

项目经验丰富,擅长H5移动项目开发。

专注技术选型、底层开发、最佳代码实践规范总结与推广。



直播录屏版






文字解析版


1.XMLHttpRequest


最早的页面,只要和服务器发生交互,就会提交整个页面、刷新整个页面。

即使只提交一个字符也要这样,用户体验很差,服务器的负担也比较重。


那有没有一个办法来解决这个问题呢?

就是在不向服务器提交整个页面的情况下,实现局部更新页面。


有,最开始人们想到一些办法,借助一些hack手段来实现,比如内嵌框架等。

直到由微软设计并在IE5中引入了XMLHttpRequest对象,可扩展超文本传输请求,简称XHR。

之后被各家浏览器所采用,最后被W3C组织标准化,因此现在可以随意使用它了。



我们现在所用到的Ajax技术的核心就是XHR对象。

XHR为客户端向服务器发送请求以及解析服务器相应提供了流畅的接口,通过它很容易取回一个url上的资源数据。


这就意味着用户点击之后,不用刷新页面也能获取新的数据,然后再通过DOM将新数据插入到页面中。

到现在为止,也催生出了许多新技术和新模式,但熟练使用XHR对象仍然是前端开发人员必须掌握的一种技能。


虽然它名字含有XML ,但该对象可以接受任何数据类型而不仅仅为XML,而且它支持的协议类型不限于HTTP(包括file,ftp)

发起一个完整的请求有如下几步


Ajax异步通信


第一步:创建XHR对象

XMLHttpRequest是一个构造函数,因此在使用它的时候,首先将它实例化:

var xhr = new XMLHttpRequest();


Ajax异步通信


第二步:向服务器发送请求

在获得一个实例化的xhr对象后,要调用两个方法以发送请求,open和send。


第一个方法是open(),open方法并不会发送请求,它只是启动一个请求以备发送,它可以接受3个参数。


xhr.open(“GET”, ”xxx.jsp", false);


第一个参数指定发送请求的方式,是一个字符串,不区分大小写,但通常惯例都是使用大写字母。

其中GET和POST是最常用的方式。

GET是用于常规请求,请求的参数也会包含在url中。

POST请求时,参数会带在request body上,经常用于表单上。


他们还有各自的语义,一个是获取一个是发送。

除了GET和POST外,这个参数还可以是PUT, DELETE等。

但到底应该怎么用,还需要大家在项目中去历练一下,并且很可能还需要和后端开发人员商量。


第二个参数指明文件在服务器上的位置,而且只能向同一个域当中使用相同端口和协议的url发送,否则就是跨域,跨域的问题我们后面会讲到。


第三个参数是一个可选参数,是个布尔值,true表示异步,false表示同步,默认为true。

一般情况下我们都会用true,如果设置为false的话,请求就会变成同步请求。

也就是说JS代码执行到这里时,会等待服务器响应后才继续执行。


如果网络不是快到光速一样,那整个程序就会挂起停止:

我们的直观感受就是页面一直卡到内容有响应了才回来继续执行,我们先使用同步,等会儿来看异步。


第二个方法是使用send(),这个方法接收一个参数,就是要发送的数据,如果没有数据就是null,调用这个方法后,请求就会被分派到服务器了:

xhr.send(null);


Ajax异步通信


第三步:服务器响应

在请求发送后,接下来就是要接收服务器的相应了

在收到响应后,响应的数据会自动填充到XHR对象的属性上,相关的属性有这么几个:


responseText:响应所返回的文本,无论内容类型是什么,响应主体的内容都会保存到这个属性上

responseXML:XML形式的响应数据

status:响应的http状态,数字类型。状态码有非常多,我们最熟悉的就是200表示成功,404表示没有找到资源,更多的就大家自行去查看。


statusText:响应的http状态说明,文本类型

readyState:表明请求的状态,是一个整数

onreadystatechange:指派在请求的状态变化是所调用的事件


var xhr = new XMLHttpRequest();

xhr.open(“GET”, ”xxx.jsp", false);

xhr.send(null);

if ((xhr.status >= 200 && xhr.status < 300) || xhr.status == 304){ // 判断最好用status,因为statusText各个浏览器下并不见得一致

     alert(xhr.responseText);

} else {

     alert("Request was unsuccessful: " + xhr.status);

}


Ajax异步通信


到目前为止都是同步响应,如果需要异步响应,就需要检测XHR对象的readyState属性了。

这个属性表示整个请求响应过程的不同阶段,它可以有这么几个值:


0:未初始化。尚未调用 open()方法。

1:启动。已经调用 open()方法,但尚未调用 send()方法。

2:发送。已经调用 send()方法,但尚未接收到响应。

3:接收。已经接收到部分响应数据。

4:完成。已经接收到全部响应数据,而且已经可以在客户端使用了。


只要这个readyState属性变化一次都会触发一次readystatechange事件

然后我们就可以利用这个事件来检测每次变化后readyState的值,就能知道目前是在什么阶段了。


Ajax异步通信


那代码可以改成这样:


var xhr = new XMLHttpRequest();

xhr.onreadystatechange = function(){

    if (xhr.readyState == 4){

        if ((xhr.status >= 200 && xhr.status < 300) || xhr.status == 304){

              document.getElementById(“content”).innerhtml = xhr.responseText;

    } else {

         alert("Request was unsuccessful: " + xhr.status);

    }

} };

xhr.open("get", "example.txt", true);

xhr.send(null);


可以看出来,一般来说,我们只对readyState的值等于4才感兴趣,因为数据已经就绪,可以接着做许多事情了。

这样我们已经能使用XHR对象发起简单的异步通信了。


这个时候有没有人想过我们才学过jQuery,如果用jQuery库来做这个事情,应该怎么做呢?


Ajax异步通信


2.jQuery的$.ajax

jQuery对Ajax操作进行了封装,$.ajax()方法属于最底层的方法,它的语法是这样的:


$.ajax(options)


这个options就是指定它的各项参数,我们列举了最最常用的几个:


名称 类型 说明
url 字符串 请求的URL
type 字符串 要使用HTTP方法,通常是POST,GET,如果省略就默认为GET
data 对象 需要传递的查询参数
dataType 字符串 标识将被响应所返回的数据类型,有xml,html,json,jsonp,script,text
success 函数 响应的状态码为成功时调用这个函数
error 函数 如果响应状态码为错误就调用这个函数


然后来看一下刚刚用原生JS写的那段代码用jQuery来写应该怎么写:


$.ajax({

    url: "xxx.jsp",

    type: "GET",

    data: {t: "sd"},

    dataType: "json",

    success: function(data) {

        $("#content").text(data);

    }

})


在jQuery中的Ajax还有一些高级方法,比如$.get(), $.post()等,它们就相当于一个简化版的$.ajax。

比如假设我们要发起一个get请求从服务器获取数据时可以这样做:


$.get(“xxx.jsp”, {a: 1}, function(data) {alert(data)});


这些方法让我们用起来更加简单和方便,提高生产力。


Ajax异步通信


3.JSON

JSON是一种轻量级的数据交换格式,它的字符集是UTF-8,它其实是javascript的一个子集,数据类型也和JS是差不多的。

它规定了字符串必须用双引号,object的键也必须用双引号。


由于json特别简单,很快就风靡web世界成为标准,现在几乎所有的语言都有解析JSON的库。

而在js中我们可以直接使用它,因为js内置了json的解析。


在js中如果有对象,可以序列化为一个JSON格式的字符串


var data =  {

    a: "abc",

    b: 123,

    c: false,

    d: [1,2,3,4],

    e: {

        aa: 123

    }

}

var str = JSON.stringify(data);

console.log(str);


如果拿到一个JSON格式的字符串,也可以把它反序列化为一个JS对象


JSON.parse(‘{“a”: 1, “b”: “web"}’);

JSON.parse(‘[“web”, “java”, 123]’);


Ajax异步通信


4.跨域

接下来我们可以去请求一下API里的数据了,这里我在网上随便找了一个免费的API:


https://www.sojson.com/api/semantic.html



Ajax异步通信


如果我们在我们的网页中使用ajax来访问它,那结果就有一点不尽人意了:


$.getJSON("http://api.qingyunke.com/api.php", {

    key: "free",

    appid: 0,

    msg: $("#question").val(),

}, function(data) {

    $("#answer").text(data.content);

})



Ajax异步通信


这就是出现了跨域问题,浏览器出于安全隐患的考虑,阻止了这个请求。

如何解决跨域有不止一种方法,但要么是hack形式的做法,要么是需要服务器端配合。

而这里我们调用第三方的接口明显没有人会来和我们做配合,那么可以优先考虑nginx反向代理的办法来解决跨域的问题。


说到nginx,它是一个高性能的HTTP服务器也是一个反向代理器。

服务器应该好理解,启动一个静态服务器意味着我们可以通过特定的域名来访问本地的文件了。


那反向代理又是什么呢?有正向代理吗?

来看一下这张图:


Ajax异步通信


其实平时我们的翻墙科学上网就是一种正向代理,是放在客户端的代理。

比如说我直接访问youtube会被墙掉,那就加一层代理来转发,实际会将客户端隐藏掉。



反向代理会负责统一统筹这些资源,隐藏了真实的服务器端,也就是说可以做负载均衡。


我们可以先来了解一下怎么将它是怎么配置使用的。

首先是安装nginx,不同系统下应该怎么安装可以自行去查阅相关资料。

我们这里假设大家都已经安装好了nginx,启动nginx服务器,输入网址localhost访问本地可以看到这个页面就说明服务已经启动了




Ajax异步通信


打开这个文件后能看到nginx服务器的基础配置,默认的配置也在这里面。


#user  nobody;

worker_processes  1;

#error_log  logs/error.log;

#error_log  logs/error.log  notice;

#error_log  logs/error.log  info;

#pid        logs/nginx.pid;

events {

    worker_connections  1024;

}

http {

    include       mime.types;

    default_type  application/octet-stream;

    #log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '

    #                  '$status $body_bytes_sent "$http_referer" '

    #                  '"$http_user_agent" "$http_x_forwarded_for"';

    #access_log  logs/access.log  main;

    sendfile        on;

    #tcp_nopush     on;

    #keepalive_timeout  0;

    keepalive_timeout  65;

    #gzip  on;

    server {

        listen       80;

        server_name  localhost;

        #charset koi8-r;

        #access_log  logs/host.access.log  main;

        location / {

            root   html;

            index  index.html index.htm;

        }

        #error_page  404              /404.html;

        # redirect server error pages to the static page /50x.html

        #

        error_page   500 502 503 504  /50x.html;

        location = /50x.html {

            root   html;

        }

        # proxy the PHP scripts to Apache listening on 127.0.0.1:80

        #

        #location ~ .php$ {

        #    proxy_pass   http://127.0.0.1;

        #}

        # pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000

        #

        #location ~ .php$ {

        #    root           html;

        #    fastcgi_pass   127.0.0.1:9000;

        #    fastcgi_index  index.php;

        #    fastcgi_param  SCRIPT_FILENAME  /scripts$fastcgi_script_name;

        #    include        fastcgi_params;

        #}

        # deny access to .htaccess files, if Apache's document root

        # concurs with nginx's one

        #

        #location ~ /.ht {

        #    deny  all;

        #}

    }

    # another virtual host using mix of IP-, name-, and port-based configuration

    #

    #server {

    #    listen       8000;

    #    listen       somename:8080;

    #    server_name  somename  alias  another.alias;

    #    location / {

    #        root   html;

    #        index  index.html index.htm;

    #    }

    #}

    # HTTPS server

    #

    #server {

    #    listen       443 ssl;

    #    server_name  localhost;

    #    ssl_certificate      cert.pem;

    #    ssl_certificate_key  cert.key;

    #    ssl_session_cache    shared:SSL:1m;

    #    ssl_session_timeout  5m;

    #    ssl_ciphers  HIGH:!aNULL:!MD5;

    #    ssl_prefer_server_ciphers  on;

    #    location / {

    #        root   html;

    #        index  index.html index.htm;

    #    }

    #}

}

#号是表示注释掉的代码,除此之外太细节的地方我们可以暂时不用管,只了解一下它大体有几个块。


首先是从第一行开始的全局块,影响nginx全局的指令。

然后是events块,影响nginx服务器与用户的网络连接的配置。

再下面就是http块,配置代理缓存日志等服务器功能。

再下面就是server,一个server就相当于一台虚拟主机。


我们目前主要需要配置http块来利用它的代理功能。

http里面可以有多个server,比如我们本地有3个网站,那就可以在http中写3个server。先来配置一个试试看:


server {

    listen 8081;

    server_name local.web.ptteng.com;

    root /Users//web-lessons;

    location / {

            index index.html index.htm;

    }

    location /ajax/ {

        rewrite  ^/ajax/(.*)$ /$1 break;

        proxy_pass   http://api.qingyunke.com;

    }

}


Ajax异步通信


listen就是声明服务器监听的端口号,默认是80,如果这个未定义那么就会默认为80.


如果访问的url和这里的server_name一致,就会执行里面的配置运转这个server啦。

另外这里需要配合修改一下host,否则就真的去找互联网上的这个域名了,而不是指向本机。


root就是放置我们需要托管的静态文件,指向了这些文件的存放路径,也就是根目录。

基本的东西设置完毕了,接下来就是配置路由location,路由来指向结果页面。


location需要接收两个参数,一个是花括号前的字符串,一个是花括号以及里面的配置内容。


这次我们再来修改一下js代码,把url改为/ajax/api.php。


$.getJSON("/ajax/api.php", {

    key: "free",

    appid: 0,

    msg: $("#question").val(),

}, function(data) {

    $("#answer").text(data.content);

})


Ajax异步通信


去掉了开头的http,这会导致我们在请求这个接口时,会访问本服务器的域名再加上后面这些文件路径。

也就是:

http://local.web.ptteng.com/ajax/api.php 


这个时候路径上当然是没有这个文件的,但是nginx会匹配这个url发现它里面有一个/ajax/的字符串和刚刚配置的server里的location一致。

因此就会在访问这个路径的时候转发到:

http://api.qingyunke.com。


也就是说,我们的浏览器,看字符串以为我们访问了一个同域同源的接口,没有进行阻拦。

但在真正请求中,却被nginx悄悄替换成了另外一个三方接口,实现了跨域。


以上就是上节课的内容解析啦




Ajax异步通信




下节预告


Ajax异步通信



报名听课的同学们将获得:



1、与技术总监级别的一线开发人员接触、提问机会

2、来自真实项目的应用开发技巧、代码规范

3、与直播课程配套的任务体系、日报体系

4、分配一对一的专属师兄,进行技术辅导

5、课程PPT、录播视频、细化知识点解析课堂等学习资源



周六上午开课,不占用工作、学习时间

一餐饭的价格,汲取实战角度的开发经验

快来扫码报名吧!







以上是关于Ajax异步通信的主要内容,如果未能解决你的问题,请参考以下文章

Ajax异步通信

同步请求和异步请求的区别(理解ajax用)

325 AJAX介绍,XMLHttpRequest对象,实现Ajax的异步交互步骤,服务器端通信状态

如何实现服务端客户端异步通信

vue和jQuery嵌套实现异步ajax通信

Vue05-Axios异步通信