认识AJAX
扫盲1:
AJAX: Asynchronous Javascript And XML
html: 超文本标记语言,W3C指定了很多具有语义化的标记标签,用以搭建页面结构(目前多用V4/V5版本)
xhtml: 更加严谨的html
dhtml: 页面中的数据是动态绑定的
xml: 可扩展的标记标签语言,它里面使用的标签都是自己定义的,用来存储数据,结构清晰,以前用的比较多,不过其二次解析的过程相对JSON比较麻烦,目前前端数据多用JSON
唐玄奘
18
wxml: 微信小程序的页面就是.wxml,小程序中使用的标签都是小程序自己定义的(微信XML)
扫盲2:
这里的异步不能理解为JS中的同步异步编程,这里面的异步指的是‘局部刷新’,如果用六个字概述AJAX的作用,那就是:实现局部刷新
在web1.0和web 2.0时代=>整体刷新
那时前端页面的功能和数据绑定大部分都是后台开发者使用后台语言来完成的,浏览器只有一个作用,就是把后台实现好的功能的页面呈现即可,所有操作的都是后台服务器完成,这样若前端页面需要改变,必须后台把最新的内容重新返回,前端页面需要重新刷新
作用:
- 如果客户端获取的是资源文件,浏览器会自动帮我们向服务器端发送请求,并接收服务器返回的内容进行渲染:
- 在地址栏输入网址
- 利用link
- 利用script
- 利用iframe
- ...
- 但如果我们需要请求的是数据,就需要AJAX等技术发送请求(AJAX是JS中的一个核心知识点)
AJAX获取数据四部曲:
//创建ajax对象xhr
var xhr = new XMLHttpRequest();//ie6及以下不兼容,用new ActiveXObject
var data = null;
//打开一个url,配置请求的基本参数信息
xhr.open('get','temp.xml?name=zf',true);
//[请求方式]:get post put delete head
//[请求的url地址]
//[同步或者异步]:默认是true(异步),false(同步)
//设置请求的用户名和密码:[userName],[userPass]提供安全权限的验证(一般不用)
xhr.setRequestHeader('key','value');//
//监听ajax状态改变,获取服务器返回数据
xhr.onreadystatechange = function () {
if(xhr.readyState === 4 && xhr.status === 200){
//xhr.readyState ajax状态码 0 1 2 3 4
//xhr.status 服务器返回的http网络状态码200 301 302 304 400 401 404 500 503...
data = JSON.PARSE(xhr.responseText) 服务器端响应主体内容,获取到的是字符串
//xhr.responseXML 服务器端响应主体内容,获取到的是XML格式数据
//获取响应头的两种方式
//xhr.getResponseHeader('key')
//xhr.getAllResponseHeaders()
}
};
xhr.send(null);
//发送请求,括号内的传递的内容是请求主体
AJAX知识分解
1、创建AJAX对象
var xhr = new XMLHttpRequest();//ie6及以下不兼容,用new ActiveXObject
2、打来一个请求的url地址
例:xhr.open('get','temp.xml?name=zf',true);
xhr.open([method],[request url],[true/false],[username],[userpass]);
//[method]:get post put delete head
//[request url]
//[true/false]:默认是true(异步),false(同步),项目中为防止出现请求阻塞的问题,大多采用异步
//[userName],[userPass]:设置请求的用户名和密码,提供安全权限的验证(一般不用)
- method:请求方式
get:从服务器上获取数据(给的少,拿得多,最常用的方式)
post:向服务器推送数据(给得多,拿得少)
put:给服务器增加资源文件(上传图片等)
delete:从服务器删除文件
head:只获取服务器响应头信息
注意:无论那种请求方式,都可以向服务器发送和接收数据,且从本质意义上来讲他们没有任何区别,只是约定俗成的规则,在不同的情况下使用不同的请求方式
get与post的区别:
核心:get通过问号传参的方式给服务器传递内容,而post通过请求主体给服务器传递内容
xhr.open('get','temp.json?name=wang&age=18')//get方式,问号传参传递
xhr.send('{"name":"wang","age":18}'//post方式,请求主体传递
一般get、delete、head等用问号传参的方式给服务器传递内容,post、put等用请求主体的方式给服务器传递内容
请求方式get系列与post系列之间存在的问题:
大小问题:get传给服务器内容有大小限制,post理论上没有限制:
因为不同浏览器对url长度存在限制,chrome:8k,firefox:7k,ie:2k,超出限制部分会被浏览器截取,请求主体大小实际上为了保证传输的速度,会限制传递内容大小。缓存问题:因为get使用问号传参的方式,如果重复向同一个地址发送请求,浏览器会默认做缓存(这个缓存不可控),所以我们一般项目中使用get请求时要把缓存清除:
xhr.open('get','temp.json?_=' + Math.random())
//这里的问号传参中属性名使用的是下划线_,是因为除了传随机数外,我们可能还会传递其他的信息,这样的话随机数的属性名可能会与其他信息的属性名冲突,所以用_做属性名防止冲突
- 安全问题:get请求相对于post来说不安全
有一种黑客技术叫url劫持,被劫持后,问号后面的参数值会被获取或修改,导致不安全
面试题:在你以前的项目中服务器返回的数据一般是什么格式的
- 返回的是字符串格式的
- 普通的字符串
- JSON格式字符串(最常用的)
- 二进制或者文件流编码格式的字符串(一般请求的是图片,服务器返回的都是二进制编码字符串)
- 返回的是XML格式的
3、监听状态改变
-
xhr.readyState:AJAX状态码
- 0 :UNSENT: 未发送,刚开始创建完成AJAX对象,默认的状态就是0
- 1 :OPENED: 已打开,执行了xhr.open之后状态变为1
- 2 :HEADERS_RECEIVED: 响应头信息已接收
- 3 :LOADING: 服务器正在处理,响应主体内容正在加载
- 4 :DONE: 响应主体内容已成功返回
-
xhr.status:服务器返回的HTTP状态码
- 200 :响应主体成功返回
- 301 :永久重定向(永久转移) 域名更换的时候会做301永久重定向,如京东的360buy
- 302 :临时重定向(临时转移) ->307(临时重定向) 服务器负载均衡:比如一台服务器最高并发数为1000,当第1001个人访问时,当前服务器不能进行有效处理了,此时需要把此客户端的请求临时转移到另外一台服务器上,这叫做负载均衡
- 304 :缓存数据,在真实项目中,产品一旦上线,资源图片、js、css、等内容是不轻易改变的,此时我们最好做一下304缓存,在第一次访问服务器时,把加载的资源文件进行缓存,第二次再加载时直接读取缓存中的数据,减少服务器压力。浏览器强制无缓存刷新快捷键:ctrl+f5
- 400 :客户端给服务器端请求参数错误
- 401 :无权限访问
- 404 :访问地址不存在
- 500 :未知服务器错误
- 503 :服务器超负荷,未做负载均衡
面试题:你之前的项目中做过倒计时之类的东西吗?从服务器读取时间你是怎么解决时间差的?
由于服务器返回给客户端的时间信息在响应头信息的Date属性中,所以当我们只需要获取服务器的时间信息时,可以用head的传递方式,且在监听xhr状态时,在readyState状态码为2的时候,就已经获取到响应头信息了,此时就可以获取到服务器的Date数据了,就没必要在状态码为4的时候去获取信息了,这样请求的效率更高,代码如下:
xhr.onreadystatechange = function () {
if (xhr.status !== 200) return;//if后没有大括号则只会对if判断后的第一条语句生效;
if (xhr.readyState === 2) {//->我们只需要响应头信息返回就可以获取到服务器的时间,如果等到4的时候,虽然也可以获取到,但是间隔的时间更长了,导致时间差也会变大(真实时间和服务器获取的时间差值)
var time = xhr.getResponseHeader('Date');//->获取到的时间是格林尼治时间(GMT),我们还需要把这个时间变为北京时间(GMT+0800)
time = new Date(time);//转换为本地客户端时区的时间
console.log(time);
}
};
4、发送ajax请求给服务器
xhr.send(null);
//get系列请求传递一般是null,post系列请求把请求主体放在send参数里
AJAX一次任务的开始和结束标志:
- 开始:xhr.send(…)
- 结束:xhr.readyState===4
AJAX方法封装(仿JQ,基础版)
jQuery中的ajax方法
下面是jq中的ajax方法调用,jq中的ajax有30多个参数,一下列举了部分常用参数
$.ajax({
url: 'temp.json',
method: 'get',//->type:'get' 和这个属性是一样的功能的,定义请求方式
dataType: 'json',//->预设服务器返回的数据内容的格式json(默认)、text、xml...
data: null,//->设置请求主体的内容,如果是get请求,jQ会把这些内容放到请求地址的末尾,通过问号传参的方式传递给服务器,post请求才是放在请求主体中
cache: true,//->是否保留get请求的缓存,true是保留get缓存,设置成为false是清除缓存(在URL末尾加随机数),此参数对于post请求无效
async: true,//->设置同步异步,默认是TRUE代表异步
//timeout:3000,//->设置请求超时的时间,如果超过3000ms,当前请求自动中断(一般不用)
success: function (result) {
//->当数据请求成功后执行的回调函数,result就是从服务器获取的结果
console.log(result);
},
error: function (msg) {
//->当数据请求失败指定的回调函数,msg就是失败的原因
}
});
以下是仿jQ的ajax基础版方法源码(注释版)
(function () {
//->验证当前的URL中是否包含问号,包含返回&,否则返回?
function check(url) {
return url.indexOf('?') > -1 ? '&' : '?';
}
//->把对象转换为字符串
function formatData(obj) {
var str = '';
for (var key in obj) {
if (obj.hasOwnProperty(key)) {
str += key + '=' + obj[key] + '&';
}
}
str = str.substring(0, str.length - 1);
return str;
}
function ajax(options) {
//1、设置参数的默认值
var _default = {//设置参数默认值,default是关键字 所以要加下划线
url: null,
method: 'get',
data: null,
dataType: 'json',//->text、xml...
async: true,
cache: true,
success: null,
error: null
};
//2、使用传递进来的参数配置值把默认值进行替换
for (var key in options) {
if (options.hasOwnProperty(key)) {
//for in循环可以遍历到__proto__公有属性中自己拓展的方法,所以先判断一下是不是私有属性
if (key === 'type') {
_default['method'] = options['type'];
continue;
}
_default[key] = options[key];
}
}
//3、发送AJAX请求
var xhr = new XMLHttpRequest;
var regGET = /^(get|delete|head)$/i;
//->3.3:处理DATA
//对象和字符串的区分,如果是对象需要转换为字符串:xxx=xxx&xxx=xxx...
if (_default.data !== null) {
if (typeof _default.data === 'object') {
_default.data = formatData(_default.data);
}
//GET和POST的区分,GET是把内容放在问号后面
if (regGET.test(_default.method)) {
char = check(_default.url);
_default.url += char + _default.data;
_default.data = null;//->在发送的时候GET请求的请求主体是null
}
}
//->3.2:处理CACHE,如果当前的请求是GET系列的,并且CACHE等于FALSE,我们清除缓存
if (regGET.test(_default.method) && _default.cache === false) {
var char = check(_default.url);
_default.url += char + '_=' + Math.random();
}
xhr.open(_default.method, _default.url, _default.async);
xhr.onreadystatechange = function () {
if (/^(2|3)\d{2}$/.test(xhr.status)) {
if (xhr.readyState === 4) {
//->3.1:我们从服务器端获取的数据一般都是字符串格式的,但是我们传递进来的参数值中的dataType预设了最后数据结果的类型,所以我们还需要自己进行数据的二次加工,把从服务器端获取的字符串转换为预设的结果
var result = xhr.responseText;
switch (_default.dataType.toUpperCase()) {
case 'JSON':
result = 'JSON' in window ? JSON.parse(result) : eval('(' + result + ')');
break;
case 'XML':
result = xhr.responseXML;
break;
}
_default.success && _default.success.call(xhr, result);
}
//->问题:JQ中的dataType是否影响了服务器的返回结果? 不影响,服务器端会根据产品需求给我们返回字符串或者XML数据,而JQ会根据我们设置的dataType值,把返回的结果二次解析成需要的类型
return;
}
//->失败:执行ERROR的回调函数,让函数中的THIS指向当前实例XHR,并且把错误信息传递给回调函数
//typeof _default.error === 'function' ? _default.error.call(xhr, xhr.responseText) : null;
_default.error && _default.error.call(xhr, xhr.responseText);//->这种写法和上面的相同,都是在判断只有传递了回调函数的情况下才会执行(项目中一般都这么写)
};
xhr.send(_default.data);
}
window.ajax = ajax;
}())();
以下是仿jQ的ajax基础版方法源码(无注释版)
~function () {
function check(url) {
return url.indexOf('?') > -1 ? '&' : '?';
}
function formatData(obj) {
var str = '';
for (var key in obj) {
if (obj.hasOwnProperty(key)) {
str += key + '=' + obj[key] + '&';
}
}
str = str.substring(0, str.length - 1);
return str;
}
function ajax(options) {
var _default = {
url: null,
method: 'get',
data: null,
dataType: 'json',
async: true,
cache: true,
success: null,
error: null
};
for (var key in options) {
if (options.hasOwnProperty(key)) {
if (key === 'type') {
_default['method'] = options['type'];
continue;
}
_default[key] = options[key];
}
}
var xhr = new XMLHttpRequest;
var regGET = /^(get|delete|head)$/i;
if (_default.data !== null) {
if (typeof _default.data === 'object') {
_default.data = formatData(_default.data);
}
if (regGET.test(_default.method)) {
char = check(_default.url);
_default.url += char + _default.data;
_default.data = null;
}
}
if (regGET.test(_default.method) && _default.cache === false) {
var char = check(_default.url);
_default.url += char + '_=' + Math.random();
}
xhr.open(_default.method, _default.url, _default.async);
xhr.onreadystatechange = function () {
if (/^(2|3)\d{2}$/.test(xhr.status)) {
if (xhr.readyState === 4) {
var result = xhr.responseText;
switch (_default.dataType.toUpperCase()) {
case 'JSON':
result = 'JSON' in window ? JSON.parse(result) : eval('(' + result + ')');
break;
case 'XML':
result = xhr.responseXML;
break;
}
_default.success && _default.success.call(xhr, result);
}
return;
}
_default.error && _default.error.call(xhr, xhr.responseText);
};
xhr.send(_default.data);
}
window.ajax = ajax;
}();