JavaScript學(xué)習(xí)筆記(二十七)-- ajax及ajax封裝

AJAX

  • ajax 全名 async javascript and XML

  • 是前后臺(tái)交互的能力

  • 也就是我們客戶(hù)端給服務(wù)端發(fā)送消息的工具惰许,以及接受響應(yīng)的工具

  • 是一個(gè)默認(rèn)異步 執(zhí)行機(jī)制的功能

AJAX 的優(yōu)勢(shì)

  1. 不需要插件的支持,原生 js 就可以使用

  2. 用戶(hù)體驗(yàn)好(不需要刷新頁(yè)面就可以更新數(shù)據(jù))

  3. 減輕服務(wù)端和帶寬的負(fù)擔(dān)

  4. 缺點(diǎn): 搜索引擎的支持度不夠史辙,因?yàn)閿?shù)據(jù)都不在頁(yè)面上汹买,搜索引擎搜索不到

AJAX 的使用

  • 在 js 中有內(nèi)置的構(gòu)造函數(shù)來(lái)創(chuàng)建 ajax 對(duì)象

  • 創(chuàng)建 ajax 對(duì)象以后,我們就使用 ajax 對(duì)象的方法去發(fā)送請(qǐng)求和接受響應(yīng)

創(chuàng)建一個(gè) ajax 對(duì)象

// IE9及以上
const xhr = new XMLHttpRequest()
?
// IE9以下
const xhr = new ActiveXObject('Mricosoft.XMLHTTP')
  • 上面就是有了一個(gè) ajax 對(duì)象

  • 我們就可以使用這個(gè) xhr 對(duì)象來(lái)發(fā)送 ajax 請(qǐng)求了

配置鏈接信息

const xhr = new XMLHttpRequest()
?
// xhr 對(duì)象中的 open 方法是來(lái)配置請(qǐng)求信息的
// 第一個(gè)參數(shù)是本次請(qǐng)求的請(qǐng)求方式 get / post / put / ...
// 第二個(gè)參數(shù)是本次請(qǐng)求的 url 
// 第三個(gè)參數(shù)是本次請(qǐng)求是否異步聊倔,默認(rèn) true 表示異步晦毙,false 表示同步
// xhr.open('請(qǐng)求方式', '請(qǐng)求地址', 是否異步)
xhr.open('get', './data.php')
  • 上面的代碼執(zhí)行完畢以后,本次請(qǐng)求的基本配置信息就寫(xiě)完了

發(fā)送請(qǐng)求

const xhr = new XMLHttpRequest()
xhr.open('get', './data.php')
?
// 使用 xhr 對(duì)象中的 send 方法來(lái)發(fā)送請(qǐng)求
xhr.send()
  • 上面代碼是把配置好信息的 ajax 對(duì)象發(fā)送到服務(wù)端

一個(gè)基本的 ajax 請(qǐng)求

  • 一個(gè)最基本的 ajax 請(qǐng)求就是上面三步

  • 但是光有上面的三個(gè)步驟耙蔑,我們確實(shí)能把請(qǐng)求發(fā)送的到服務(wù)端

  • 如果服務(wù)端正常的話(huà)见妒,響應(yīng)也能回到客戶(hù)端

  • 但是我們拿不到響應(yīng)

  • 如果想拿到響應(yīng),我們有兩個(gè)前提條件

  1. 本次 HTTP 請(qǐng)求是成功的甸陌,也就是我們之前說(shuō)的 http 狀態(tài)碼為 200 ~ 299

  2. ajax 對(duì)象也有自己的狀態(tài)碼须揣,用來(lái)表示本次 ajax 請(qǐng)求中各個(gè)階段

ajax 狀態(tài)碼

  • ajax 狀態(tài)碼 - xhr.readyState

  • 是用來(lái)表示一個(gè) ajax 請(qǐng)求的全部過(guò)程中的某一個(gè)狀態(tài)

    • readyState === 0: 表示未初始化完成,也就是 open 方法還沒(méi)有執(zhí)行

    • readyState === 1: 表示配置信息已經(jīng)完成钱豁,也就是執(zhí)行完 open 之后

    • readyState === 2: 表示 send 方法已經(jīng)執(zhí)行完成

    • readyState === 3: 表示正在解析響應(yīng)內(nèi)容

    • readyState === 4: 表示響應(yīng)內(nèi)容已經(jīng)解析完畢耻卡,可以在客戶(hù)端使用了

  • 這個(gè)時(shí)候我們就會(huì)發(fā)現(xiàn),當(dāng)一個(gè) ajax 請(qǐng)求的全部過(guò)程中牲尺,只有當(dāng) readyState === 4 的時(shí)候卵酪,我們才可以正常使用服務(wù)端給我們的數(shù)據(jù)

  • 所以幌蚊,配合 http 狀態(tài)碼為 200 ~ 299

  • 一個(gè) ajax 對(duì)象中有一個(gè)成員叫做 xhr.status

  • 這個(gè)成員就是記錄本次請(qǐng)求的 http 狀態(tài)碼的

  • 兩個(gè)條件都滿(mǎn)足的時(shí)候,才是本次請(qǐng)求正常完成

readyStateChange

  • 在 ajax 對(duì)象中有一個(gè)事件溃卡,叫做 readyStateChange 事件

  • 這個(gè)事件是專(zhuān)門(mén)用來(lái)監(jiān)聽(tīng) ajax 對(duì)象的readyState 值改變的的行為

  • 也就是說(shuō)只要readyState 的值發(fā)生變化了溢豆,那么就會(huì)觸發(fā)該事件

  • 所以我們就在這個(gè)事件中來(lái)監(jiān)聽(tīng) ajax 的readyState 是不是到 4 了

const xhr = new XMLHttpRequest()
xhr.open('get', './data.php')
?
xhr.send()
?
xhr.onreadyStateChange = function () {
 // 每次 readyState 改變的時(shí)候都會(huì)觸發(fā)該事件
 // 我們就在這里判斷 readyState 的值是不是到 4
 // 并且 http 的狀態(tài)碼是不是 200 ~ 299
 if (xhr.readyState === 4 && /^2\d{2|$/.test(xhr.status)) {
 // 這里表示驗(yàn)證通過(guò)
 // 我們就可以獲取服務(wù)端給我們響應(yīng)的內(nèi)容了
  }
}

responseText

  • ajax 對(duì)象中的responseText 成員

  • 就是用來(lái)記錄服務(wù)端給我們的響應(yīng)體內(nèi)容的

  • 所以我們就用這個(gè)成員來(lái)獲取響應(yīng)體內(nèi)容就可以

const xhr = new XMLHttpRequest()
xhr.open('get', './data.php')
?
xhr.send()
?
xhr.onreadyStateChange = function () {
 if (xhr.readyState === 4 && /^2\d{2|$/.test(xhr.status)) {
 // 我們?cè)谶@里直接打印 xhr.responseText 來(lái)查看服務(wù)端給我們返回的內(nèi)容
 console.log(xhr.responseText)
  }
}

使用 ajax 發(fā)送請(qǐng)求時(shí)攜帶參數(shù)

  • 我們使用 ajax 發(fā)送請(qǐng)求也是可以攜帶參數(shù)的

  • 參數(shù)就是和后臺(tái)交互的時(shí)候給他的一些信息

  • 但是攜帶參數(shù) get 和 post 兩個(gè)方式還是有區(qū)別的

發(fā)送一個(gè)帶有參數(shù)的 get 請(qǐng)求

  • get 請(qǐng)求的參數(shù)就直接在 url 后面進(jìn)行拼接就可以
const xhr = new XMLHttpRequest()
// 直接在地址后面加一個(gè) ?,然后以 key=value 的形式傳遞
// 兩個(gè)數(shù)據(jù)之間以 & 分割
xhr.open('get', './data.php?a=100&b=200')
?
xhr.send()
  • 這樣服務(wù)端就能接受到兩個(gè)參數(shù)

  • 一個(gè)是 a瘸羡,值是 100

  • 一個(gè)是 b漩仙,值是 200

發(fā)送一個(gè)帶有參數(shù)的 post 請(qǐng)求

  • post 請(qǐng)求的參數(shù)是攜帶在請(qǐng)求體中的,所以不需要再 url 后面拼接
const xhr = new XMLHttpRequest()
xhr.open('post', './data.php')
?
// 如果是用 ajax 對(duì)象發(fā)送 post 請(qǐng)求犹赖,必須要先設(shè)置一下請(qǐng)求頭中的 content-type
// 告訴一下服務(wù)端我給你的是一個(gè)什么樣子的數(shù)據(jù)格式
xhr.setRequestHeader('content-type', 'application/x-www-form-urlencoded')
?
// 請(qǐng)求體直接再 send 的時(shí)候?qū)懺?() 里面就行
// 不需要問(wèn)號(hào)讯赏,直接就是 'key=value&key=value' 的形式
xhr.send('a=100&b=200')
  • application/x-www-form-urlencoded 表示的數(shù)據(jù)格式就是 key=value&key=value

同源策略

  • ** 同源策略是由瀏覽器給的**

  • 瀏覽器不允許我們向別人發(fā)送請(qǐng)求,只能向自己的服務(wù)器發(fā)送請(qǐng)求

  • 當(dāng)我們想向別人的服務(wù)器發(fā)送請(qǐng)求的時(shí)候冷尉,就會(huì)被瀏覽器阻止了

  • 什么是 “別人的服務(wù)器” 呢?

    • 當(dāng) 請(qǐng)求協(xié)議/域名/端口號(hào) 有任意一個(gè)不同的時(shí)候系枪,那么就算是別人的服務(wù)器

    • 這個(gè)時(shí)候就會(huì)觸發(fā)同源策略

  • 我們管觸發(fā)了 同源策略 的請(qǐng)求叫做跨域請(qǐng)求

實(shí)現(xiàn)一個(gè)跨域請(qǐng)求

  • 有的時(shí)候我們是需要實(shí)現(xiàn)跨域請(qǐng)求的

  • 我們需要多個(gè)服務(wù)器給一個(gè)頁(yè)面提供數(shù)據(jù)

  • 那么這個(gè)時(shí)候我們就要想辦法解決跨域問(wèn)題

JSONP

  • jsonp 是我們實(shí)現(xiàn)跨域請(qǐng)求的手段私爷,是把我們之前的東西組合在一起使用的技術(shù)手段而已

  • 利用的是 script 標(biāo)簽來(lái)實(shí)現(xiàn)

script 標(biāo)簽的本質(zhì)

  • 瀏覽器給我們提供了一個(gè) script 標(biāo)簽

  • 它的本質(zhì)就是請(qǐng)求一個(gè)外部資源衬浑,是不受到同源策略的影響的

  • 同時(shí) script 標(biāo)簽的 src 屬性,也是一種請(qǐng)求尸饺,也能被服務(wù)器接收到

  • 并且:

    • script標(biāo)簽的src屬性請(qǐng)求回來(lái)的東西是一個(gè)字符串助币,瀏覽器會(huì)把這個(gè)字符串當(dāng)作 js 代碼來(lái)執(zhí)行
  • 所以我們就可以利用這個(gè) script 標(biāo)簽的 src 屬性來(lái)進(jìn)行跨域請(qǐng)求了

配置代理(了解)

  • 代理,分成兩種迹栓,正向代理和反向代理

正向代理

  • 有一個(gè)客戶(hù)端需要向一個(gè)非同源的服務(wù)器B發(fā)送請(qǐng)求

  • 我們搭建一個(gè)和客戶(hù)端同源的服務(wù)器A

  • 當(dāng)客戶(hù)端發(fā)送請(qǐng)求的時(shí)候克伊,由服務(wù)器A來(lái)接受

  • 再由服務(wù)器A向服務(wù)器B發(fā)送請(qǐng)求华坦,因?yàn)?同源策略是由瀏覽器給的季春,服務(wù)器之間沒(méi)有

  • 服務(wù)器B接受到請(qǐng)求以后,會(huì)處理請(qǐng)求耘拇,并把響應(yīng)返回給服務(wù)器A

  • 再由服務(wù)器A把響應(yīng)給到客戶(hù)端就可以了

  • 我們就可以用這個(gè)方式來(lái)進(jìn)行跨域請(qǐng)求了

反向代理

  • 反向代理一般是用來(lái)做負(fù)載均衡的

  • 當(dāng)我請(qǐng)求一個(gè)服務(wù)器的時(shí)候惫叛,其實(shí)請(qǐng)求的是服務(wù)器端設(shè)置的代理服務(wù)器

  • 由代理服務(wù)器把若干大量的請(qǐng)求分發(fā)給不同的服務(wù)器進(jìn)行處理

  • 再由服務(wù)器把響應(yīng)給到代理服務(wù)器

  • 代理服務(wù)器返回給客戶(hù)端

封裝 AJAX

  • ajax 使用起來(lái)太麻煩,因?yàn)槊看味家獙?xiě)很多的代碼

  • 那么我們就封裝一個(gè) ajax 方法來(lái)讓我們使用起來(lái)簡(jiǎn)單一些

確定一下使用的方式

  • 因?yàn)橛幸恍﹥?nèi)容可以不傳遞妻熊,我們可以使用默認(rèn)值扔役,所以選擇對(duì)象傳遞參數(shù)的方式
    // 使用的時(shí)候直接調(diào)用亿胸,傳遞一個(gè)對(duì)象就可以
    ajax({
    url: '', // 請(qǐng)求的地址
    type: '', // 請(qǐng)求方式
    async: '', // 是否異步
    data: '', // 攜帶的參數(shù)
    dataType: '', // 要不要執(zhí)行 json.parse
    success: function () {} // 成功以后執(zhí)行的函數(shù)
    })

  • 確定好使用方式以后预皇,就開(kāi)始書(shū)寫(xiě)封裝函數(shù)

封裝

function ajax(options) {
  // 先準(zhǔn)備一個(gè)默認(rèn)值
  var defInfo = {
    url: '', // 地址不需要默認(rèn)值
    type: 'GET', // 請(qǐng)求方式的默認(rèn)值是 GET
    async: false, // 默認(rèn)值是異步
    data: '', // 參數(shù)沒(méi)有默認(rèn)值
    dataType: 'string', // 默認(rèn)不需要執(zhí)行 json.parse
    success () {}, // 默認(rèn)是一個(gè)函數(shù)
  }
?
  // 先來(lái)判斷一下有沒(méi)有傳遞 url吟温,如果沒(méi)有,直接拋出異常
  if (!options.url) {
    throw new Error('url 必須傳遞')
  }
?
  // 有了 url 以后就潘悼,我們就把用戶(hù)傳遞的參數(shù)和我們的默認(rèn)數(shù)據(jù)合并
  for (let key in options) {
    defInfo[key] = options[key]
  }
?
  // 接下來(lái)的一切我們都是使用我們的 defInfo 就可以了
  // 第一步就是判斷參數(shù) data
  // data 可以不傳遞挥等,可以為空
  // data 也可以是一個(gè) key=value&key=value 格式的字符串
  // data 也可以是一個(gè)對(duì)象
  // 否則就拋出異常
  if (!(typeof defInfo.data === 'string' && /^(\w+=\w+&?)*$/.test(defInfo.data) || Object.prototype.toString.call(defInfo.data) === '[object Object]')) {
    throw new Error('請(qǐng)按照要求傳遞參數(shù)')
  }
?
  // 參數(shù)處理完畢以后肝劲,在判斷 async 的數(shù)據(jù)類(lèi)型
  // 只能傳遞 布爾數(shù)據(jù)類(lèi)型
  if (typeof defInfo.async !== 'boolean') {
    throw new Error('async 參數(shù)只接受布爾數(shù)據(jù)類(lèi)型')
  }
?
  // 在接下來(lái)就判斷 type
  // 請(qǐng)求方式我們只接受 GET 或著 POST
  if (!(defInfo.type.toUpperCase() === 'GET' || defInfo.type.toUpperCase() === 'POST')) {
    throw new Error('目前本插件只接受 GET 和 POST 方式辞槐,請(qǐng)期待更新')
  }
?
  // 接下來(lái)就是判斷 success 的判斷粘室,必須是一個(gè)函數(shù)
  if (Object.prototype.toString.call(defInfo.success) !== '[object Function]') {
    throw new Error('success 只接受函數(shù)數(shù)據(jù)類(lèi)型')
  }
?
  // 參數(shù)都沒(méi)有問(wèn)題了
  // 我們就要把 data 處理一下了
  // 因?yàn)?data 有可能是對(duì)象,當(dāng) data 是一個(gè)對(duì)象的時(shí)候鹿榜,我們要把它轉(zhuǎn)換成一個(gè)字符串
  var str = ''
  if (Object.prototype.toString.call(defInfo.data) === '[object Object]') {
    for (let attr in defInfo.data) {
      str += `${attr}=${defInfo.data[attr]}&`
    }
    str = str.slice(0, -1)
    defInfo.data = str
  }
?
  // 參數(shù)全部驗(yàn)證過(guò)了以后,我們就可以開(kāi)始進(jìn)行正常的 ajax 請(qǐng)求了
  // 1. 準(zhǔn)備一個(gè) ajax 對(duì)象
  //    因?yàn)橐幚砑嫒輪?wèn)題奥裸,所以我們準(zhǔn)備一個(gè)函數(shù)
  function createXHR() {
    if (XMLHttpRequest) {
      return new XMLHttpRequest()
    } else {
      return new ActiveXObject('Microsoft.XMLHTTP')
    }
  }
?
  // 2. 創(chuàng)建一個(gè) ajax 對(duì)象
  var xhr = createXHR()
?
  // 3. 進(jìn)行 open
  xhr.open(defInfo.type, defInfo.url + (defInfo.type.toUpperCase() === 'GET' ? `?${defInfo.data}&_=${new Date().getTime()}` : ''), defInfo.async)
?
  if (defInfo.type.toUpperCase() === 'POST') {
    xhr.setRequestHeader('content-type', 'application/x-www-form-urlencoded')
  }
?
  // 4. 進(jìn)行 send
  xhr.send((defInfo.type.toUpperCase() === 'POST' ? `${defInfo.data}` : ''))
?
  // 5. 接受響應(yīng)
  xhr.onreadystatechange = function () {
    if (xhr.readyState === 4 && /2\d{2}/.test(xhr.status)) {
      // 表示成功湾宙,我們就要執(zhí)行 success
      // 但是要進(jìn)行 dataType 的判斷
      if (defInfo.dataType === 'json') {
        defInfo.success(JSON.parse(xhr.responseText))
      } else {
        defInfo.success()
      }
    }
  }
}

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末侠鳄,一起剝皮案震驚了整個(gè)濱河市伟恶,隨后出現(xiàn)的幾起案子毅该,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,544評(píng)論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件畏线,死亡現(xiàn)場(chǎng)離奇詭異良价,居然都是意外死亡明垢,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,430評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門(mén)抵蚊,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)贞绳,“玉大人致稀,你說(shuō)我怎么就攤上這事∥埽” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 162,764評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵刃永,是天一觀的道長(zhǎng)羹应。 經(jīng)常有香客問(wèn)我,道長(zhǎng)雳刺,這世上最難降的妖魔是什么掖桦? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,193評(píng)論 1 292
  • 正文 為了忘掉前任枪汪,我火速辦了婚禮怔昨,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘赖捌。我一直安慰自己矮烹,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,216評(píng)論 6 388
  • 文/花漫 我一把揭開(kāi)白布卤唉。 她就那樣靜靜地躺著桑驱,像睡著了一般跛蛋。 火紅的嫁衣襯著肌膚如雪问芬。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 51,182評(píng)論 1 299
  • 那天强戴,我揣著相機(jī)與錄音,去河邊找鬼预烙。 笑死道媚,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的谴分。 我是一名探鬼主播镀脂,決...
    沈念sama閱讀 40,063評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼薄翅,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了鼎天?” 一聲冷哼從身側(cè)響起暑竟,我...
    開(kāi)封第一講書(shū)人閱讀 38,917評(píng)論 0 274
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后怀大,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體化借,經(jīng)...
    沈念sama閱讀 45,329評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,543評(píng)論 2 332
  • 正文 我和宋清朗相戀三年铐炫,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了倒信。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片泳梆。...
    茶點(diǎn)故事閱讀 39,722評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡榜掌,死狀恐怖憎账,靈堂內(nèi)的尸體忽然破棺而出卡辰,到底是詐尸還是另有隱情,我是刑警寧澤反砌,帶...
    沈念sama閱讀 35,425評(píng)論 5 343
  • 正文 年R本政府宣布允蚣,位于F島的核電站,受9級(jí)特大地震影響森渐,放射性物質(zhì)發(fā)生泄漏同衣。R本人自食惡果不足惜壶运,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,019評(píng)論 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望埠况。 院中可真熱鬧棵癣,春花似錦、人聲如沸喜命。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,671評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)牌里。三九已至二庵,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間催享,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,825評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工痰憎, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留攀涵,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,729評(píng)論 2 368
  • 正文 我出身青樓蜗细,卻偏偏與公主長(zhǎng)得像怒详,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子吊骤,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,614評(píng)論 2 353