Ajax,Asynchronus JavaScript and XML,字母意思:异步的 JavaScript 和 XML,是指一种建立交互式网页应用的网页开发技术。
用于异步地去获取XML做为数据交换的格式,固然,如今的 ajax 并不只仅局限于XML做为数据交换格式,还能够像纯文本、XML、HTML、JSON 等格式都可。javascript
Ajax 的 A 就是 asynchronous 的简写,表示异步。php
同步和异步:css
同步,按照代码书写的顺序,一个任务一个任务的来执行。 异步,并非按照代码书写的顺序,一般会结合回调和事件来执行相应代码。 在同步中,若是有一个任务耗时较长,整个的后面任务都须要等待。 在异步中,能够将耗时较长先放起来,执行其余的,其余的执行完毕,回头再执行这个。 同步:提交请求->等待服务器处理->处理完毕返回 阻塞模式。 异步:请求经过事件触发->服务器处理->处理完毕。非阻塞模式。
首先,咱们都了解软件的两种架构形态,C/S(Client/Server)和B/S(Browser/Server)。它们的区别以下:html
C/S,在客户端安装了客户端软件以后,整个程序的运行,分担到客户端和服务器端。用户在操做的时候,体验更好,响应速度很是快。java
B/S,所务的服务都放在服务器端,经过浏览器使用服务器,用户在操做的时候,体验不太好,响应速度特别慢。node
B/S的好处,就是不须要安装客户端,软件维护和更新比较方便,用户体验很差。ajax
C/S的好处,用户体验够好,响应及时,可是软件的维护和更新比较麻烦。chrome
随着互联网的发展,愈来愈多的C/S应用慢慢 转成的B/S,这也是将来的趋势。可是矛盾很突出,B/S的响应速度慢。须要B/S模式具有C/S模式的快速响应特色。
Ajax的出现就是为了解决这个问题-----异步刷新。
在异步刷新机制,整个页面不用跳转,只须要更新须要变化的地方。数据库
宗旨:提高页面的访问速度,提升用户体验。express
2005年2月,Adaptive Path公司的Jesse James Garrett最先提出这个概念。它出如今Garrett的文章“Ajax: A New Approach to Web Applications”中。这篇文章描述了混合使用XHTML、CSS、JavaScript、DOM、XMLHttpRequest进行Web开发将会成为一种新的趋势。
同年,Google在三大产品中使用了Ajax技术:
实际上,Ajax发展经历了三个时代:
2005年,没有 Ajax 的概念,可是异步刷新的须要仍是很是多。经常使用的方式就是 iframe。
第一,iframe有一个src属性,经过设置src属性,发出请求,发出请求的时候不会跳转;
第二,iframe其实就是指向单独的页面,能够在其中进行任意的操做,能够在iframe中书写js代码,能够去操做父页面。
优势,实现起来特别简单,兼容性好。
缺点,功能比较弱,在服务端须要写大量的代码。
简单示例:
前台代码:
<body> <h2>用户注册</h2> <form action="/reg" method="post"> <p> <label for="username">用户名:</label> <input type="text" id="username" name="username"> <span id="msg"></span> </p> <p> <label for="password">密 码:</label> <input type="password" id="password" name="password"> </p> <p><input type="submit" value="注册"></p> </form> <iframe src="" frameborder="0" width="0" height="0" id="iframe1"></iframe> <script> var iframe1 = document.getElementById('iframe1'); var username = document.getElementById('username'); username.addEventListener('blur',function(){ iframe1.src = "/check?username=" + this.value; }); </script> </body>
服务器端代码:
const express = require('express'); const bodyParser = require('body-parser'); const path = require('path'); const app = express(); app.get('/',(req,res)=>{ res.sendFile(path.join(__dirname,'login.html')); }); app.post('/reg',(req,res)=>{ let username = req.body.username; let password = req.body.password; // 检测用户名是否可用,典型的作法须要去链接数据库,取出全部用户名,进行查询比较 // TODO // 这种方式,能够知足需求,可是用户体验极差 // 每次都是填写完全部的表单内容,才能作一些检测 // 整个页面已经发生刷新跳转 }); // 模仿数据库 let users = ['admin','test']; app.get('/check',(req,res)=>{ let username = req.query.username; if (inArray(username,users)) { res.send("<script>window.parent.document.getElementById('msg').innerHTML='对不起,用户名已被占用,请从新注册!'</script>"); } else { res.send("<script>window.parent.document.getElementById('msg').innerHTML='恭喜,用户名可用!'</script>"); } }); app.listen(3000,()=>{ console.log('server is listening in port 3000...'); }); // 简单封装一个方法,用于检测一个值是否在数组中存在 function inArray(str,arr){ for(let i=0; i<arr.length; i++){ if(str == arr[i]){ return true; break; } } return false; }
var xhr = new XMLHttpRequest();
状态:readyState:
status:
responseText:
状态:readyState:
status:
responseText:
open()
方法,开启一个请求,但没有向服务器端发起请求xhr.open(method, url [,async = true]);
状态:readyState: 1
status:
responseText:
send()
方法,正式向服务器端发起请求get
xhr.send([data=null]);
post
xhr.setRequestHeader(header,value);
header:content-type
表单value:application/x-www-form-urlencoded
文件上传value:multipart/form-data
状态:readyState:2
status:
responseText:
状态:readyState:3
status:
responseText:
当浏览器端结束请求的时候
xhr.onreadystatechange = function(callback){ if(xhr.readyState == 4){ if( (xhr.status >= 200 && xhr.status < 300) || xhr.status==304 ){ callback(xhr.responseText); } else { alert('request was unsuccessful:' + xhr.status); } } }
状态:readyState:4
status:200
responseText:<!DOCTYPE html>响应返回值
onreadystatechange* 指定当readyState属性改变时的事件处理句柄。只写
readyState 返回当前请求的状态,只读.
responseBody 将回应信息正文以unsigned byte数组形式返回.只读 responseStream 以Ado Stream对象的形式返回响应信息。只读 responseText 将响应信息做为字符串返回.只读 responseXML 将响应信息格式化为Xml Document对象并返回,只读 status 返回当前请求的http状态码.只读 statusText 返回当前请求的响应行状态,只读
abort 取消当前请求
getAllResponseHeaders 获取响应的全部http头
getResponseHeader 从响应信息中获取指定的http头
open 建立一个新的http请求,并指定此请求的方法、URL以及验证信息(用户名/密码) send 发送请求到http服务器并接收回应 setRequestHeader 单独指定请求的某个http头
Microsoft最先把XMLHttpRequest对象引入到IE5中,且在IE5和IE6中它只是一个ActiveX对象。IE7以前的版本不支持非标准的XMLHttpRequest()构造函数,兼容以下:
function createXHR(){ var xhr = null; try{ xhr = new XMLHttpRequest(); }catch(e1){ try{ xhr = new ActiveXObject("Microsoft.XMLHTTP"); }catch(e2){ try{ xhr = new ActiveXObject("Msxml2.XMLHTTP"); }catch(e3){ throw new Error("XMLHttpRequest is not supported"); } } } return xhr; }
两种请求方式,有所不一样。
get 请求
&
连接?
跟上xhr.send([data=null])
post请求
form
表单的数据给请求出来以xml形式传递给服务器send
方法,其中数据以键值对的形式来传递xhr.setRequestHeader("content-type","application/x-www-form-urlencoded");
xhr.send(Formdata);
post请求注意事项
get请求中,若是参数是对象,那么咱们须要对它进行转换,编码以后,不须要解码,浏览器会自动解码;
var ulr = "example.json?" + serialize(formdata); xhr.open('get', url, true); xhr.send(null);
post请求中,有特殊符号(=和&)和中文须要编码,encodeURIComponent
方法
xhr.open('post', 'example.json', true); xhr.setRequestHeader('content-type', 'application/x-www-form-urlencoded'); xhr.send(serialize(formdata));
序列化方法:
function serialize(data){ if(!data) return ''; var pairs = []; for(var name in data){ if(!data.hasOwnProperty(name)) continue; if(typeof data[name] === 'function') continue; var value = data[name].toString(); name = encodeURIComponent(name); value = encodeURIComponent(value); pairs.push(name + '=' + value); } return pairs.join('&'); }
0(未初始化)
此阶段确认xhr对象是否建立成功,为调用open方法作好准备,值为0表示对象已经存在,不然浏览器报错
1(载入)
对xhr进行初始化,调用open方法,根据参数完成对象状态的设置,并调用send开始向服务器端发送请求,值为1表示正在向服务器端发送请求
2(载入完成)
接收服务器端的响应数据,但得到的仍是服务器响应的原始数据,并不能在客户端使用。值为2表示已经接收彻底部响应数据,并为下一阶段对数据进行解析作好准备
3(交互)
解析接收到的服务器端响应数据,根据服务器头部返回的MIME把数据转换成对应格式,为在客户端调用作好准备。3表示正在解析数据
4(完成)
确认所有数据已经解析为客户端可用的格式,解析已经完成。值为4表示数据解析完毕,能够经过xhr对象的属性取得数据
xml responseXML
和 text responseText
做为文本返回时,返回的是字符串,但有些字符串比较特殊,如html代码、json对象,因此有以下几种形式
两个页面拥有相同的协议(protocol)端口(port)和主机(host),那么这两个页面就属于同一个源(origin)
不知足同源策略的资源访问,叫跨域资源访问
若是是协议和端口形成的跨域问题“前台”是无能为力的
在跨域问题上,域仅仅是经过“URL的首部”来识别而不会去尝试判断相同的ip地址对应着两个域或两个域是否在同一个ip上。
(“URL的首部”指window.location.protocol +window.location.host,也能够理解为“Domains, protocols and ports must match”。)
CORS(Cross-Origin Resource Sharing)跨域资源共享,定义了必须在访问跨域资源时,浏览器与服务器应该如何沟通。
CORS背后的基本思想就是使用自定义的HTTP头部让浏览器与服务器进行沟通,从而决定请求或响应是应该成功仍是失败。
目前,全部浏览器都支持该功能,IE浏览器不能低于IE10。整个CORS通讯过程,都是浏览器自动完成,不须要用户参与。
对于开发者来讲,CORS通讯与同源的Ajax通讯没有差异,代码彻底同样。
浏览器一旦发现Ajax请求跨源,就会自动添加一些附加的头信息,有时还会多出一次附加的请求,但用户不会有感受。
参考阮老师的文章:http://www.ruanyifeng.com/blog/2016/04/cors.html
JSONP(JSON with Padding)是一个非官方的协议,它容许在服务器端集成Script tags返回至客户端,经过javascript callback的形式实现跨域访问(这仅仅是JSONP简单的实现形式)。单向的数据请求。
JSONP的原理与实现思路
<script><img><iframe>
。由于经过script标签引入的js是不受同源策略的限制的。因此咱们能够经过script标签引入一个js或者是一个其余后缀形式(如php,jsp等)的文件,此文件返回一个js函数的调用
示例以下:(服务器端用node.js)
浏览器端代码:
动态生成script
标签,建立一个callback
,以查询字符串形式跟在src
的url
后边
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>JSONP</title> </head> <body> <button id="btn">JSONP</button> <script> var btn = document.getElementById('btn'); function show(data){ console.log(data); } btn.onclick = function(){ var url = "http://localhost:4000/test?callback=show"; var script = document.createElement('script'); script.setAttribute('src',url); document.getElementsByTagName('head')[0].appendChild(script); } </script> </body> </html>
服务器端代码:
const express = require('express'); const path = require('path'); const app = express(); app.get('/',(req,res)=>{ res.sendFile(path.join(__dirname,'index.html')); }); app.listen(3000,()=>{ console.log('http server is listening in port 3000'); });
跨域服务器端代码:获取请求url
中的callback
,把callback
和json
数据组合后返回,这里须要注意,node.js返回数据的时候,设置res.type("text/javascript");
,
const express = require('express'); const path = require('path'); const app = express(); app.get('/test',(req,res)=>{ let test = require('./test.json'); const callback = req.query.callback; res.type("text/javascript"); res.send(callback+'('+JSON.stringify(test)+')'); }); app.listen(4000,()=>{ console.log('http server is listening in port 4000'); });
json数据:
{
"title" : "JSONP", "time" : "2017-1-1" }
结果:
图1
图2
优缺点
JSONP的优势:它不像XMLHttpRequest对象实现的Ajax请求那样受到同源策略的限制;
它的兼容性更好,在更加古老的浏览器中均可以运行,不须要XMLHttpRequest或ActiveX的支持;
而且在请求完毕后能够经过调用callback的方式回传结果
JSONP的缺点:它只支持GET请求而不支持POST等其它类型的HTTP请求;
它只支持跨域HTTP请求这种状况,不能解决不一样域的两个页面之间如何进行JavaScript调用的问题
一种用 CSS 跨域传输文本的方案
优势:相比 JSONP 更为安全,不须要执行跨站脚本。
缺点:没有 JSONP 适配广,CSST 依赖支持 CSS3 的浏览器。
原理:经过读取 CSS3 content 属性获取传送内容。经过建立一个 link 请求到 css 文件,而后经过 computedStyle = window.getComputedStyle
获取到指定元素的 style 对象,再经过 computedStyle .content
获取到内容