J56 AJAX

190 阅读5分钟

一、什么是 AJAX

async javascript and xml:异步的 JS 和 XML

  • 1.此处的异步指的是:局部刷新(对应的是全局刷新)
  • 2.XML:可扩展的标记语言,用自己自定义的标签来存储数据的(在很早以前,我们基于 AJAX 和服务器进行交互的数据格式一般都已 XML 格式为主,因为它能清晰展示出对应的数据和结构层级;但是到后面,流行了一种新的数据格式 JSON,它不仅比 XML 更清晰展示数据的结构,而且同样的数据存储,JSON 更加轻量,也方便解析和相关的操作,所以现在前后端的数据交互都已 JSON 格式为主)

1、xml格式

<?xml version="1.0" encoding="UTF-8"?>
<root>
	<student>
		<name>张三</name>
		<age>25</age>
		<score>
			<english>95</english>
		</score>
	</student>
	<student>
		<name>张三</name>
		<age>25</age>
	</student>
	<student>
		<name>张三</name>
		<age>25</age>
	</student>
</root>

2 json

[{	"name": "张三",	"age": 25,	"score": {		"english": 95	}}, {	"name": "张三",	"age": 25}, {	"name": "张三",	"age": 25}]

1、服务器渲染(服务器接收到请求后)

  • 1、找到对应的页面,获取到页面的代码
  • 2、在根据需求从数据库中获取到需要动态展示的数据
  • 3、把页面和数据混合在一起进行渲染,生成有结构有内容的完整页面=》服务器渲染
  • 4、把渲染完的页面返回给客户端

2.服务器渲染渲染的特点

  • 1、我们看到的内容都是在服务器端渲染完的(JSP、PHP、ASP.NET NODE... ),客户端只是吧所有渲染好的内容呈现在页面中而已;然后我们第一次渲染完,页面中的某部分数据需要更新了,我们需要让服务器整体重新的渲染一次,把最新全局刷新,性能和体验等都非常的差,而且服务器压力也很大
  • 2、如果服务器性能比较高,页面呈现出来的速度会快一些,因为只要从服务器拿到内容,一切信息都已经准备好了
  • 3、由于内容在服务器端就已经渲染好了,所以页面渲染完成后,在页面的源代码红都可以看到内容,有利于seo搜索引擎优化

3.客户端渲染数据内容

  • 1、可以实现页面中内容局部刷新,而且渲染的操作交给客户端来做=>体验好、减轻了服务器的压力
  • 2、而且开始可以只把部分区域的数据获取到,滚动到某个区域后,在请求对应的数据也可以,实现数据的 分批异步加载,提高性能体验
  • 3、由客户端渲染的内容没有出现在页面的源代码中,不利于seo优化

4.当代项目中开发的整个架构模型

  • 1、纯服务器渲染(需要做seo优化或者是技术氛围问题 )
  • 2、混编模式:部分内容是服务器渲染,部分内容是客户端渲染=>常见的
    • 骨架屏:首屏内容为服务器渲染(目的是让页面一打开,就能吧首屏内容加载出来,前提服务器得给力),其余屏内容都是客户端基于AJAX再逐一获取到的,对于表单提交等数据交互操作也是客户端基于AJAX等操作为主(局部刷新)
  • 3、完全客户端和服务器端 分离开发=>目前最常见的(推荐的)
    • VUE、react、jq
  • 4、把vue和react在服务器端基于node来渲染=>服务器端渲染

二、AJAX的基础操作

  • 1.创建AJAX实例
    • IE低版本浏览器中用的是 new ActiveXObject() 高程三中JS惰性编程思想,关于XHR的兼容处理
let xhr=new XMLHttpRequest; 
  • 2.打开URL(配置发送请求的信息)
    • METHOD:HTTP请求方式
    • URL:请求地址(API接口地址)
    • ASYNC:设置同步或者异步,默认是TRUE异步,FALSE同步
    • USER-NAME:传递给服务器的用户名
    • USER-PASS:传递给服务器的密码
xhr.open('GET','./json/xxx.json',true);
  • 3.监听AJAX状态,在状态为X的时候,获取服务器响应的内容
    • AJAX状态码:0 1 2 3 4
xhr.onreadystatechange=function(){
	if(xhr.readyState===4 && /^(2|3)\d{2}$/.test(xhr.status)){
		let result = xhr.responseText;
	}
}
  • 4.发送请求
    • SEND中放的是请求主体的内容
xhr.send(null);

=>AJAX任务(发送一个请求给服务器,从服务器获取到对应的内容)从SEND后开始,到XHR.READYSTATE===4的时候算任务结束

三、HTTP的请求方式

1. GET系列请求

+ GET
+ DELETE 一般应用于告诉服务器,从服务器上删除点东西
+ HEAD 只想获取响应头内容,告诉服务器响应主体内容不要了
+ OPTIONS 试探性请求,发个请求给服务器,看看服务器能不能接收到,能不能返回

2. POST系列请求

+ POST
+ PUT 和DELETE对应,一般是想让服务器把我传递的信息存储到服务器上(一般应用于文件和大型数据内容)

3.使用场景

真实项目中用对应的请求方式,会使请求变的更加明确(语义化),不遵循这些方式也可以,最起码浏览器在语法上是允许的;但是这些是开发者们相互间约定俗成的规范;

GET系列一般用于从服务器获取信息,POST系列一般用于给服务器推送信息,但是不论GET和POST都可以把信息传递给服务器,也能从服务器获取到结果,只不过是谁多谁少的问题

  • GET:给的少,拿的多
  • POST:给的多,拿的少

4.客户端怎么把信息传递给服务器?

  • 问号传参 xhr.open('GET','/getdata?xxx=xxx&xxx=xxx')
  • 设置请求头 xhr.setRequestHeader([key],[value])
  • 设置请求主体 xhr.send(请求主体信息)

5.服务器怎么把信息返回给客户端?

  • 通过响应头
  • 通过响应主体(大部分信息都是基于响应主体返回的)

6.GET系列和POST系列的本质区别:

GET系列传递给服务器信息的方式一般采用:问号传参 POST系列传递给服务器信息的方式一般采用:设置请求主体

  • 6.1 GET传递给服务器的内容比POST少,因为URL有最长大小限制(IE浏览器一般限制2KB,谷歌浏览器一般限制4~8KB,超过长度的部分自动被浏览器截取了)
xhr.open('GET','/list?name=zhangsan&year=10&xxx=xxx...')
xhr.send('....')  请求主体中传递的内容理论上没有大小限制,但是真实项目中,为了保证传输的速度,我们会自己限制一些
  • 6.2 GET会产生缓存(缓存不是自己可控制的):因为请求的地址(尤其是问号传递的信息一样),浏览器有时候会认为你要和上次请求的数据一样,拿的是上一次信息;这种缓存我们不期望有,我们期望的缓存是自己可控制的;所以真实项目中,如果一个地址,GET请求多次,我们要去除这个缓存;
//=>解决办法设置随机数
xhr.open('GET','/list?name=zhufeng&_='+Math.random());
...
xhr.open('GET','/list?name=zhufeng&_='+Math.random());
  • 6.3 GET相比较POST来说不安全:GET是基于问号传参传递给服务器内容,有一种技术叫做URL劫持,这样别人可以获取或者篡改传递的信息;而POST基于请求主体传递信息,不容易被劫持;

四、AJAX的状态码

xhr.readyState 获取状态码

  • UNSEND 0 : 未发送(创建一个XHR,初始状态是0)
  • OPENED 1 :已经打开(执行了xhr.open)
  • HEADERS_RECEIVED 2 : 响应头信息已经返回给客户端(发送请求后,服务器会依次返回响应头和响应主体的信息)
  • LOADING 3 : 等待服务器返回响应内容
  • DONE 4 : 响应主体信息已经返回给客户端
//1、创建ajax实例对象
let xhr=new XMLHttpRequest;
console.log(xhr.readyState);//=>0
//2、建立连接
xhr.open("GET","json/data.json");
 console.log(xhr.readyState);//=>1
//3、发送请求
xhr.send(null);

//4/接收数据
xhr.onreadystatechange=function(){
    console.log(xhr.readyState);//2 3 4
    
    //5.判断状态为2的时候
    if(/^(2丨3)\d{2}$/.test(xhr.status)) return;
    if(xhr.readyState===2){
         //=>获取响应头信息
        //获取的服务器时间是标准的日期格式对象
        //获取的服务器时间是标准的日期格式对象(GMT格林尼治时间)
		//new Date()能把格林尼治时间转换为北京时间
 	let serverTime = xhr.getResponseHeader('Date');
        console.log(new Date(serverTime));
       
    }

    //6.判断状态为4的时候
    if(xhr.readyState===4){
        //获取相应主题信息:我们一般用responseText,因为服务器返回的信息是json格式的字符串,如果返回的事xml格式,我们用responseXML...
       //xhr.responseXML
       //xhr.response 
       //xhr.responseType:响应类型
      console.log(xhr.responseText);
    }
}

五、AJAX方法

  • 1.xhr.abort: 中断请求
  • 2.getAllResponseHeaders :获取所有头请求信息
  • 3.getResponseHeader:获取响应头信息
  • 4.overrideMimeType:
  • 5.send:发送
  • 6.open:打开
  • 7.setRequestHeader:设置请求头
let xhr=new XMLHttpRequest;
//1.设置AJAX等待时间,超过这个时间算ajax延迟
xhr.timeout=10;

//2.超时后触发这个事件
 xhr.ontimeout=function(){
    console.log("请求超时~~~");
    //=>手动中断ajax的请求
    xhr.abort();
}

//3.在跨域请求中是否允许携带证书(携带cookIE)
xhr.withCredentials=true;

//4.设置请求头信息 
//设置请求头不能使中文 
   xhr.setRequestHeader('AAA', encodeURIComponent('Hello'));

xhr.open("GET","data.json");  
xhr.onreadystatechange=function(){   
if(!/^(2|3)\d{2}$/.test(xhr.status)) return;    
if(xhr.readyState===4){
    console.log(xhr.responseText);
}
}   
xhr.send(null);

六、AJAX同步和异步

1.异步

let xhr=new HMLHttpRequest;
    xhr.open("GET","json/data.json");
    //=>设置事件绑定之前状态是1
    xhr.onreadystatechange=function(){
        console.log(xhr.readyState);//=>2 3 4
    }
    xhr.send(null);

let xhr=new HMLHttpRequest;
    xhr.open("GET","json/data.json");
        xhr.send(null);
    xhr.onreadystatechange=function(){
        console.log(xhr.readyState);//=>2 3 4
    }

2.同步

let xhr=new HMLHttpRequest;
xhr.open("GET","json/data.json",false);    
xhr.onreadystatechange=function(){
    console.log(xhr.readyState);
//=>4 使用ajax同步编程,不能在状态码为2的时候获取到相应的信息,
//=>但是转态码为4的时候也是可以获取到头和主题信息
}
xhr.send(null);

let xhr=new HMLHttpRequest;
xhr.open("GET","json/data.json",false); 
xhr.send(null);
//=>执行后,只有状态码为4才会继续处理下面的代码
//=>状态码为4的时候绑定的,而状态码为4的时候也是可以获取到头和主题信息的
xhr.onreadystatechange=function(){
console.log(xhr.readyState);//=>无输出
}

七、JQUERY中AJAX的应用

1、$.ajax() 基于原生JS的AJAX四步操作进行封装

  • $.ajax([URL],[OPTIONS])
  • $.ajax([OPTIONS]) URL在配置项中(推荐)
  • $.get/post/getJSON/getScript()

2、配置项信息

  • 1)url:请求的API接口地址
  • 2)method:HTTP请求方式,默认GET
  • 3)data:传递给服务器的信息,默认null(可以是字符串,可以是对象,而且如果GET系列请求,JQ会自动把信息拼接到URL的末尾,基于问号传参传递给服务器;如果是POST请求,JQ会基于请求主体,把信息传递给服务器)
  • 4)dataType:预设服务器返回的结果格式(服务器返回的一般都是JSON格式的字符串,如果我们设置了DATA-TYPE,JQ会根据设置的类型,把服务器返回的结果处理为对应的格式),支持的内容text / json / xml / html / script / jsonp(跨域) =>不影响服务器返回的结果,只是把服务器返回的结果进行二次处理
  • 5)async:是否为异步操作,默认是TRUE,代表异步操作
  • 6)cache:缓存处理,只对GET系列请求有作用,默认是TRUE不处理缓存,当我们设置FALSE后,JQ帮我们在URL的末尾设置一个随机数
  • 7)contentType:设置传递给服务器内容的格式类型 默认是"application/x-www-form-urlencoded"
  • 客户端传递给服务器信息的格式(类型一般都是字符串),常用的:
    • form-data表单数据:JSON格式 '{"name":"xxx","lx":1}'
    • x-www-form-urlencoded:name=xxx&lx=1
    • raw:纯文本格式
  • 8)headers:设置请求头信息,他是一个对象
  • 9)timeout:设置超时的时间
  • 10)success:回调函数,当数据请求成功执行,方法中的参数就是从服务器获取的结果
  • 11)error:回调函数,数据请求失败执行,方法中的参数是错误信息
$.ajax({
	url: 'http://yapi.demo.qunar.com/mock/95100/project/list',
	method: 'POST',
	data: {
		name: 'zhufeng',
		lx: 'teacher'
	},
	dataType: 'json',
	async: true,
	cache: false,
	headers: {},
	success: (result, status, xhr) => {
		//=>xhr:是JQ帮我们处理过的AJAX实例
		console.log(result, status, xhr);
	}
});

八、倒计时抢购

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>倒计时抢购</title>
</head>
<body>
<div id="box"></div>

<script>
//1、new Data()获取客户端本地当前时间(不能拿它做重要依据,因为用户可以随意修改)
/* 倒计时抢购需要从服务器获取当前时间 AJAX
*  问题:时间差  (从服务器把事件给客户端,到客户端获取到这个信息,中间经历的时间就是时间差,而时间差事不可避免的,我们应该尽可能减少这个误差)
*    - 从 响应头获取时间(AJAX)
*    - 基于head请求(只获取响应头信息) 
*/
//1、抢购的目标开始时间
let target = new Date('2020/05/15 01:15:00'),
	now = null,
	timer = null;

//=>从服务器获取时间:获取到时间后再做其他的事情
function func(callback) {
	let xhr = new XMLHttpRequest;
	xhr.open('HEAD', 'json/data.json', true);
	xhr.onreadystatechange = function () {
		if (!/^(2|3)\d{2}$/.test(xhr.status)) return;
		if (xhr.readyState === 2) {
			now = new Date(xhr.getResponseHeader('Date'));
			callback && callback();
		}
	}
	xhr.send(null);
}

//=>开启倒计时模式
function computed() {
	let spanTime = target - now;
	if (spanTime <= 0) {
		//=>到抢购时间:结束定时器
		clearInterval(timer);
		timer = null;
		box.innerHTML = "开抢~~";
		return;
	}
	let hours = Math.floor(spanTime / (60 * 60 * 1000));
	spanTime -= hours * 60 * 60 * 1000;
	let minutes = Math.floor(spanTime / (60 * 1000));
	spanTime -= minutes * 60 * 1000;
	let seconds = Math.floor(spanTime / 1000);
	box.innerHTML =
		`距离抢购还剩 ${hours<10?'0'+hours:hours}:${minutes<10?'0'+minutes:minutes}:${seconds<10?'0'+seconds:seconds}`;

	//=>每一次计算完,我们需要让NOW在原来的基础上加上一秒(第一次从服务器获取到时间,后期直接基于这个时间自己加即可,不要每隔一秒重新从服务器拿)
	now = new Date(now.getTime() + 1000);
}
func(() => {
	//=>已经从服务器获取时间了
	computed();
	timer = setInterval(computed, 1000);
});
        
</script>
</body>
</html>