AJAX 基本步骤
2)连接地址,准备数据
3)发送请求
4)接收数据(正在接收,尚未完成)
5)接收数据完成
const xhr = new XMLHttpRequest()实例化后,我们就可以通过 xhr 来发起一个请求
// xhr 具有一个 open 方法,这个方法的作用类似于初始化,并不会发起真正的请求
// open 方法具有 5 个参数,但是常用的是前 3 个
// method: 请求方式 —— get / post
// url:请求的地址
// async:是否异步请求,默认为 true(异步)
xhr.open(method, url, async)
//发送信息至服务器时内容编码类型
ajax.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
// send 方法发送请求,并接受一个可选参数
// 当请求方式为 post 时,可以将请求体的参数传入
// 当请求方式为 get 时,可以不传或传入 null
// 不管是 get 还是 post,参数都需要通过 encodeURIComponent 编码后拼接
xhr.send(data)
//接受服务器响应数据
ajax.onreadystatechange = function () {
if (obj.readyState == 4 && (obj.status == 200 || obj.status == 304)) {
}
};
在通过send方法发送请求后,xhr 对象在收到响应数据时会自动填充到其对应的属性中,xhr 具有以下常用属性:
responseText: 请求返回的数据内容 responseXML: 如果响应内容是"text/xml""application/xml",这个属性将保存响应数据的 XML DOM文档 status: 响应的HTTP状态,如 200 304 404 等 statusText: HTTP状态说明 readyStatus: 请求/响应过程的当前活动阶段 timeout: 设置请求超时时间
上面写了那么多,我们并没有发现收到数据时的回调方法,那么该怎么在收到数据时进行处理呢?这个问题的重点需要放在readyStatus这个属性上
readyStatus的值会随着请求各阶段的变化而改变,其一共有 5 个值:
xhr.readyStatus==0 尚未调用 open 方法
xhr.readyStatus==1 已调用 open 但还未发送请求(未调用 send)
xhr.readyStatus==2 已发送请求(已调用 send)
xhr.readyStatus==3 已接收到请求返回的数据
xhr.readyStatus==4 请求已完成
当readyStatus的状态发生改变时,会触发 xhr 的事件onreadystatechange,于是我们就可以在这个方法中,对接收到的数据进行处理
xhr.onreadystatechange = () => {
if (xhr.readyStatus === 4) {
// HTTP 状态在 200-300 之间表示请求成功
// HTTP 状态为 304 表示请求内容未发生改变,可直接从缓存中读取
if (xhr.status >= 200 &&
xhr.status < 300 ||
xhr.status == 304) {
console.log('请求成功', xhr.responseText)
}
}
}
当网络不佳时,我们需要给请求设置一个超时时间
// 超时时间单位为毫秒
xhr.timeout = 1000
// 当请求超时时,会触发 ontimeout 方法
xhr.ontimeout = () => console.log('请求超时')
以上就是 XMLHttpRequest 对象的基础内容,它还有很多其他的属性和时间,如onerror onabort onload等等,同时 ajax 的跨域、兼容 IE 浏览器等问题我们也并没有提到。对这些感兴趣的小伙伴可以自己 Google 一下,或者等以后有机会,我们再一起来学习这些内容
下面奉上自己封装的一个极简 ajax 请求方法,在生产项目中不能使用,但是对于临时测试请求个本地文件什么的还是没什么问题的,该 ajax 方法通过 Promise 方式实现回调
function ajax (options) {
let url = options.url
const method = options.method.toLocaleLowerCase() || 'get'
const async = options.async != false // default is true
const data = options.data
const xhr = new XMLHttpRequest()
if (options.timeout && options.timeout > 0) {
xhr.timeout = options.timeout
}
return new Promise ( (resolve, reject) => {
xhr.ontimeout = () => reject && reject('请求超时')
xhr.onreadystatechange = () => {
if (xhr.readyState == 4) {
if (xhr.status >= 200 && xhr.status < 300 || xhr.status == 304) {
resolve && resolve(xhr.responseText)
} else {
reject && reject()
}
}
}
xhr.onerror = err => reject && reject(err)
let paramArr = []
let encodeData
if (data instanceof Object) {
for (let key in data) {
// 参数拼接需要通过 encodeURIComponent 进行编码
paramArr.push( encodeURIComponent(key) + '=' + encodeURIComponent(data[key]) )
}
encodeData = paramArr.join('&')
}
if (method === 'get') {
// 检测 url 中是否已存在 ? 及其位置
const index = url.indexOf('?')
if (index === -1) url += '?'
else if (index !== url.length -1) url += '&'
// 拼接 url
url += encodeData
}
xhr.open(method, url, async)
if (method === 'get') xhr.send(null)
else {
// post 方式需要设置请求头
xhr.setRequestHeader('Content-Type','application/x-www-form-urlencoded;charset=UTF-8')
xhr.send(encodeData)
}
} )
}
使用方式
ajax({
url: 'your request url',
method: 'get',
async: true,
timeout: 1000,
data: {
test: 1,
aaa: 2
}
}).then(
res => console.log('请求成功: ' + res),
err => console.log('请求失败: ' + err)
)AJAX 缓存处理
Ajax能提高页面载入速度的主要原因是通过Ajax减少了重复数据的载入,也即在载入数据的同时将数据缓存到内存中,一旦数据被加载,只要没有刷新页面,这些数据就会一直被缓存在内存中,当提交的URL与历史的URL一致时,就不需要提交给服务器,也即不需要从服务器获取数据,虽然降低了服务器的负载,提高了用户体验,但不能获取最新的数据。为了保证读取的信息都是最新的,需要禁止其缓存功能。
解决方案有如下几种:
① 在Ajax发送请求前加上anyAjaxObj.setRequestHeader("If-Modified-Since", "0")。
② 在Ajax发送请求前加上anyAjaxObj.setRequestHeader("Cache-Control", "no-cache")。
③ 在URL后加上一个随机数:"fresh=" + Math.random();。
④ 在URL后加上时间搓:"nowtime=" + new Date().getTime();。
⑤ 如果是使用jQuery,则用$.ajaxSetup({cache:false})。这样页面的所有Ajax都会执行该语句而不需要保存缓存记录。
AJAX 的局限性?
AJAX 不支持浏览器 back 按钮。
安全问题 AJAX 暴露了与服务器交互的细节。
对搜索引擎的支持比较弱。不会执行你的 JS 脚本,只会操作你的网页源代码;
跨域请求有一定限制。解决方式:jsonp;
跨域
1:jsonp 只能解决get跨域(问的最多)
-
原理:动态创建一个script标签。利用script标签的src属性不受同源策略限制。因为所有的src属性和href属性都不受同源策略限制。可以请求第三方服务器数据内容。
-
步骤:
- 去创建一个script标签
- script的src属性设置接口地址
- 接口参数,必须要带一个自定义函数名 要不然后台无法返回数据。
- 通过定义函数名去接收后台返回数据
//去创建一个script标签
var script = document.createElement("script");
//script的src属性设置接口地址 并带一个callback回调函数名称
script.src = "http://127.0.0.1:8888/index.php?callback=jsonpCallback";
//插入到页面
document.head.appendChild(script);
//通过定义函数名去接收后台返回数据
function jsonpCallback(data){
//注意 jsonp返回的数据是json对象可以直接使用
//ajax 取得数据是json字符串需要转换成json对象才可以使用。
}
复制代码2:CORS:跨域资源共享
-
原理:服务器设置Access-Control-Allow-OriginHTTP响应头之后,浏览器将会允许跨域请求
-
限制:浏览器需要支持HTML5,可以支持POST,PUT等方法兼容ie9以上
需要后台设置
Access-Control-Allow-Origin: * //允许所有域名访问,或者
Access-Control-Allow-Origin: http://a.com //只允许所有域名访问
复制代码3:设置 document.domain
-
原理:相同主域名不同子域名下的页面,可以设置document.domain让它们同域
-
限制:同域document提供的是页面间的互操作,需要载入iframe页面
// URL http://a.com/foo
var ifr = document.createElement('iframe');
ifr.src = 'http://b.a.com/bar';
ifr.onload = function(){
var ifrdoc = ifr.contentDocument || ifr.contentWindow.document;
ifrdoc.getElementsById("foo").innerHTML);
};
ifr.style.display = 'none';
document.body.appendChild(ifr);
复制代码4:用Apache做转发(逆向代理),让跨域变成同域
http常见状态码
一: 2开头状态码
2xx (成功)表示成功处理了请求的状态代码
200 (成功) 服务器已成功处理了请求。 通常。
复制代码二: 3开头状态码
3xx (重定向) 表示要完成请求,需要进一步操作。 通常,这些状态代码用来重定向。
304 (未修改) 自从上次请求后,请求的网页未修改过。 服务器返回此响应时,不会返回网页内容。
复制代码三: 4开头状态码
4xx(请求错误) 这些状态代码表示请求可能出错,妨碍了服务器的处理
1:400 (错误请求) 服务器不理解请求的语法。
2:403 (禁止) 服务器拒绝请求。
3:404 (未找到) 服务器找不到请求的网页。
复制代码四: 5开头状态码
5xx(服务器错误)这些状态代码表示服务器在尝试处理请求时发生内部错误。 这些错误可能是服务器本身的错误,而不是请求出错
500 (服务器内部错误) 服务器遇到错误,无法完成请求。
501 (尚未实施) 服务器不具备完成请求的功能。 例如,服务器无法识别请求方法时可能会返回此代码。
502 (错误网关) 服务器作为网关或代理,从上游服务器收到无效响应。
503 (服务不可用) 服务器目前无法使用(由于超载或停机维护)。 通常,这只是暂时状态。
504 (网关超时) 服务器作为网关或代理,但是没有及时从上游服务器收到请求。
505 (HTTP 版本不受支持) 服务器不支持请求中所用的 HTTP 协议版本。