進(jìn)階12 ajax實(shí)踐

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

Ajax(['eid??ks])是Asynchronous JavaScript and XML的縮寫,從標(biāo)題里就能看出,是異步的JS和XML,這一技術(shù)能夠向服務(wù)器請(qǐng)求額外的數(shù)據(jù)而無(wú)需卸載整個(gè)頁(yè)面,會(huì)帶來(lái)良好的用戶體驗(yàn)。因?yàn)閭鹘y(tǒng)的HTTP 請(qǐng)求流程大概如下:

  1. 瀏覽器向服務(wù)器發(fā)送請(qǐng)求
  2. 服務(wù)器根據(jù)瀏覽器傳來(lái)數(shù)據(jù)生成response
  3. 服務(wù)器把response返回給瀏覽器
  4. 瀏覽器刷新整個(gè)頁(yè)面顯示最新數(shù)據(jù)

這個(gè)過(guò)程是同步的,順序執(zhí)行。如果網(wǎng)絡(luò)傳輸速率過(guò)慢或者其他情況導(dǎo)致,用戶長(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最直觀的感受是向服務(wù)器獲取新數(shù)據(jù)不需要刷新頁(yè)面等待了。

2. 前后端開(kāi)發(fā)聯(lián)調(diào)需要注意哪些事情?后端接口完成前如何 mock 數(shù)據(jù)?

  • 前后端聯(lián)調(diào)時(shí)需要溝通定義接口:

  1. 約定好請(qǐng)求方法
  2. 請(qǐng)求路徑(URL)
  3. 前端需要傳遞什么樣的參數(shù)(入?yún)ⅲ?/li>
  4. 數(shù)據(jù)格式(回參,包括可能的狀態(tài)碼)
  • mock數(shù)據(jù):

在后端接口完成之前,前端通過(guò)mock數(shù)據(jù),能夠在不依賴后端環(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)求。

除此之外,還可以使用線上的模擬數(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)有用戶鼠標(biāo)雙擊按鈕更快,在當(dāng)前請(qǐng)求未到來(lái)之前,用戶的習(xí)慣總愛(ài)再次點(diǎn)擊按鈕,這會(huì)導(dǎo)致瀏覽器重復(fù)發(fā)送請(qǐng)求,結(jié)果對(duì)于用戶來(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)站,到了選(搶)課的時(shí)候,變得異常卡頓,F(xiàn)5都磨爛了,也刷新不出來(lái)。其實(shí)這個(gè)時(shí)候服務(wù)器內(nèi)心是崩潰的。

回到正題,阻止用戶重復(fù)點(diǎn)擊,一個(gè)簡(jiǎn)單的做法是,令用戶的點(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()
      }
  }

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

推薦閱讀更多精彩內(nèi)容