AJAX简介:
ajax背景:
1.AJAX(Asynchronous JavaScript And Xml)异步的 JavaScript 和 XML:ajax是浏览器提供的一套API,最早出现在谷歌浏览器,是在浏览器端进行网络编程(发送请求、接收响应)的技术方案。它可以使我们通过JavaScript直接获取服务端最新的内容而不必重新加载页面,让web更接近桌面应用的体验。
2.涉及到ajax操作的界面‘不能’使用文件协议(文件的方式)访问,实际是可以访问的。
3.ajax是一套API,核心提供的类型:XMLHttpRequest,其使用步骤如下:
<script>
// 1.创建 XMLHttpRequest 对象,xhr就类似浏览器的作用(发送请求 接收响应)
var xhr = new XMLHttpRequest(); //xhr变量名可以为其它名,习惯使用xhr;此对象有兼容问题,解决方法如下:
// 解决ajax的IE5-6兼容问题:
var xhr = window.XMLHttpRequest ? new XMLHttpRequest() : new ActiveXOBject('Microsoft.XMLHTTP');
// 2.打开浏览器输入网址,可以是服务端任何文件,比如 .txt或.php
xhr.open('GET', 'http://day-03.io/6.Ajax练习/01.ajax的背景/time.php'); //第一个参数为请求方式,第二个参数为请求地址
// 3.发送一个请求,如果是post请求方式,send里面可以传请求体,后面详细讲:
xhr.send();
// 4.响应,因为客户端永远不知道服务端何时才能返回我们的需求响应,所以,ajax API 采用时间机制(类似通知的方式):
xhr.onreadystatechange = function() {
// 这里this.readyState有三个阶段:2,3,4;写在xhr.open()下面会有状态1的出现,如果需要捕获第一个状态的变化,需要注意代码执行顺序的问题,有0-4五个状态,四个阶段;
if (this.readyState !== 4) return;//只有状态4表示请求响应已经完成,此时可以对数据进行操作:
// 5.看结果
console.log(this.responseText); //responseText可以接收到响应体,response也可以接收的响应体但是有区别
};
script>
ajax中五种状态四种阶段代表什么:
ajax有五个状态,四种阶段,分别代表如下xhr.readyState的返回值:
<script>
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:已经接收到了响应报文的响应头,但是还没有拿到体,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;
};
});
script>
<script>
var xhr = new XMLHttpRequest(); //----安装一个浏览器
console.log(xhr.readyState); //--------------0:初始化请求代理
xhr.open('GET', 'time.php'); //打开一个浏览器,建立一个与服务端端口的连接
console.log(xhr.readyState); //--------------1:建立一个与服务端端口的连接
xhr.send(); //发送请求
console.log(xhr.readyState); //--------------1
// 用addEventListener()的方式注册事件:
xhr.addEventListener('readystatechange', function() {
// if (this.readyState!==4) return;
console.log(this.readyState); //-------------4,如果是注释掉上面的if条件,那么这里打印的值为:2,3,4,分别表示:
// 2:已经接收到响应体的响应头,还没有拿到响应体
// 3:正在下载响应体中
// 4:已经下载完一个完整的响应体
console.log(this.response); //服务端响应的东西,this.responseText也可以拿到响应体
// 在可以使用this的作用域建议使用this,因为这样可避免沿着作用域链一级一级的向下找,优化了性能。
});
script>
onload事件代替readystatechange事件:
onload事件实现readystatechange事件效果:
<script>
var xhr = new XMLHttpRequest();
xhr.open('GET', 'time.php');
xhr.send(null);
xhr.onload = function() {
//onload事件可以代替readystatechange事件,但是onload有兼容性问题,因此实际开发中依然不会使用onload
console.log(this.readyState);
console.log(this.responseText);
};
script>
ajax遵循http协议:
ajax遵循http协议进行发送请求,在ajax中以post方式进行请求时注意:设置请求头中content-type属性及值,send()中以urlencoded格式设置请求体,如:
<script>
var xhr = new XMLHttpRequest();
xhr.open('POST', 'add.php'); //以POST的方式请求一个地址
//xhr.setRequestHeader('keyA', 'valueA'); //setRequestHeader用来设置一个请求头内容
// 请求体是 urlencoded 格式的内容,一定要设置请求头中的Content-Type 为'application/x-www-form-urlencoded'才可以正常请求:
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
xhr.send('key1=value1&key2=value2'); // 以 urlencoded 格式设置请求体
script>
数据接口:
数据接口:能提供特定的能力,有输入有输出,返回数据的地址,如下面服务端代码的URL:users.php即为一个数据接口,详细如下:
服务端代码:
//这里使用假数据做测试,实际开发中可能会从数据库获取数据:
$data=array(
array(
'id'=>1,
'name'=>'法海',
'age'=>39
),
array(
'id'=>2,
'name'=>'如经',
'age'=>14
),
array(
'id'=>3,
'name'=>'道号',
'age'=>19
)
);
//当收到ajax发送的请求是做出相应的数据返回:
if(empty($_GET['id'])){
// 没有传递id返回所有数据,因为http协议中约定报文的内容是字符串,所以需要用json数据格式
$json = json_encode($data);//==>返回json字符串:[{"id":1,"name":"法海","age":39},{"id":2,"name":"如经","age":14},{"id":3,"name":"道号","age":19}]
echo $json;
}else{
foreach ($data as $item){
if($item['id']!=$_GET['id']) continue;
$json = json_encode($item);//==>[{"id":1,"name":"\u5e08\u5085","age":39}]
echo $json;
};
};
通过ajax请求数据接口的数据:
<script>
var listElement = document.getElementById('list');
var xhr = new XMLHttpRequest();
xhr.open('GET', 'users.php?id=' + this.id); //这里可以使用?传参请求到具体的值,若不传入参数,则拿回所有数据遍历;在一个readystatechange事件中可以继续new XMLHTTPRequest()请求数据
xhr.send();
xhr.onreadystatechange = function() {
if (this.readyState !== 4) return;
var data = JSON.parse(this.responseText); //将json字符串转换为json对象
//通过遍历的方式对数据data进行处理(渲染到页面):
for (var i = 0; i < data.length; i++) {
console.log(data[i].id + '--' + data[i].name + '---' + data[i].age); //打印拿到的所有数据
};
};
script>
responseText和response区别:
responseTexxt返回的是普通字符串的形式的数据,不能通过 xhr.responseType='数据类型’设置返回的数据类型;response可以通过 xhr.responseType='数据类型’设置返回的数据类型。
<script>
var xhr = new XMLHttpRequest();
xhr.open('GET', 'test.php');
xhr.send();
xhr.responseType = 'json'; //有兼容问题
xhr.onreadystatechange = function() {
if (this.readyState === 4) {
// console.log(this.responseText);
console.log(this.response);
};
};
script>
通过ajax发送post请求:
以post方式提交数据,需要注意:如果请求体是urlencoded格式,需要设置请求头的content-type,如:
以post方式通过ajax提交密码和用户名:
<script>
// 假设给按钮注册点击事件执行ajax请求提交数据:
getElement('btn').onclick = function() {
// 假设获取input中的值并给变量:
var username = getElement('username').value;
var password = getElement('password').value;
var xhr = new XMLHttpRequest(); //在js中开启一个浏览器
xhr.open('POST', 'data.php'); //打开浏览器并访问data.php
//如果请求体是 urlencoded 格式 必须设置请求头为:Content-Type','application/x-www-form-urlencoded'
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
// xhr.send('username=' + username + '&password=' + password),发送请求体,字符串拼接不好操作,推荐使用模板字符串:
xhr.send(`username=${
username}&password=${
password}`);
// 3. 根据服务端的反馈 作出界面提示
xhr.onreadystatechange = function() {
if (this.readyState !== 4) return;
console.log(this.responseText); //若服务端有数据返回,则接收(服务端各个处理的反馈)
};
};
script>
服务端对用户名和密码做校验并做反馈:
// 判断是否提交数据并做出反馈:
if(empty($_POST['username'])||empty($_POST['password'])){
exit('请提交用户名和密码');
};
// 接收客户端传过来的数据并给变量:
$username=$_POST['username'];
$password=$_POST['password'];
// 校验数据:通常使用数据库中的数据,这里使用假数据做测试:
if($username==='admin' && $password==='123'){
exit('登录成功!');
};
exit('用户名或者密码错误');
同步与异步模式:
同步模式:在相同的时间内只能做一件事情,ajax 操作会有楞等的情况,区别在于 send 方法会不会出现等待情况;
异步模式:在相同的时间内可以做不同的事情,比起同步模式,异步处理时间更短,但是时间差异不是很大,一般需要使用console.time()方法进行测试时间。
xhr.open()中的第三个参数async是控制是否开启异步模式,值为布尔值,默认是true则表示异步;
<script>
// 同步模式,加载时间长
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')
// 异步模式,加载时间短
var xhrSync = new XMLHttpRequest()
// open 方法的第三个参数是 async 可以传入一个布尔值,默认为 true
xhrSync.open('GET', 'time.php', true) //打开异步模式,默认是打开的
console.time('sync')
xhrSync.send()
console.log(xhrSync.responseText)
// console.log('end request')
console.timeEnd('sync')
script>
ajax请求XML数据:
XML数据格式类似存在html标签中的内容,但是标签需要大写,文件后缀名为xml;ajax请求服务端xml数据时,需要指明响应头中content-type为application/xml,如:
ajax请求代码:
<script>
// 一般服务端响应一个简单的数据就是一个字符串,如果想要返回一个复杂的数据,一般采用json的格式;但是有很多老项目采用xml的方式存数据,此时也是需要获取数据的,如:
// ajax请求XML格式的数据:
var xhr = new XMLHttpRequest();
xhr.open('GET', 'xml.php');
xhr.send();
xhr.onreadystatechange = function() {
if (this.readyState !== 4) return;
// this.responseXML 是专门用于获取服务端返回XML数据的,操作方式就是通过DOM(节点)的方式操作,但是需要服务端响应头中的 Content-Type 必须是 application/xml
// header('Content-Type: application/xml');---需要服务端响应头中的 Content-Type 必须是 application/xml
console.log(this.responseXML.documentElement.children[0].innerHTML)
console.log(this.responseXML.documentElement.getElementsByTagName('name')[0])
}
script>
服务端XML数据:
header('Content-Type: application/xml');//告知响应头中响应文档类型为xml
?>
xml version="1.1" encoding="utf-8"?>
<person>
<name>小明</name>
<age>14</age>
<gender>男</gender>
</person>
<!-- 上面不能有空行,否则报错 -->
封装ajax函数:
实际开发中使用ajax获取数据的地方很多,为了节省开发成本,一般是将ajax封装成一个函数,使用时调用即可,我封装了一个名为ajax的AJAX数据请求函数,其中参数解释为:
method:必须,请求方式,
url:必须,请求路径
askbody:可选参数,当不需要向服务端传入参数时,可以给null占位,但是不能不写
callback:必须,一个用来处理响应数据的回调函数,回调函数中的参数即为响应结果
<script>
function ajax(method, url, askbody, callback) {
method = method.toUpperCase(); //当调用者输入的请求方式为小写字母时转大写
var xhr = window.XMLHttpRequest ? new XMLHttpRequest() : new ActiveXOBject('Microsoft.XMLHTTP'); //解决兼容问题
//默认请求体是字符串形式的,当调用者输入的是对象形式时,需要转换为字符串形式:
if (typeof askbody === 'object') {
var arr = []; //定义一个空数组用来对askbody进行拆解再拼接为字符串
for (var key in askbody) {
var value = askbody[key]; //通过遍历的的方式将请求体对象中的键拿到,并使用[键]的方式将对应的值拿到给变量value
arr.push(key + '=' + value); //将键和值作为一组元素追加到数组中
};
askbody = arr.join('&'); //将数组每个元素之间用&符号分割后转换为字符串形式,并重新给askbody,join可以将数组转化为字符串,括号中加参数则表示给每个元素之间添加的字符。
};
//如果是以get方式请求:将url和askbody进行拼接:
if (method === 'GET') {
url += '?' + askbody;
};
xhr.open(method, url);
//需要以post请求时,定义一个变量data接收请求体;当为get方式请求时,因为下面send中传入null或者不传,因此这里需要将data变量提升到整个函数作用域并初始值为null:
var data = null;
if (method === 'POST') {
//当以post请求时,需要传入请求体,如下:
data = askbody; //将上面处理好的askbody赋值给data变量供下面send中使用
xhr.setRequestHeader("Content-type", "application/x-www-form-urlencoded"); //当为post请求时,必须设置请求头中的Content-Type的值为application/x-www-form-urlencoded,且此代码要放到open的后面,否则会报错
};
xhr.send(data);
xhr.addEventListener('readystatechange', function() {
if (this.readyState === 4) {
callback = callback || function() {
alert('请传入数据处理回调函数');
};
callback(this.responseText);
};
});
};
//以get方式调用ajax:
ajax('GET', 'time.php', null, function(result) {
console.log(result);
});
//以post方式调用ajax:
ajax('POST', 'add.php', {
key1: 'value1',
key2: 'value2'
}, function(result) {
console.log(result);
});
script>
artTemplate引擎模板:
模板引擎实际上就是一个 API,模板引擎有很多种,使用方式大同小异,目的为了可以更容易的将数据渲染到 HTML中,这里采用artTemplate:https://aui.github.io/art-template/ 介绍,其文件实际是一个js文件。
模板属性位置:
模板写在script标签中,模板写在js中不便于维护;script标签的特点:1.innerHTML永远不会显示在界面上 2.如果script标签type属性的值不为 text/JavaScript 时,内部的内容不会作为JavaScript执行;
使用步骤:
// 1.选择一个模板引擎,去百度搜索,并引入这个模板的js文件:
<script src='template-web.js'>script>
// 2.准备一个模板 : each遍历数据,$value拿到的是当前被遍历的那个数据,{
{}}中定义某个数据或模板语法,其他地方依然可以写HTML
<script id="tmpl" type="text/x-art-template">
{
{
each comments}}//comments为下面content中comments,注意名字一直:
<tr>
<td>{
{
$value.id}}</td>
<td>{
{
$value.author}}</td>
<td>{
{
$value.content}}</td>
</tr>
{
{
/each}}
script>
// 3.下面将通过ajax获取数据,并将数据提供给art-template模板,之后在渲染到dom中:
<script>
var xhr = new XMLHttpRequest();
xhr.open('GET', 'test.php');//test.php中返回数据
xhr.send();
xhr.onreadystatechange = function() {
if (this.readyState === 4) {
var res = JSON.parse(this.responseText);
//3-1:将获取到的数据给tmpl模板中的comments:
var content = {
comments: res.data
};
//3-2:使用art-template模板引擎的API:template()将模板和数据整合得到渲染结果并赋值给变量:
var html = template('tmpl', content);
//3-3:将要渲染的结果渲染到某dom元素中:(这里渲染到id名为box的div中)
document.getElementById('div').innerHTML = html;
};
};
script>
同源策略和跨域请求:
同源: 协议 域名 端口相同的URL地址
同源策略: 不同源地址之间, 默认是不能相互发送ajax请求,后面有解决方案
不同源地址之间如果要相互请求, 必须服务端和客户端配合才可以
同源或者不同源说的是两个地址之间的关系, 不同源地址之间请求我们称之为跨域请求
现代化的 Web 应用中肯定会有不同源的现象, 所以必然要解决这个问题, 从而实现跨域请求。
跨域请求:
<body>
<script>
// 请求一个不同源的地址实际上就是我们所说的跨域请求
// 校验目标:1 能发出去,2 能收回来
// 可以发送请求的标签:img link script iframe
// ## 1. img
// 可以发送不同源地址之间的请求
// 无法拿到响应结果
// var img = new Image()
// img.src = 'http://test.com/categories'
// ## 2. link
// 可以发送不同源地址之间的请求
// 无法拿到响应结果
// var link = document.createElement('link')
// link.rel = 'stylesheet'
// link.href = 'http://test.com/categories'
// document.body.appendChild(link)
// ## 3. script
// 可以发送不同源地址之间的请求
// 无法拿到响应结果,但是借助于JS能够执行
var script = document.createElement('script')
script.src = 'http://test/time.php'
document.body.appendChild(script) // 开始发起请求
// 相当于请求的回调,在服务端定义一个函数调用如:echo "foo({$json}})"
function foo(res) {
console.log(res)
}
script>
body>
JSONP:
通过 script 标签请求一个服务端的 PHP 文件,这个文件返回的结果是一段JS,php文件作用是调用我们事先定义好的一个函数,从而将服务端想要给客户端发过去的数据以函数参数的形式发送给客户端
<script src="http://test/data.php">script>
<script>
function myonload (data) {
console.log(data)
}
script>
服务端代码:
echo 'myonload({'time':12231234})';
?>
封装jsonp函数:
实际开发中可能多次会用到跨域请求,此时为了节约开发成本,封装jsonp函数,其参数解释:
url:必须,请求的路径;
param:可选参数,用于请求时传入参数;
callback:必须,对响应回来的数据进行处理的回调函数,回调函数中的参数即为服务端响应回来的数据;
<script>
function jsonp(url, param, callback) {
var functionName = 'JSONP_' + Date.parse(new Date()); //函数调用时定义一个不同的变量名
if (typeof param === 'object') {
//判断param参数是否是对象,若是的还将对象的形式转化为字符串形式
var arr = [];
for (var key in param) {
//通过遍历的方式将param中的对象的每一个项的键和值拼接
var value = param[key];
arr.push(key + '=' + value);
};
param = arr.join('&'); //将数组转化为字符串形式,并且数组每个元素之间使用&分割
};
var script = document.createElement('script'); //创建一个script标签
script.src = url + '?' + param + '&callback=' + functionName; //设置script标签的src属性值,以?传参的形式传入请求字符(包括函数名给服务端)
//例如URL: 'http://test/server.php?name=jack&callback=JSONP_80284020025
document.body.appendChild(script); //将script标签追加到body中,此时对地址URL发起请求
window[functionName] = function(data) {
//window.JSONP_80284020025=function(){};服务端对window.JSONP_80284020025函数进行调用且传入了服务端的数据$resultData实参
callback(data); //data为函数window.JSONP_80284020025的形参,同时为回调函数callback的实参
// (function(result) {//result为回调函数callback的形参
// console.logg(result)
// })(data);//data此时作为实参供回调函数处理
delete window[functionName]; //当处理完数据后释放内存,并删除script标签
document.body.removeChild(script);
};
};
// 调用:
jsonp('http://test/server.php', {
id: 123
}, function(result) {
console.log(result)
});
script>
服务端代码:
$data = time();
// 如果客户端采用的是 script对服务器发送的请求, 一定要返回一段 JavaScript,设置Content-Type为application/javascript
header('Content-Type: application/javascript');
$resultData = json_encode($data);
$callback = $_GET['callback'];//接收到函数名JSONP_80284020025
echo "typeof {
$callback} === 'function' && {
$callback}({
$resultData})";//如果JSONP_80284020025为函数类型,则调用JSONP_80284020025函数,并将请求的数据作为函数参数传给函数处理
提示:本文图片等素材来源于网络,若有侵权,请发邮件至邮箱:[email protected]联系笔者删除。
笔者:苦海