1. ajax 是什么?有什么作用?

Ajax(['eid??ks])是Asynchronous JavaScript and XML的縮寫,從標題里就能看出,是異步的JS和XML,這一技術能夠向服務器請求額外的數(shù)據(jù)而無需卸載整個頁面,會帶來良好的用戶體驗。因為傳統(tǒng)的HTTP 請求流程大概如下:
- 瀏覽器向服務器發(fā)送請求
- 服務器根據(jù)瀏覽器傳來數(shù)據(jù)生成response
- 服務器把response返回給瀏覽器
- 瀏覽器刷新整個頁面顯示最新數(shù)據(jù)
這個過程是同步的,順序執(zhí)行。如果網(wǎng)絡傳輸速率過慢或者其他情況導致,用戶長時間等待請求結果,而瀏覽器是卡死狀態(tài),那么體驗就很糟糕。
另外如果當前頁面DOM結構特別復雜,內(nèi)容繁多,而需要請求的僅僅是一小段數(shù)據(jù),卻要大費周章,數(shù)據(jù)到來后總是要重新渲染頁面。
而使用AJAX技術,通過JavaScript操作瀏覽器提供的XMLHttpRequest 對象,去發(fā)送一個Ajax請求,并接收服務器傳來的數(shù)據(jù),然后操作DOM將新數(shù)據(jù)對網(wǎng)頁的某部分進行更新,使用Ajax最直觀的感受是向服務器獲取新數(shù)據(jù)不需要刷新頁面等待了。
2. 前后端開發(fā)聯(lián)調需要注意哪些事情?后端接口完成前如何 mock 數(shù)據(jù)?
-
前后端聯(lián)調時需要溝通定義接口:
- 約定好請求方法
- 請求路徑(URL)
- 前端需要傳遞什么樣的參數(shù)(入?yún)ⅲ?/li>
- 數(shù)據(jù)格式(回參,包括可能的狀態(tài)碼)
-
mock數(shù)據(jù):
在后端接口完成之前,前端通過mock數(shù)據(jù),能夠在不依賴后端環(huán)境的情況下進行開發(fā),只要約定好接口。
常用的手段就是搭建本地mock server,如果裝了nodejs,就可以用npm 下載安裝了,然后只需要在本地的mock server中實現(xiàn)請求路由映射即可,如果使用js語言,也就是 router.js,可以按照約定在里面模擬假數(shù)據(jù),以相應前端瀏覽器的請求。
除此之外,還可以使用線上的模擬數(shù)據(jù)生成服務,比如Easy Mock:
http://www.easy-mock.com 注冊 打開任意一個項目 創(chuàng)建接口 使用接口(AJAX、JSONP都行,具體看文檔)
xhr.open('get', 'http://www.easy-mock.com/mock/59b95cf3e0dc663341a8fa20/example/loadMore', true)
3. 點擊按鈕,使用 ajax 獲取數(shù)據(jù),如何在數(shù)據(jù)到來之前防止重復點擊?
首先這樣做的目的,是因為在真實的網(wǎng)絡環(huán)境中,傳輸速率必然是沒有用戶鼠標雙擊按鈕更快,在當前請求未到來之前,用戶的習慣總愛再次點擊按鈕,這會導致瀏覽器重復發(fā)送請求,結果對于用戶來說,占用更多帶寬資源還好說,如果是網(wǎng)購付款按鈕呢,另外對于服務器端來說,也會帶來更大的運行壓力。
一個典型的例子就是上大學的時候,到了選修課端口開放的時候,大家齊刷刷早起守著電腦,不停的刷新,結果平時很流暢的教務網(wǎng)站,到了選(搶)課的時候,變得異??D,F(xiàn)5都磨爛了,也刷新不出來。其實這個時候服務器內(nèi)心是崩潰的。
回到正題,阻止用戶重復點擊,一個簡單的做法是,令用戶的點擊變得無效, 使用狀態(tài)鎖,請求未處理完成之前,拒絕再次請求,而請求到了并處理完之后,恢復到可請求狀態(tài)。
具體的代碼演示,在下文中,將會提到。
4. 實現(xiàn)加載更多的功能,后端在本地使用server-mock來模擬數(shù)據(jù)
本地模擬成功:

代碼地址,注意!想要看到效果,需要把 index.html 和 router.js 分別保存在同一個文件夾下,然后終端里使用server mock 開啟mock。
5. 對AJAX進行封裝,這是必須的(以get為例)
var btn = document.querySelector('#load-more')
var ct = document.querySelector('#ct')
var pageIndex = 0
var isDataArrive = true //設計個狀態(tài)鎖
btn.addEventListener('click', function(e){
e.preventDefault() //防止點擊 a 鏈接頁面跳到頂部 或者href里 javascript:void(0)
if(!isDataArrive){ // 1. 點擊按鈕后,一開始先判斷,如果狀態(tài)是false,數(shù)據(jù)還沒到來,就直接忽略
return;
}
loadData(function(news){ //loadData(renderPage) // 執(zhí)行l(wèi)oadData時,數(shù)據(jù)到了,執(zhí)行callback,也就是傳入的函數(shù) renderPage
renderPage(news)
pageIndex += 5
isDataArrive = true
})
isDataArrive = false
})
function loadData(callback){
ajax({
type: 'get',
url: 'loadMore',
data: {
index: pageIndex,
length: 5
},
onSuccess: callback,
onError: function(){
console.log('error')
}
})
}
function renderPage(news){
var fragment = document.createDocumentFragment()
for( var i = 0; i < news.length; i++){
var node = document.createElement('li')
node.innerText = news[i]
fragment.appendChild(node)
}
ct.appendChild(fragment)
}
function ajax(options){
var xhr = new XMLHttpRequest()
xhr.onreadystatechange = function(){
if(xhr.readyState === 4){
if(xhr.status === 200 || xhr.status === 304){
var results = JSON.parse(xhr.responseText)
options.onSuccess(results) //相當于callback(results)
}else{
options.onError()
}
}
}
var query = '?'
for(key in options.data){
query += key + '=' + options.data[key] + '&'
}
query = query.substr(0, query.length-1) //舍去最后一個&,截取出來
xhr.open(options.type, options.url + query, true)
if(type === 'post'){
xhr.send(dataStr)
}else{
xhr.send()
}
}