Ajax 技術(shù)
第1章 認(rèn)識Ajax
1.1 初識 ajax
我們平常上網(wǎng),不管是注冊賬號侦啸,還是瀏覽網(wǎng)頁突颊,其本質(zhì)就是通過客戶端向服務(wù)器發(fā)送請求,服務(wù)器接到請求后返回處理后的數(shù)據(jù)給客戶端评腺;
在我們之前學(xué)習(xí)代碼中帘瞭,向服務(wù)器提交數(shù)據(jù)典型的應(yīng)用是就是 form 表單,其中的 action 就是我們提交數(shù)據(jù)的服務(wù)器端地址蒿讥;
完成一個 form 表單蝶念;
當(dāng)我們點(diǎn)擊提交按鈕時,頁面就會跳轉(zhuǎn)到服務(wù)器頁面芋绸;
但是媒殉,我本不想讓頁面跳轉(zhuǎn),數(shù)據(jù)也能被發(fā)送到服務(wù)器端摔敛,同時廷蓉,還可以接受服務(wù)器返回的數(shù)據(jù);
當(dāng)我注冊一個網(wǎng)站的賬號時马昙,填寫完用戶名并沒有點(diǎn)擊提交桃犬,但是,用戶名如果有重復(fù)行楞,文本框的傍邊便會提示我更換用戶名攒暇;
類似的功能還有 驗證短信的發(fā)送、百度搜索的關(guān)鍵字推舉子房、無刷新的分頁等等……
想要完成這些類似的功能實現(xiàn)形用,我們今天所要學(xué)習(xí)的ajax技術(shù),就是核心技術(shù)证杭;
ajax 也是技術(shù)名詞的縮寫:
Asynchronous [?'s??kr?n?s; e?-]
:異步的田度;
JavaScript :JavaScript語言
And :和、與
XML :數(shù)據(jù)傳輸格式
1998年微軟公司(Microsoft)的Outlook Web Access第一次使用了ajax技術(shù)解愤,允許客戶端腳本發(fā)送HTTP請求镇饺,并隨后集成在IE4.0中應(yīng)用(XMLHTTP),到2005年送讲,谷歌(Google)把Ajax成功應(yīng)用于自家的多款Web系統(tǒng)中(Gmail郵箱奸笤、Google Map、Google 搜索建議)李茫,
從此Ajax被越來越多的人所接受…
客戶端通過HTTP向服務(wù)器發(fā)送請求
1.2 快速入門
<body>
<form action="1-1-1.php" method="get">
<input type="text" name="names" value=""><br>
<input type="button" value="提交">
</form>
</body>
<script>
//獲取DOM對象
var inp = document.getElementsByTagName('input');
//綁定點(diǎn)擊事件
inp[1].onclick = function(){
//獲取文本值
var v = inp[0].value;
//獲取ajax對象
var xhr = new XMLHttpRequest();
//監(jiān)聽狀態(tài)變化
xhr.onreadystatechange = function(){
//判斷狀態(tài)值
if(xhr.readyState == 4){
//獲取服務(wù)器返回信息
alert(xhr.responseText);
}
}
//打開鏈接
xhr.open('get','/test');
//發(fā)送連接
xhr.send();
}
</script>
http.js
var fs = require('fs');
// 服務(wù)器模塊
var http = require('http');
var server = http.createServer();
server.listen(8080,function(){
console.log('服務(wù)器啟動成功,請訪問:http://127.0.0.1:8080')
})
server.on('request',function(req,res){
var method = req.method;
var urls = require('url').parse(req.url);
if(method == 'GET'){
if(urls.pathname.indexOf('.html')>=0){
fs.readFile('.'+urls.pathname,function(err,data){
res.end(data);
})
}else if(urls.pathname == '/test'){
res.end('123')
}
// console.log(urls.pathname);
}else if(method == 'POST'){
}else{
res.end('err_method')
}
})
第2章 Ajax對象
2.1 獲取對象
通過上一節(jié)我們發(fā)現(xiàn)揭保,想要使用 ajax 的一系列功能肥橙,我們就必須先得到 ajax 對象
基于 W3C標(biāo)準(zhǔn) 瀏覽器:
var xhr = new XMLHttpRequest();
基于IE內(nèi)核的瀏覽器:
var xhr = new ActiveXObject('Microsoft.XMLHTTP');
<script>
var btu = document.getElementById('btu');
btu.onclick = function(){
//基于 W3C標(biāo)準(zhǔn) 瀏覽器
var xhr = new XMLHttpRequest();
alert(xhr);
//基于IE內(nèi)核的瀏覽器魄宏, W3C標(biāo)準(zhǔn)瀏覽器中報錯
var xhr = new ActiveXObject('Microsoft.XMLHTTP');
alert(xhr);
}
</script>
瀏覽器標(biāo)準(zhǔn)不一樣,得到的對象也不一樣存筏,我們也不知道客戶使用什么樣的瀏覽器宠互,因此味榛,我們需要解決兼容性問題;
修改上述代碼并測試予跌,具有兼容性:
<script>
var btu = document.getElementById('btu');
btu.onclick = function(){
try{
var xhr = new XMLHttpRequest()
}catch(e){};
try{
var xhr = new ActiveXObject('Microsoft.XMLHTTP')
}catch(e){};
alert(xhr);
}
</script>
再次對代碼進(jìn)行修改 兼容代碼封裝進(jìn)函數(shù)調(diào)用
<script>
var btu = document.getElementById('btu');
btu.onclick = function(){
//封裝進(jìn)函數(shù)供其他程序調(diào)用
function cXHR(){
try{return new XMLHttpRequest()}catch(e){};
try{return new ActiveXObject('Microsoft.XMLHTTP')}catch(e){};
}
alert(cXHR());
}
</script>
將函數(shù)寫入單獨(dú)的文件搏色,共其他地方引入調(diào)用
創(chuàng)建createXHR.js
將函數(shù)復(fù)制到文件 createXHR.js 內(nèi)并保存, 如圖:
[圖片上傳失敗...(image-25b3a3-1590025293827)]
使用:
//文件引入
<script src="createXHR.js"></script>
<script>
var btu = document.getElementById('btu');
btu.onclick = function(){
//函數(shù)調(diào)用
alert(cXHR());
}
</script>
順便封裝一個方法:
使用id屬性獲取DOM對象,方便后面使用
function gid(id){
return document.getElementById(id);
}
2.2 ajax對象的屬性券册、方法 *
火狐開發(fā)者文檔:
https://developer.mozilla.org/zh-CN/docs/Web/API/XMLHttpRequest
2.2.1 屬性
**readyState: Ajax狀態(tài)碼 * **
0:表示對象已建立频轿,但未初始化,只是 new 成功獲取了對象烁焙,但是未調(diào)用open方法
1:表示對象已初始化航邢,但未發(fā)送,調(diào)用了open方法骄蝇,但是未調(diào)用send方法
2:已調(diào)用send方法進(jìn)行請求
3:正在接收數(shù)據(jù)(接收到一部分)膳殷,客戶端已經(jīng)接收到了一部分返回的數(shù)據(jù)
**4:接收完成,客戶端已經(jīng)接收到了所有數(shù)據(jù) * **status :http響應(yīng)狀態(tài)碼
200代表成功獲取服務(wù)器端數(shù)據(jù)
404未找到頁面等等……statusText :http響應(yīng)狀態(tài)文本
responseText:如果服務(wù)器端返回字符串九火,使用responseText進(jìn)行接收
responseXML :如果服務(wù)器端返回XML數(shù)據(jù)赚窃,使用responseXML進(jìn)行接收
onreadystatechange:當(dāng) readyState 狀態(tài)碼發(fā)生改變時所觸發(fā)的回調(diào)函數(shù)
2.2.2 方法
-
open(method,url,[aycs]):初始化Ajax對象 (打開)
method:http請求方式,get/post
url:請求的服務(wù)器地址
aycs:同步與異步
-
setRequestHeader(header,value):設(shè)置請求頭信息
header :請求頭名稱
value :請求頭的值
xhr.getAllResponseHeaders() 獲取全部響應(yīng)頭信息
xhr.getResponseHeader('key') 獲取指定頭信息
-
send([content]) :發(fā)送Ajax請求
content : 如果是get請求時岔激,此參數(shù)為null;如果是post請求時勒极,此參數(shù)就是要傳遞的數(shù)據(jù)
注意: 所有相關(guān)的事件綁定必須在調(diào)用send()方法之前進(jìn)行.
2.2.3 同步與異步
例如,小明去餐館排隊點(diǎn)餐鹦倚,前臺服務(wù)員將小明的菜單告訴廚師進(jìn)行制作河质,此時小明后面排隊的人就一直等著,
直到廚師制作完成震叙,把飯菜送到小明手里后離開掀鹅,后面的人才能繼續(xù)點(diǎn)餐;這就是同步處理
但是媒楼,如果前臺服務(wù)員將小明的菜單告訴廚師后乐尊,服務(wù)員發(fā)給小明一個好牌去旁邊等待,后面的人繼續(xù)點(diǎn)餐划址,
廚師將小明的飯菜做好后扔嵌,隨時呼喚小明就餐;這就是異步處理
服務(wù)器的不同做法夺颤,就代表著 Ajax 的同步或異步處理痢缎;
小明就是客戶端;
廚師就是后臺服務(wù)器世澜;
圖示:
前臺代碼:
<script src="createXHR.js"></script>
<script>
function t1(){
var xhr = cXHR();
xhr.onreadystatechange = function(){
if(this.readyState == 4){
alert(this.responseText);
}
}
//false同步
//true 異步
xhr.open('get','/test',false);
xhr.send(null);
}
function t2(){
alert('t2');
}
t1();
t2();
</script>
第3章 判斷用戶名是否可用--案例
百度注冊效果
用戶名被占用:
用戶名沒有沒占用:
前臺代碼
<body>
<input type="text" value="" id="names">
<span id="tip"></span>
</body>
<script>
var inp = document.getElementById('names');
inp.onblur = function () {
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function () {
if (xhr.readyState == 4) {
// alert(xhr.responseText);
if (xhr.responseText == 1) {
var h = '<font color="red">用戶名已經(jīng)被占用</font>';
document.getElementById('tip').innerHTML = h;
} else {
var h = '<font color="green">用戶名可用</font>';
document.getElementById('tip').innerHTML = h;
}
}
}
xhr.open('get', '/baidu?names=' + inp.value);
xhr.send();
}
</script>
http.js
else if(urls.pathname == '/test'){
res.end('123')
}else if(urls.pathname == '/baidu'){
if(urls.query.names == 'admin'){
res.end('1')
}else{
res.end('0')
}
}
第4章 緩存問題
4.1 緩存的產(chǎn)生
以上一節(jié)的案例為模板独旷,使用IE9以下版本瀏覽器測試,有緩存問題;
原因:
在Ajax的get請求中嵌洼,如果運(yùn)行在IE內(nèi)核的瀏覽器下案疲,
其如果向同一個url發(fā)送多次請求時,就會產(chǎn)生所謂的緩存問題麻养。
緩存問題最早設(shè)計初衷是為了加快應(yīng)用程序的訪問速度褐啡,
但是其會影響Ajax實時的獲取服務(wù)器端的數(shù)據(jù)。
4.2 客戶端解決緩存問題
產(chǎn)生緩存的問題就是 我們的客戶端向同一個 url 發(fā)送了多次請求鳖昌;
如果我們每次請求的url不同备畦,那么,緩存問題就不會存在了许昨;
我們可以在請求地址的后面加上一個無意義的參數(shù)萍恕,參數(shù)值使用隨機(jī)數(shù)即可,
那么每次請求都會產(chǎn)生隨機(jī)數(shù)车要,URL就會不同允粤,緩存問題就被解決了;
Math.random():返回 0--1 之間的隨機(jī)數(shù)翼岁,包括 0 但不包括 1类垫;
修改代碼如下:
var url = '03-1.php?names='+inp.value+'&_='+Math.random();
xhr.open('get',url);
但是,隨機(jī)數(shù)雖然解決了問題琅坡,但是悉患,我們不能保證每次生成的隨機(jī)數(shù)都不一樣;
也就是說榆俺,使用隨機(jī)數(shù)存在一定的隱患售躁;
new Date().getTime() : 獲取當(dāng)前時間的毫秒時間戳
修改代碼如下:
var url = '03-1.php?names='+inp.value+'&_='+new Date().getTime();
xhr.open('get',url);
4.3 設(shè)置響應(yīng)頭禁用客戶端緩存
服務(wù)器端在相應(yīng)客戶端請求時,可以設(shè)置相應(yīng)頭詳細(xì)茴晋,如:
header(‘Content-type:text/html; charset=utf-8’) :告訴客戶端瀏覽器陪捷,使用utf-8的編碼格式解析html頁面信息。
設(shè)置不緩存的響應(yīng)頭標(biāo)識即可:
//告訴客戶端瀏覽器不要緩存數(shù)據(jù)
res.setHeader('Cache-Control','no-cache');
第5章 Ajax發(fā)送POST請求
5.1 post請求
復(fù)制第3章案例代碼诺擅,將 get 請求修改為 post 請求市袖;
//請求地址
var url = 'baidu';
//open參數(shù)為post
xhr.open('post',url);
//設(shè)置請求頭 ***
xhr.setRequestHeader('Content-type','application/x-www-form-urlencoded');
//設(shè)置post請求參數(shù)值
xhr.send('names='+inp.value);
5.2 無刷新修改信息
將海賊王項目修改用戶信息操作,改為ajax方式烁涌;
第6章 Ajax框架的封裝
如果一個頁面中有十幾個地方用到Ajax苍碟,那么我們需要寫十幾次open()、十幾次send()撮执、十幾次獲取xhr對象微峰;
代碼重復(fù)相當(dāng)多,而凡是有代碼重復(fù)的地方抒钱,就有封裝的可能蜓肆;
創(chuàng)建新文件: ajax.js
6.1 餐前甜點(diǎn)
之前我們?yōu)榱朔奖闶褂玫嗬疲庋b過使用指定 id 獲取DOM對象及獲取xhr對象;
我們對之前的代碼進(jìn)行一次修改症杏,使其更加優(yōu)雅;
定義一個自調(diào)用匿名函數(shù)
(function(){
//code……
})();
為什么 定義一個自調(diào)用匿名函數(shù)瑞信?
在實際項目開發(fā)中厉颤,如果一個項目同時引入了多個javascript框架,可能會產(chǎn)生命名的沖突問題凡简,
如果使用自調(diào)用匿名函數(shù)來封裝javascript框架逼友,所有變量處于封閉狀態(tài),就可以避免這個問題秤涩。
封裝一個$函數(shù)帜乞,用于獲取指定id的dom對象
(function(){
//封裝$函數(shù),獲取指定 id 的DOM對象并返回給調(diào)用者
var $ = function(id){
return document.getElementById(id);
}
})();
我們在前臺代碼中引入并使用ajax.js
<body>
<div id="d">div</div>
</body>
<script src="ajax.js"></script>
<script>
alert($('d'));
</script>
報錯原因: 函數(shù) $ 為局部變量筐眷;
讓 $ 局部變量全局化
(function(){
//封裝$函數(shù)黎烈,獲取指定 id 的DOM對象并返回給調(diào)用者
var $ = function(id){
return document.getElementById(id);
}
//將局部變量 $ 復(fù)制給頂層window對象,使其成為全局變量
window.$ = $;
})();
6.2 封裝get方法
ajax代碼我們都會寫匀谣,問題是:
如何把代碼放進(jìn)匿名函數(shù)中并且外部可以調(diào)用照棋?
(function(){
//封裝$函數(shù),獲取指定 id 的DOM對象并返回給調(diào)用者
var $ = function(id){
return document.getElementById(id);
}
//將局部變量 $ 復(fù)制給頂層window對象武翎,使其成為全局變量
window.$ = $;
//聲明gets方法
var gets = function(url){
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function(){
if(xhr.readyState == 4){
alert(xhr.responseText);
}
}
xhr.open('get',url);
xhr.send();
}
//將局部變量 gets 復(fù)制給頂層window對象烈炭,使其成為全局變量
window.ajax_get = gets;
})();
這樣寫并沒有語法錯誤,也可以正常調(diào)用宝恶,但是符隙,隨著功能的不斷增加,
我們的window對象也會被賦予各種各樣的值垫毙,最終還是會導(dǎo)致混亂霹疫;
在JavaScript中一切都是對象
综芥;
(function(){
//封裝$函數(shù)更米,獲取指定 id 的DOM對象并返回給調(diào)用者
var $ = function(id){
return document.getElementById(id);
}
//聲明ajax函數(shù),并復(fù)制給$;
$.get = function(url){
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function(){
if(xhr.readyState == 4){
alert(xhr.responseText);
}
}
xhr.open('get',url);
xhr.send();
}
window.$ = $;
})();
前臺調(diào)用
<script>
$.get('/test');
</script>
6.3 解決獲取Ajax對象的兼容性
修改上節(jié)代碼:
//獲取Ajax對象
$.init = function(){
try{return new XMLHttpRequest()}catch(e){};
try{return new ActiveXObject('Microsoft.XMLHTTP')}catch(e){};
}
//聲明ajax函數(shù)毫痕,并復(fù)制給$;
$.get = function(url){
var xhr = $.init(); //調(diào)用init,獲取ajax對象
xhr.onreadystatechange = function(){
if(xhr.readyState == 4){
alert(xhr.responseText);
}
}
xhr.open('get',url);
xhr.send();
}
6.4 獲取Ajax的返回值
前臺調(diào)用:
<script>
var cb = function(msg){
$('d').innerHTML = msg;
}
$.get('/test',cb);
</script>
修改 ajax.js
$.get = function(url,callback){
var xhr = $.init(); //調(diào)用init,獲取ajax對象
xhr.onreadystatechange = function(){
if(xhr.readyState == 4){
callback(xhr.responseText);
}
}
xhr.open('get',url);
xhr.send();
}
前臺調(diào)用修改:
<script>
// var cb = function(msg){
// $('d').innerHTML = msg;
// }
$.get('09-1.php',function(msg){
$('d').innerHTML = msg;
});
</script>
6.5 配合后臺獲取不同的返回值類型
修改 ajax.js
//聲明ajax函數(shù)征峦,并復(fù)制給$;
$.get = function(url,callback,type=null){
var xhr = $.init(); //調(diào)用init,獲取ajax對象
xhr.onreadystatechange = function(){
if(xhr.readyState == 4){
if(type==null){
callback(xhr.responseText);
}
if(type=='json'){
var t = JSON.parse(xhr.responseText);
callback(t);
}
}
}
xhr.open('get',url);
xhr.send();
}
前臺調(diào)用,代碼修改:
<script>
$.get('/test',function(msg){
console.log(msg);
},'json');
</script>
6.6 作業(yè)
封裝 POST 方法到 ajax.js
第10章 跨域問題的解決方案
10.1 認(rèn)識jsonp
<script src="ajax.js"> </script>
<script>
$.get('http://127.0.0.1:9000',function(){});
</script>
ajax 請求的URL地址消请,不在當(dāng)前域名下栏笆,就會出現(xiàn)一下錯誤:
同源策略,也叫跨域禁止策略臊泰;
阻止從一個域上加載的腳本蛉加,獲取或操作另一個域上的資源;
但是,公司內(nèi)部系統(tǒng)的數(shù)據(jù)交互就無法進(jìn)行:
公司OA系統(tǒng) :http://oa.itcast.cn
公司ERP系統(tǒng) :http://erp.itcast.cn
公司ESM系統(tǒng) :http://esm.itcast.cn
而Web頁面上調(diào)用js文件時則不受是否跨域的影響
(不僅如此针饥,我們還發(fā)現(xiàn)凡是擁有"src"這個屬性的標(biāo)簽都擁有跨域的能力厂抽,比如script、img丁眼、iframe)筷凤;
src 的能力就是把遠(yuǎn)程的數(shù)據(jù)資源加載到本地(圖片、JS代碼等);
前臺代碼:
<script src="ajax.js"> </script>
<script>
//提前寫好函數(shù)苞七,調(diào)用函數(shù)需要傳參
function cb(msg){
console.log(msg);
}
</script>
<!--src加載進(jìn)來的代碼就是一個JS的函數(shù)調(diào)用,cb函數(shù)調(diào)用 -->
<script src="http://bbs.com/1.php"></script>
后臺PHP代碼:
$arr = ['a'=>1,'b'=>'san','c'=>'wu','d'=>4];
$str = json_encode($arr);
//返回字符串藐守,JS代碼的函數(shù)調(diào)用
//要返回的數(shù)據(jù)作為函數(shù)傳參傳遞
echo "cb(".$str.")";
修改前后臺代碼,增加靈活性蹂风;
前臺代碼:
<script src="ajax.js"> </script>
<script>
//提前寫好函數(shù)卢厂,調(diào)用函數(shù)需要傳參
function callback(msg){
console.log(msg);
}
</script>
<!--src加載進(jìn)來的代碼就是一個JS的函數(shù)調(diào)用,cb函數(shù)調(diào)用 -->
<!--地址get傳參,告知后臺函數(shù)調(diào)用名稱 -->
<script src="http://bbs.com/1.php?cb=callback"></script>
后臺PHP代碼:
$arr = ['a'=>1,'b'=>'san','c'=>'wu','d'=>4];
$str = json_encode($arr);
//返回字符串惠啄,JS代碼的函數(shù)調(diào)用
//要返回的數(shù)據(jù)作為函數(shù)傳參傳遞
//接受參數(shù)拼接慎恒,作為函數(shù)調(diào)用名稱
echo $_GET['cb']."($str)";
10.2 如何使用JSONP
<body>
<input type="button" id="btu" value="點(diǎn)擊">
</body>
<script src="ajax.js"> </script>
<script>
//提前寫好函數(shù),調(diào)用函數(shù)需要傳參
function callback(msg){
console.log(msg);
}
//動態(tài)添加script標(biāo)簽及src屬性
$('btu').onclick = function(){
var sc = document.createElement('script');
sc.src = "http://bbs.com/2.php?cb=callback";
document.getElementsByTagName('head')[0].appendChild(sc);
}
</script>
就是在遠(yuǎn)程服務(wù)器上設(shè)法動態(tài)的把數(shù)據(jù)裝進(jìn)js格式的文本代碼段中撵渡,供客戶端調(diào)用和進(jìn)一步處理巧号;
在前臺通過動態(tài)添加script標(biāo)簽及src屬性,表面看上去與ajax極為相似姥闭,但是丹鸿,這和ajax并沒有任何關(guān)系;
為了便于使用及交流棚品,逐漸形成了一種 非正式傳輸協(xié)議靠欢,人們把它稱作 JSONP ;
該協(xié)議的一個要點(diǎn)就是允許用戶傳遞一個callback參數(shù)給服務(wù)端铜跑,
然后服務(wù)端返回數(shù)據(jù)時會將這個callback參數(shù)作為函數(shù)名來包裹住JSON數(shù)據(jù)门怪,
這樣客戶端就可以隨意定制自己的函數(shù)來自動處理返回數(shù)據(jù)了。
10.3 跨域資源共享( CORS)機(jī)制
https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Access_control_CORS
php代碼中添加一下header頭聲明:
Access-Control-Allow-Origin:* //域名锅纺,* 允許所有
php:(服務(wù)端代碼)
<?php
header('Access-Control-Allow-Origin:http://localhost');
echo 1