Part7: Ajax基础、JQuery版Ajax库封装

一、Ajax局部刷新和全局刷新

1.什么是 AJAX

async javascript and xml:异步的 JS 和 XML

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

客户端渲染VS服务端渲染:在这里插入图片描述

2.AJAX的基础操作
//1.创建AJAX实例
let xhr=new XMLHttpRequest; //=>IE低版本浏览器中用的是 new ActiveXObject() 高程三中JS惰性编程思想,关于XHR的兼容处理

//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的时候算任务结束
3.HTTP的请求方式
  • GET系列请求
    • GET
    • DELETE 一般应用于告诉服务器,从服务器上删除点东西
    • HEAD 只想获取响应头内容,告诉服务器响应主体内容不要了
    • OPTIONS 试探性请求,发个请求给服务器,看看服务器能不能接收到,能不能返回
  • POST系列请求
    • POST
    • PUT 和DELETE对应,一般是想让服务器把我传递的信息存储到服务器上(一般应用于文件和大型数据内容)

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

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

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

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

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

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

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

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

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

1. GET传递给服务器的内容比POST少,因为URL有最长大小限制(IE浏览器一般限制2KB,谷歌浏览器一般限制4~8KB,超过长度的部分自动被浏览器截取了)

xhr.open('GET','/list?name=zhufeng&year=10&xxx=xxx...')
xhr.send('....')  请求主体中传递的内容理论上没有大小限制,但是真实项目中,为了保证传输的速度,我们会自己限制一些

2. GET会产生缓存(缓存不是自己可控制的):因为请求的地址(尤其是问号传递的信息一样),浏览器有时候会认为你要和上次请求的数据一样,拿的是上一次信息;这种缓存我们不期望有,我们期望的缓存是自己可控制的;所以真实项目中,如果一个地址,GET请求多次,我们要去除这个缓存;

//=>解决办法设置随机数
xhr.open('GET','/list?name=zhufeng&_='+Math.random());
...
xhr.open('GET','/list?name=zhufeng&_='+Math.random());

3. GET相比较POST来说不安全:GET是基于问号传参传递给服务器内容,有一种技术叫做URL劫持,这样别人可以获取或者篡改传递的信息;而POST基于请求主体传递信息,不容易被劫持;

4.AJAX状态码和常用方法

xhr.readyState 获取状态码:

  • UNSEND 0 : 未发送(创建一个XHR,初始状态是0)
  • OPENED 1 :已经打开(执行了xhr.open)
  • HEADERS_RECEIVED 2 : 响应头信息已经返回给客户端(发送请求后,服务器会依次返回响应头和响应主体的信息)
  • LOADING 3 : 等待服务器返回响应内容
  • DONE 4 : 响应主体信息已经返回给客户端

AJAX实例对象常用的方法:

  • xhr.abort();
  • xhr.getAllResponseHeaders();
  • xhr.getResponseHeader([key]);
  • xhr.open();
  • xhr.overrideMimeType();
  • xhr.send();
  • xhr.setRequestHeader();
let xhr = new XMLHttpRequest;
// console.log(xhr.readyState); //=>0
xhr.open('GET', 'json/data.json');
// console.log(xhr.readyState); //=>1
xhr.onreadystatechange = function () {
	// console.log(xhr.readyState); //=>2 3 4
	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));
	}
	if (xhr.readyState === 4) {
		//=>获取响应主体信息:我们一般用responseText,因为服务器返回的信息一般都是JSON格式的字符串,如果返回的是XML格式,我们用responseXML...
		// xhr.responseXML
		// xhr.response
		// xhr.responseType
		// console.log(xhr.responseText);
	}
}
xhr.send(null);
let xhr = new XMLHttpRequest;
xhr.timeout = 10; //=>设置AJAX等待时间,超过这个时间算AJAX延迟,数据就获取不到了
// xhr.ontimeout = function () {
// 	console.log('请求超时~~');
// 	xhr.abort(); //=>手动中断AJAX的请求
// }
xhr.withCredentials = true; //=>在跨域请求中是否允许携带证书(携带COOKIE)
xhr.open('GET', 'json/data.json');
//=>设置请求头信息
// xhr.setRequestHeader('AAA', encodeURIComponent('珠峰培训'));
// Uncaught TypeError: Failed to execute 'setRequestHeader' on 'XMLHttpRequest': Value is not a valid ByteString. 设置的请求头信息值不能是中文
xhr.onreadystatechange = function () {
	if (!/^(2|3)\d{2}$/.test(xhr.status)) return;
	if (xhr.readyState === 4) {
		console.log(xhr.responseText);
	}
}
xhr.send(null);
5.AJAX同步异步编程【较难】
<!DOCTYPE html>
<html>

<head>
	<meta charset="UTF-8">
	<title>AJAX</title>
</head>

<body>
	<!-- <script>
		let xhr = new XMLHttpRequest;
		// console.log(xhr.readyState); //=>0
		xhr.open('GET', 'json/data.json');
		// console.log(xhr.readyState); //=>1
		xhr.onreadystatechange = function () {
			// console.log(xhr.readyState); //=>2 3 4
			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));
			}
			if (xhr.readyState === 4) {
				//=>获取响应主体信息:我们一般用responseText,因为服务器返回的信息一般都是JSON格式的字符串,如果返回的是XML格式,我们用responseXML...
				// xhr.responseXML
				// xhr.response
				// xhr.responseType
				// console.log(xhr.responseText);
			}
		}
		xhr.send(null);
	</script> -->

	<!-- <script>
		let xhr = new XMLHttpRequest;
		xhr.timeout = 10; //=>设置AJAX等待时间,超过这个时间算AJAX延迟,数据就获取不到了
		// xhr.ontimeout = function () {
		// 	console.log('请求超时~~');
		// 	xhr.abort(); //=>手动中断AJAX的请求
		// }
		xhr.withCredentials = true; //=>在跨域请求中是否允许携带证书(携带COOKIE)
		xhr.open('GET', 'json/data.json');
		//=>设置请求头信息
		// xhr.setRequestHeader('AAA', encodeURIComponent('珠峰培训'));
		// Uncaught TypeError: Failed to execute 'setRequestHeader' on 'XMLHttpRequest': Value is not a valid ByteString. 设置的请求头信息值不能是中文
		xhr.onreadystatechange = function () {
			if (!/^(2|3)\d{2}$/.test(xhr.status)) return;
			if (xhr.readyState === 4) {
				console.log(xhr.responseText);
			}
		}
		xhr.send(null); 
	</script> -->

	<!-- 同步异步 -->
	<script>
		//=>AJAX的异步
		let xhr = new XMLHttpRequest;
		xhr.open('GET', 'json/data.json');
		//=>设置事件绑定之前状态1
		xhr.onreadystatechange = function () {
			console.log(xhr.readyState); //=>2 3 4
		}
		xhr.send(null); 

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


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

		/* let xhr = new XMLHttpRequest;
		xhr.open('GET', 'json/data.json', false);
		xhr.send(null);//=>执行后,只有状态码为4才会继续处理下面的代码
		//=>状态码为4的时候绑定的,而状态不会在变了,所以方法不会执行
		xhr.onreadystatechange = function () {
			console.log(xhr.readyState);
		} */

		// API:Application Programming Interface 凡是可被别人调用,并且给予反馈结果的都可以被称之为API接口
	</script>

</body>

</html>

在这里插入图片描述

6.JQuery中的AJAX应用
<!DOCTYPE html>
<html>

<head>
	<meta charset="UTF-8">
	<title>JQUERY中AJAX的应用</title>
</head>

<body>
	<script src="js/jquery.min.js"></script>
	<script>
		/* 
		 * $.ajax() 基于原生JS的AJAX四步操作进行封装
		 *    $.ajax([URL],[OPTIONS])
		 *    $.ajax([OPTIONS])  URL在配置项中(推荐)
		 *    $.get/post/getJSON/getScript()
		 *    ......
		 * 配置项信息
		 *    url:请求的API接口地址
		 *    method:HTTP请求方式,默认GET
		 *    data:传递给服务器的信息,默认null(可以是字符串,可以是对象,而且如果GET系列请求,JQ会自动把信息拼接到URL的末尾,基于问号传参传递给服务器;如果是POST请求,JQ会基于请求主体,把信息传递给服务器)
		 *    dataType:预设服务器返回的结果格式(服务器返回的一般都是JSON格式的字符串,如果我们设置了DATA-TYPE,JQ会根据设置的类型,把服务器返回的结果处理为对应的格式),支持的内容text / json / xml / html / script / jsonp(跨域) =>不影响服务器返回的结果,只是把服务器返回的结果进行二次处理
		 *    async:是否为异步操作,默认是TRUE,代表异步操作
		 *    cache:缓存处理,只对GET系列请求有作用,默认是TRUE不处理缓存,当我们设置FALSE后,JQ帮我们在URL的末尾设置一个随机数
		 *    contentType:设置传递给服务器内容的格式类型 默认是"application/x-www-form-urlencoded"
		 *        客户端传递给服务器信息的格式(类型一般都是字符串),常用的:
		 *        form-data表单数据:JSON格式 '{"name":"xxx","lx":1}'
		 *        x-www-form-urlencoded:name=xxx&lx=1
		 *        raw:纯文本格式
		 *    headers:设置请求头信息,他是一个对象
		 *    timeout:设置超时的时间
		 *    success:回调函数,当数据请求成功执行,方法中的参数就是从服务器获取的结果
		 *    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);
			}
		});
	</script>
</body>

</html>
7.倒计时抢购案例
<!DOCTYPE html>
<html>

<head>
	<meta charset="UTF-8">
	<title>倒计时</title>
</head>

<body>
	<div id="box"></div>
	<script>
		//new Date()获取客户端本地当前时间(不能拿它做重要依据,因为用户可以随意修改)
		/* 
		 * 倒计时抢购需要从服务器获取当前时间  AJAX
		 *    问题:时间差(从服务器把时间给客户端,到客户端获取到这个信息,中间经历的时间就是时间差,而时间差是不可避免的,我们应尽可能减少这个误差)
		 *    - 从响应头获取时间(AJAX异步)
		 *    - 基于HEAD请求(只获取响应头信息)
		 */
		let target = new Date('2021/02/28 21:27: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>
8.封装JQuery版的AJAX库
<!DOCTYPE html>
<html>

<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>
</head>

<body>
	<script src="js/jquery.min.js"></script>
	<script src="js/ajax.js"></script>
	<script>
		_ajax({
			url: 'json/data.json',
			method: 'GET',
			data: {
				name: 'zhufeng',
				lx: 'teacher'
			},
			dataType: 'json',
			cache: false,
			headers: {
				AAA: '珠峰培训',
				BBB: '10'
			},
			success: (result, status, xhr) => {
				console.log(result, status, xhr);
			}
		});
	</script>
</body>

</html>
/*
 * 支持的参数配置项
 *    url
 *    method:'GET'
 *    data:null
 *    dataType:'json'
 *    async:true
 *    cache:true
 *    success:null
 *    error:null
 *    headers:null
 *    timeout:null
 */
~ function () {
	function ajax(options) {
		return new init(options);
	}

	/* ==AJAX处理的核心== */
	let regGET = /^(GET|DELETE|HEAD|OPTIONS)$/i;
	let defaults = {
		url: '', //=>请求的API接口地址
		method: 'GET', //=>请求方式 GET/POST/DELETE/PUT/HEAD/OPTIONS
		data: null, //=>传递给服务器的信息:支持格式STRING和OBJECT,如果是OBJECT,我们需要把其处理为x-www-form-urlencoded格式;GET请求是把信息作为问号参数传递给服务器,POST请求是放到请求主体中传递给服务器;
		dataType: 'JSON', //=>把服务器返回结果处理成为对应的格式 JSON/TEXT/XML
		async: true, //=>是否异步请求
		cache: true, //=>只对GET请求有作用:设置为FALSE,在URL的末尾加随机数来清除缓存
		timeout: null, //=>超时时间
		headers: null, //=>设置请求头信息(请求头信息不能是中文,所以我们需要为其编码)
		success: null, //=>从服务器获取成功后执行 把获取的结果、状态信息、XHR传递给它
		error: null //=>获取失败后执行 把错误信息传递给它
	};

	function init(options = {}) {
		//=>参数初始化:把传递的配置项替换默认的配置项
		this.options = Object.assign(defaults, options);
		this.xhr = null;
		this.send();
	}

	ajax.prototype = {
		constructor: ajax,
		version: 1.0,
		//=>发送AJAX请求
		send() {
			let xhr = null,
				{
					url,
					method,
					async,
					data,
					cache,
					timeout,
					dataType,
					headers,
					success,
					error
				} = this.options;
			this.xhr = xhr = new XMLHttpRequest;

			//=>处理DATA:如果是GET请求把处理后的DATA放在URL末尾传递给服务器
			data = this.handleData();
			if (data !== null && regGET.test(method)) {
				url += `${this.checkASK(url)}${data}`;
				data = null;
			}

			//=>处理CACHE:如果是GET并且CACHE是FALSE需要清除缓存
			if (cache === false && regGET.test(method)) {
				url += `${this.checkASK(url)}_=${Math.random()}`;
			}

			xhr.open(method, url, async);

			//=>超时处理
			timeout !== null ? xhr.timeout = timeout : null;

			//=>设置请求头信息
			if (Object.prototype.toString.call(headers) === "[object Object]") {
				for (let key in headers) {
					if (!headers.hasOwnProperty(key)) break;
					xhr.setRequestHeader(key, encodeURIComponent(headers[key]));
				}
			}

			xhr.onreadystatechange = () => {
				let {
					status,
					statusText,
					readyState: state,
					responseText,
					responseXML
				} = xhr;
				if (/^(2|3)\d{2}$/.test(status)) {
					//=>成功
					if (state === 4) {
						switch (dataType.toUpperCase()) {
							case 'JSON':
								responseText = JSON.parse(responseText);
								break;
							case 'XML':
								responseText = responseXML;
								break;
						}
						success && success(responseText, statusText, xhr);
					}
					return;
				}
				//=>失败的
				typeof error === "function" ? error(statusText, xhr) : null;
			}
			xhr.send(data);
		},
		//=>关于DATA参数的处理
		handleData() {
			let {
				data
			} = this.options;
			if (data === null || typeof data === "string") return data;
			//=>只有DATA是一个对象,我们需要把它变为xxx=xxx&xxx=xxx这种格式字符串
			let str = ``;
			for (let key in data) {
				if (!data.hasOwnProperty(key)) break;
				str += `${key}=${data[key]}&`;
			}
			str = str.substring(0, str.length - 1);
			return str;
		},
		//=>检测URL中是否存在问号
		checkASK(url) {
			return url.indexOf('?') === -1 ? '?' : '&';
		}
	};
	init.prototype = ajax.prototype;

	window._ajax = ajax;
}();

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值