目录
- 1. 第一个AJAX代码
- 2. `readyState`状态码
- 3. AJAX的`onload`事件
- 4. 控制请求、接收响应
- 5. 发送GET、POST请求
- 6. 同步和异步
- 7. 处理JSON和XML
- 8. 兼容性——ActiveXObject
- 9. responseType和response属性
- 9. JS前端模板
- 10. 封装ajax代码
- 11. JQuery的ajax基本方法
- 12. JQuery中AJAX的全局事件处理
- 13. ★跨域(源)问题
- 13. XMLHttpRequest 2.0
浏览器发出请求的方式有:
- 地址栏输入地址
- 特定标签的
src
或者href
- 表单提交
- css中
@import
- AJAX(Asynchronous JavaScript and XML)
- …
var img = new Image(); // DOM的image标签对象
img.src = "1.jpg"; // 这句话运行后浏览器就会尝试去发送请求获取图片
AJAX就是浏览器提供的一套API,可以使用JavaScript调用,对服务器进行请求的发送和响应获取。
1. 第一个AJAX代码
类比与使用浏览器查看页面的步骤:
- 安装浏览器
- 打开浏览器,输入网址
- 等待响应
- 看结果
使用步骤:
- 创建发送请求的对象
- 打开和目标连接
- 发送情趣
- 注册处理函数
- 处理结果
方法/函数/属性 | 含义 |
---|---|
new XMLHttpRequest() |
创建 xhr 请求对象 |
xhr.open('GET', 'url' ) |
创建链接 |
xhr.send() |
发送请求 |
xhr.readyState |
获取状态 |
xhr.responseText |
获取请求内容 |
xhr.getAllResponseHeaders() |
获取所有响应头 |
xhr.getResponseHeader("string") |
根据key获取特定响应头内容 |
xhr.onreadystatechange |
状态时间变化的event |
var xhr = new XMLHttpRequest()
xhr.open('GET', './time.php')
xhr.send()
// 因为客户端永远不知道服务端何时才能返回我们需要的响应
// 所以 AJAX API 采用事件的机制(通知的感觉)
xhr.onreadystatechange = function () {
// 这个事件并不是只在响应时触发,XHR 状态改变就触发
if (this.readyState !== 4) return
// 获取响应的内容(响应体)
console.log(this.responseText)
}
JS是单线程的
发送请求的地址不能使用文件协议访问(file://)
2. readyState
状态码
onreadystatechange
事件会在xhr
对象状态变化时触发调用,所以说这个事件会多次触发。
状态码 | 时机 | 含义 |
---|---|---|
0 | UNSENT | 已经完成对象初始化 创建了xhr 对象之后,但还未调用open , |
1 | OEPNED | xhr 对象调用了open 方法之后,已建立了一个与服务端特定端口的连接 |
2 | HEADERS_RECIEVED | send 已经被调用,且已获取了响应报文的响应头和响应行 |
3 | LOADING | xhr 正在下载响应报文的响应体,responseText 已经可以获取部分响应体 |
4 | DONE | xhr 处理结束,整个响应报文已经完整下载下来了,可以直接使用responseText |
习惯使用
addEventListener
注册事件
var xhr = new XMLHttpRequest();
console.log(xhr.readyState);
// => 0
// 初始化 请求代理对象
xhr.open('GET', 'time.php');
console.log(xhr.readyState);
// => 1
// open 方法已经调用,建立一个与服务端特定端口的连接
xhr.send();
xhr.addEventListener('readystatechange', function () {
switch (this.readyState) {
case 2:
// => 2
// 已经接受到了响应报文的响应头
// 可以拿到头
// console.log(this.getAllResponseHeaders())
console.log(this.getResponseHeader('server'));
// 但是还没有拿到体
console.log(this.responseText);
break;
case 3:
// => 3
// 正在下载响应报文的响应体,有可能响应体为空,也有可能不完整
// 在这里处理响应体不保险(不可靠)
console.log(this.responseText);
break;
case 4:
// => 4
// 一切 OK (整个响应报文已经完整下载下来了)
console.log(this.responseText);
break;
}
})
3. AJAX的onload
事件
HTML提供的 XMLHttpRequest Version 2.0
onload
事件,就是专门用来处理当请求已经完成时候的处理:
var xhr = new XMLHttpRequest();
xhr.open("GET", "url");
xhr.send();
xhr.onload = function(){
console.log(this.readyState); // 永远是 4
console.log(this.responseText);
}
4. 控制请求、接收响应
方法/属性 | 含义 |
---|---|
xhr.setRequestHeader(key,value) |
设置请求头,一次设置一个 |
xhr.send(data) |
发送的时候设置请求体 |
xhr.status |
响应状态码 |
xhr.statusText |
响应状态描述文本 |
xhr.responseText |
获取请求内容 |
xhr.getAllResponseHeaders() |
获取所有响应头 |
xhr.getResponseHeader("string") |
根据key获取特定响应头内容 |
根据不同携带的内容,要设置不同的
Content-Type
。
Content-Type ‘application/x-www-form-urlencoded’, 携带的是Form Data
Content-Type 'text/plain;charset=UTF-8, 携带的是Request Payload
var xhr = new XMLHttpRequest()
xhr.open('POST', '/add.php') // 设置请求行
xhr.setRequestHeader('Foo', 'Bar') // 设置一个请求头
// 一旦你的请求体是 urlencoded 格式的内容,一定要设置请求头中 Content-Type 'application/x-www-form-urlencoded'
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded')
xhr.send('key1=value1&key2=value2') // 以 urlencoded 格式设置请求体
5. 发送GET、POST请求
- GET请求的参数直接写在url中
- POST请求的参数使用
send
传递,并且要记住设置Content-Tye
xhr1.open('GET', 'users.php?id=' + this.id)
// 设置Content-Type
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded')
// xhr.send('username=' + username + '&password=' + password)
xhr.send(`username=${username}&password=${password}`) // 发送POST请求内容
浏览器可以将id唯一的对象创建出来直接使用:
<input type="button" id="btn" />
<script>
btn.onclick = ...
</script>
6. 同步和异步
- AJAX的
open
方法还有第三个参数async
, 取值为true/false
,默认是true
: - 主要阻塞位置是
send
方法,在同步中,send结束就可以直接获取响应体;而异步中,需要使用事件处理 - 目前同步使用的方法已废弃*,不推荐使用
var xhrAsync = new XMLHttpRequest()
// open 方法的第三个参数是 async 可以传入一个布尔值,默认为 true
xhrAsync.open('GET', 'time.php', true)
console.time('async')
xhrAsync.send()
console.log(xhrAsync.responseText)
// console.log('end request')
console.timeEnd('async')
// 同步模式 ajax 操作会有楞等的情况
// 区别在于 send 方法会不会出现等待情况
// console.log('begin request')
var xhrSync = new XMLHttpRequest()
// open 方法的第三个参数是 async 可以传入一个布尔值,默认为 true
xhrSync.open('GET', 'time.php', false)
console.time('sync')
xhrSync.send()
console.log(xhrSync.responseText)
// console.log('end request')
console.timeEnd('sync')
同步阶段,注册onreadystatechange
的时机需要在send
之前:
var xhr = new XMLHttpRequest()
xhr.open('GET', 'time.php', true)
xhr.send()
// 一定要注意事件注册时间问题
xhr.onreadystatechange = function () {
console.log(this.readyState)
}
console.time(
字符串标识符
);
console.timeEnd(字符串标识符
); // 结束秒表,打印计时
7. 处理JSON和XML
7.1 处理JSON
方法 |
---|
JSON.parse() |
var jsonData = JSON.parse(data);
7.2 处理XML
方法 |
---|
xhr.responseXML 返回DOM |
但是需要服务端响应头中的 Content-Type 必须是 application/xml
// this.responseXML 专门用于获取服务端返回的 XML 数据,操作方式就是通过 DOM 的方式操作
console.log(this.responseXML.documentElement.children[0].innerHTML)
console.log(this.responseXML.documentElement.getElementsByTagName('name')[0])
8. 兼容性——ActiveXObject
XMLHttpRequest
在老版本IE(5/6)有兼容问题:
var xhr = window.XMLHttpRequest ?new XMLHttpRequest(): new ActiveXObject('Microsoft.XMLHTTP');
9. responseType和response属性
xhr.responseType
属性是客户端设置的,这样之后就可以通过xhr.response
来获取对应的对象:
// 我们通过代码告诉请求代理对象,服务端响应给我们的是 JSON
xhr.responseType = 'json';
xhr.onreadystatechange = function () {
if (this.readyState !== 4) return;
console.log(this.response);
// this.response 获取到的结果会根据 this.responseType 的变化而变化
// this.responseText 永远获取的是字符串形式的响应体
}
9. JS前端模板
有很多前端的js渲染模板,art-template就是一个国产的。
步骤:
- 选择一个模板引擎:
https://github.com/tj/consolidate.js#supported-template-engines
- 下载模板引擎JS文件
- 引入到页面中
- 准备一个模板
- 准备一个数据
- 通过模板引擎的JS提供的一个函数将模板和数据整合得到渲染结果HTML
- 将渲染结果的HTML 设置到 默认元素的 innerHTML 中
重点::
- 利用的是,在script标签中写模板代码,只要script标签的type不是
text/javascript
,这样不会被解释执行: script
标签在被使用做为模板的时候,type
属性建议写跟模板相关的内容,比如x-art-template
, 其中x
代表自定义
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>AJAX 动态表格渲染</title>
</head>
<body>
<table id="demo"></table>
<!--
script 标签的特点是
1. innerHTML 永远不会显示在界面上
2. 如果 type 不等于 text/javascript 的话,内部的内容不会作为 JavaScript 执行
-->
<script id="tmpl" type="text/x-art-template">
{{each comments}}
<!-- each 内部 $value 拿到的是当前被遍历的那个元素 -->
<tr>
<td>{{$value.author}}</td>
<td>{{$value.content}}</td>
<td>{{$value.created}}</td>
</tr>
{{/each}}
</script>
<script src="template-web.js"></script>
<script>
var xhr = new XMLHttpRequest()
xhr.open('GET', 'test.php')
xhr.send()
xhr.onreadystatechange = function () {
if (this.readyState !== 4) return
var res = JSON.parse(this.responseText)
// 模板所需数据
var context = { comments: res.data }
// 借助模板引擎的API 渲染数据
var html = template('tmpl', context)
console.log(html)
document.getElementById('demo').innerHTML = html
// 1. 选择一个模板引擎
// https://github.com/tj/consolidate.js#supported-template-engines
// 2. 下载模板引擎JS文件
// 3. 引入到页面中
// 4. 准备一个模板
// 5. 准备一个数据
// 6. 通过模板引擎的JS提供的一个函数将模板和数据整合得到渲染结果HTML
// 7. 将渲染结果的HTML 设置到 默认元素的 innerHTML 中
//
// 为什么不在JS变量中写模板?
// 1. 如果将模板写到JS中,维护不方便,不能换行,没有着色
// 为什么使用script标记
// 1. script不会显示在界面
}
</script>
</body>
</html>
10. 封装ajax代码
这种异步调用结束时的结果,没法像同步调用一样返回给调用者,只能通过回调函数
function ajax (method, url, params, done) {
method = method.toUpperCase()
var xhr = new XMLHttpRequest()
if (typeof params === 'object') {
var tempArr = []
for (var key in params) {
var value = params[key]
tempArr.push(key + '=' + value)
}
params = tempArr.join('&')
}
if (method === 'GET') {
url += '?' + params
}
xhr.open(method, url, false)
var data = null
if (method === 'POST') {
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded')
data = params
}
xhr.onreadystatechange = function () {
if (this.readyState !== 4) return
// 不应该在封装的函数中主观的处理响应结果
// console.log(this.responseText)
// 你说我太主观,那么你告诉我应该做什么
done(this.responseText)
}
xhr.send(data)
}
11. JQuery的ajax基本方法
$.ajax
方法是jQuery封装的ajax
, 底层也是 XMLHttpRequest
。 jQuery也提供了其他更加方便的ajax方法,它们的底层都是$.ajax
方法 |
---|
$.ajax |
$.get |
$.post |
$.getJSON |
a. 底层方法——$.ajax
// 关于参数
$.ajax({
url: 'json.php',
type: 'get',
// 设置的是请求参数
data: { id: 1, name: '张三' },
// 用于设置响应体的类型 注意 跟 data 参数没关系!!!
dataType: 'json',
success: function (res) {
// 一旦设置的 dataType 选项,就不再关心 服务端 响应的 Content-Type 了
// 客户端会主观认为服务端返回的就是 JSON 格式的字符串
console.log(res)
}
})
// 关于回调
$.ajax({
url: 'time.php',
type: 'get',
beforeSend: function (xhr) {
// 在所有发送请求的操作(open, send)之前执行
console.log('beforeSend', xhr)
},
success: function (res) {
// 隐藏 loading
// 只有请求成功(状态码为200)才会执行这个函数
console.log(res)
},
error: function (xhr) {
// 隐藏 loading
// 只有请求不正常(状态码不为200)才会执行
console.log('error', xhr)
},
complete: function (xhr) {
// 不管是成功还是失败都是完成,都会执行这个 complete 函数
console.log('complete', xhr)
}
})
b. 快捷方法—— $.get 、 $.post
、$.getJSON
$.get('json.php', { id: 1 }, function (res) {
console.log(res)
})
$.post('json.php', { id: 1 }, function (res) {
console.log(res)
})
$.getJSON('json.php', { id: 1 }, function (res) {
console.log(res)
})
c. 局部ajax加载—— $ele.load
ajax发送数据,然后将获取到的结果,直接替换元素的内容
$( "#result" ).load( "ajax/test.html #container" );
上面代码,会像 ajax/test.html
发送请求,然后将获取到的元素解析,拿到其中id="container"
的元素,将其替换成 原页面中#result
选择器选中元素的内容。
局部jQuery对象
$(function($){
// jquery 的入口可以传入一个 $, 代表就是jquery对象
// 这样以后就可以调用使用这个局部 jq对象,而不是全局的 jq对象了
});
12. JQuery中AJAX的全局事件处理
为所有ajax请求来注册事件,会让每次发送请求的特定时机都触发相同的handler。
Global events are never fired for cross-domain script or JSONP requests, regardless of the value of global
事件处理函数 |
---|
.ajaxComplete() |
.ajaxError() |
.ajaxSend() |
.ajaxStart() |
.ajaxStop() |
.ajaxSuccess() |
$( document ).ajaxComplete(function() {
$( ".log" ).text( "Triggered ajaxComplete handler." );
});
所有这些全局注册事件,都必须绑定在document
上面。
使用 NProgess来完成进度条
就是一个库,用于在浏览器最顶端显示进度条
<link rel="stylesheet" href="../nprogress.css">
<script src="../nprogress.js"></script>
<script>
$(document)
.ajaxStart(function () {
NProgress.start()
})
.ajaxStop(function () {
NProgress.done()
})
</script>
13. ★跨域(源)问题
- 同源策略:是浏览器的一种安全策略,所谓同源是指域名、协议、端口完全相同,只有同源的地址才能可以互相发送AJAX请求
- 不同源地址之间的请求成为跨域请求
- 而实际上请求能发送出去,响应也返回了,但是浏览器不允许获取这些响应结果,因为同源策略是浏览器的。
- 不同源地址之间需要相互发送ajax请求,需要服务端和客户端配合才可以。
除了ajax,还有其他可以发送请求的方式:
- img
- link
- script
- iframe
a. 使用别的方式发送跨域请求——img、link、script
1. img
var img = new Image()
img.src = 'http://locally.uieee.com/categories'
- 可以发送不同源地址之间的请求
- 无法拿到响应结果
2. link
var link = document.createElement('link')
link.rel = 'stylesheet'
link.href = 'http://locally.uieee.com/categories'
document.body.appendChild(link) // 要加入到文档中,才能发送出去
- 可以发送不同源地址之间的请求
- 无法拿到响应结果
3. script
var script = document.createElement('script')
script.src = 'http://localhost/time2.php'
document.body.appendChild(script) // 开始发起请求
- 可以发送不同源地址之间的请求
- 无法拿到响应结果
- 借助于能够作为 JS 执行
用js创建script标签发送请求,这是一个异步操作,无法获取到结果,更别说json的结果了。
解决办法:
让服务器端返回一段JavaScript代码,将数据传入一个事先约定好的函数中当参数,然后客户端定义这个函数,就可以处理了。
客户端:
var script = document.createElement('script')
script.src = 'http://localhost/time2.php'
document.body.appendChild(script) // 开始发起请求
// 相当于请求的回调
function foo (res) {
console.log(res)
}
服务器端:
- 返回类型为
application/javascript
- 返回数据为:
foo(data)
这样当客户端接收到服务器端响应后,就会解析其中的js代码,然后调用 foo
函数,由于foo
已经在客户端定义了,所以就能够接收到服务器给的参数
b. ★JSONP
JSONP就是 JSON with Padding, 是一种借助script
标签发送跨域请求的技巧
其原理就是在客户端借助script
标签请求服务端的一个动态网页,服务器端返回一段带有函数调用的JavaScript全局函数调用的脚本,将原本需要返回给客户端的数据传递进去。
以后绝大多数都是采用JSONP的手段完成不同源地址之间的跨域请求。
例子:
客户端:
<script src="http://api.xxxx.xxx/a.php?callback=foo"></script>
服务器端发送
foo(data)
总结:
- 跨域问题只是因为
XMLHttpRequest
无法发送不同源地址之间的请求,所以才导致使用script
标签来解决跨越办法,这种方式成为JSONP
- JSONP需要服务器端配合,要求服务器端按照客户端的要求,返回一段JS代码来调用客户端提供的函数
- 只能发送
GET
请求 - JSONP本质使用的是
script
标签,和AJAX没有任何关系 - JQuery中使用JSONP,就是用
$.ajax
,然后设置dataType
为jsonp
c. JQuery发送JSONP请求
$.ajax({
url: 'http://localhost/jsonp/server.php',
dataType: 'jsonp',
success: function (res) {
console.log(res)
}
})
d. 跨域资源共享CORS
Cross Origin Resource Share
跨域问题以前无法解决,而现在是可以解决的了,只要在服务器返回响应的时候设置一个头:
Access-Control-Allow-Origin: * # *代表允许所有IP地址的请求
这样就可以用AJAX跨域了
13. XMLHttpRequest 2.0
http://www.ruanyifeng.com/blog/2012/09/xmlhttprequest_level_2.html