1. ajax 是什么?有什么作用苔咪?
Ajax(['eid??ks])是Asynchronous JavaScript and XML的縮寫(xiě)锰悼,從標(biāo)題里就能看出,是異步的JS和XML团赏,這一技術(shù)能夠向服務(wù)器請(qǐng)求額外的數(shù)據(jù)而無(wú)需卸載整個(gè)頁(yè)面箕般,會(huì)帶來(lái)良好的用戶(hù)體驗(yàn)。因?yàn)閭鹘y(tǒng)的HTTP 請(qǐng)求流程大概如下:
- 瀏覽器向服務(wù)器發(fā)送請(qǐng)求
- 服務(wù)器根據(jù)瀏覽器傳來(lái)數(shù)據(jù)生成response
- 服務(wù)器把response返回給瀏覽器
- 瀏覽器刷新整個(gè)頁(yè)面顯示最新數(shù)據(jù)
這個(gè)過(guò)程是同步的舔清,順序執(zhí)行丝里。如果網(wǎng)絡(luò)傳輸速率過(guò)慢或者其他情況導(dǎo)致,用戶(hù)長(zhǎng)時(shí)間等待請(qǐng)求結(jié)果体谒,而瀏覽器是卡死狀態(tài)杯聚,那么體驗(yàn)就很糟糕。
另外如果當(dāng)前頁(yè)面DOM結(jié)構(gòu)特別復(fù)雜抒痒,內(nèi)容繁多幌绍,而需要請(qǐng)求的僅僅是一小段數(shù)據(jù),卻要大費(fèi)周章故响,數(shù)據(jù)到來(lái)后總是要重新渲染頁(yè)面傀广。
而使用AJAX技術(shù),通過(guò)JavaScript操作瀏覽器提供的XMLHttpRequest 對(duì)象彩届,去發(fā)送一個(gè)Ajax請(qǐng)求伪冰,并接收服務(wù)器傳來(lái)的數(shù)據(jù),然后操作DOM將新數(shù)據(jù)對(duì)網(wǎng)頁(yè)的某部分進(jìn)行更新惨缆,使用Ajax最直觀(guān)的感受是向服務(wù)器獲取新數(shù)據(jù)不需要刷新頁(yè)面等待了糜值。
2. 前后端開(kāi)發(fā)聯(lián)調(diào)需要注意哪些事情丰捷?后端接口完成前如何 mock 數(shù)據(jù)?
-
前后端聯(lián)調(diào)時(shí)需要溝通定義接口:
- 約定好請(qǐng)求方法
- 請(qǐng)求路徑(URL)
- 前端需要傳遞什么樣的參數(shù)(入?yún)ⅲ?/li>
- 數(shù)據(jù)格式(回參寂汇,包括可能的狀態(tài)碼)
-
mock數(shù)據(jù):
在后端接口完成之前病往,前端通過(guò)mock數(shù)據(jù),能夠在不依賴(lài)后端環(huán)境的情況下進(jìn)行開(kāi)發(fā)骄瓣,只要約定好接口停巷。
常用的手段就是搭建本地mock server,如果裝了nodejs榕栏,就可以用npm 下載安裝了畔勤,然后只需要在本地的mock server中實(shí)現(xiàn)請(qǐng)求路由映射即可,如果使用js語(yǔ)言扒磁,也就是 router.js庆揪,可以按照約定在里面模擬假數(shù)據(jù),以相應(yīng)前端瀏覽器的請(qǐng)求妨托。
除此之外缸榛,還可以使用線(xiàn)上的模擬數(shù)據(jù)生成服務(wù),比如Easy Mock:
http://www.easy-mock.com 注冊(cè) 打開(kāi)任意一個(gè)項(xiàng)目 創(chuàng)建接口 使用接口(AJAX兰伤、JSONP都行内颗,具體看文檔)
xhr.open('get', 'http://www.easy-mock.com/mock/59b95cf3e0dc663341a8fa20/example/loadMore', true)
3. 點(diǎn)擊按鈕,使用 ajax 獲取數(shù)據(jù)敦腔,如何在數(shù)據(jù)到來(lái)之前防止重復(fù)點(diǎn)擊?
首先這樣做的目的均澳,是因?yàn)樵谡鎸?shí)的網(wǎng)絡(luò)環(huán)境中,傳輸速率必然是沒(méi)有用戶(hù)鼠標(biāo)雙擊按鈕更快符衔,在當(dāng)前請(qǐng)求未到來(lái)之前找前,用戶(hù)的習(xí)慣總愛(ài)再次點(diǎn)擊按鈕,這會(huì)導(dǎo)致瀏覽器重復(fù)發(fā)送請(qǐng)求判族,結(jié)果對(duì)于用戶(hù)來(lái)說(shuō)纸厉,占用更多帶寬資源還好說(shuō),如果是網(wǎng)購(gòu)付款按鈕呢五嫂,另外對(duì)于服務(wù)器端來(lái)說(shuō)颗品,也會(huì)帶來(lái)更大的運(yùn)行壓力。
一個(gè)典型的例子就是上大學(xué)的時(shí)候沃缘,到了選修課端口開(kāi)放的時(shí)候躯枢,大家齊刷刷早起守著電腦,不停的刷新槐臀,結(jié)果平時(shí)很流暢的教務(wù)網(wǎng)站锄蹂,到了選(搶?zhuān)┱n的時(shí)候,變得異乘卡頓得糜,F(xiàn)5都磨爛了敬扛,也刷新不出來(lái)。其實(shí)這個(gè)時(shí)候服務(wù)器內(nèi)心是崩潰的朝抖。
回到正題啥箭,阻止用戶(hù)重復(fù)點(diǎn)擊,一個(gè)簡(jiǎn)單的做法是治宣,令用戶(hù)的點(diǎn)擊變得無(wú)效急侥, 使用狀態(tài)鎖,請(qǐng)求未處理完成之前侮邀,拒絕再次請(qǐng)求坏怪,而請(qǐng)求到了并處理完之后,恢復(fù)到可請(qǐng)求狀態(tài)绊茧。
具體的代碼演示铝宵,在下文中,將會(huì)提到华畏。
4. 實(shí)現(xiàn)加載更多的功能捉超,后端在本地使用server-mock來(lái)模擬數(shù)據(jù)
本地模擬成功:
代碼地址,注意唯绍!想要看到效果,需要把 index.html 和 router.js 分別保存在同一個(gè)文件夾下枝誊,然后終端里使用server mock 開(kāi)啟mock况芒。
5. 對(duì)AJAX進(jìn)行封裝,這是必須的(以get為例)
var btn = document.querySelector('#load-more')
var ct = document.querySelector('#ct')
var pageIndex = 0
var isDataArrive = true //設(shè)計(jì)個(gè)狀態(tài)鎖
btn.addEventListener('click', function(e){
e.preventDefault() //防止點(diǎn)擊 a 鏈接頁(yè)面跳到頂部 或者h(yuǎn)ref里 javascript:void(0)
if(!isDataArrive){ // 1. 點(diǎn)擊按鈕后叶撒,一開(kāi)始先判斷绝骚,如果狀態(tài)是false,數(shù)據(jù)還沒(méi)到來(lái)祠够,就直接忽略
return;
}
loadData(function(news){ //loadData(renderPage) // 執(zhí)行l(wèi)oadData時(shí)压汪,數(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) //相當(dāng)于callback(results)
}else{
options.onError()
}
}
}
var query = '?'
for(key in options.data){
query += key + '=' + options.data[key] + '&'
}
query = query.substr(0, query.length-1) //舍去最后一個(gè)&止剖,截取出來(lái)
xhr.open(options.type, options.url + query, true)
if(type === 'post'){
xhr.send(dataStr)
}else{
xhr.send()
}
}