好程式員web前端教育訓練分享JavaScript學習筆記ajax及ajax封裝,ajax 全名 async javascript and XML
是前背景互動的能力
也就是我們用戶端給服務端發送消息的工具,以及接受響應的工具
是一個 預設異步 執行機制的功能
AJAX 的優勢
. 不需要插件的支援,原生 js 就可以使用
. 使用者體驗好(不需要重新整理頁面就可以更新資料)
. 減輕服務端和帶寬的負擔
. 缺點: 搜尋引擎的支援度不夠,因為資料都不在頁面上,搜尋引擎搜尋不到
AJAX 的使用
在 js 中有内置的構造函數來建立 ajax 對象
建立 ajax 對象以後,我們就使用 ajax 對象的方法去發送請求和接受響應
建立一個 ajax 對象
// IE9及以上const xhr = new XMLHttpRequest()// IE9以下const xhr = new ActiveXObject('Mricosoft.XMLHTTP')
上面就是有了一個 ajax 對象
我們就可以使用這個 xhr 對象來發送 ajax 請求了
配置連結資訊
const xhr = new XMLHttpRequest()// xhr 對象中的 open 方法是來配置請求資訊的// 第一個參數是本次請求的請求方式 get / post / put / ...// 第二個參數是本次請求的 url // 第三個參數是本次請求是否異步,預設 true 表示異步,false 表示同步// xhr.open('請求方式', '請求位址', 是否異步)xhr.open('get', './data.php')
上面的代碼執行完畢以後,本次請求的基本配置資訊就寫完了
發送請求
const xhr = new XMLHttpRequest()xhr.open('get', './data.php')// 使用 xhr 對象中的 send 方法來發送請求xhr.send()
上面代碼是把配置好資訊的 ajax 對象發送到服務端
一個基本的 ajax 請求
一個最基本的 ajax 請求就是上面三步
但是光有上面的三個步驟,我們确實能把請求發送的到服務端
如果服務端正常的話,響應也能回到用戶端
但是我們拿不到響應
如果想拿到響應,我們有兩個前提條件
.本次 HTTP 請求是成功的,也就是我們之前說的 http 狀态碼為 200 ~ 299
.ajax 對象也有自己的狀态碼,用來表示本次 ajax 請求中各個階段
ajax 狀态碼
ajax 狀态碼 - xhr.readyState
是用來表示一個 ajax 請求的全部過程中的某一個狀态
readyState === 0: 表示未初始化完成,也就是 open 方法還沒有執行
readyState === 1: 表示配置資訊已經完成,也就是執行完 open 之後
readyState === 2: 表示 send 方法已經執行完成
readyState === 3: 表示正在解析響應内容
readyState === 4: 表示響應内容已經解析完畢,可以在用戶端使用了
這個時候我們就會發現,當一個 ajax 請求的全部過程中,隻有當 readyState === 4 的時候,我們才可以正常使用服務端給我們的資料
是以,配合 http 狀态碼為 200 ~ 299
一個 ajax 對象中有一個成員叫做 xhr.status
這個成員就是記錄本次請求的 http 狀态碼的
兩個條件都滿足的時候,才是本次請求正常完成
readyStateChange
在 ajax 對象中有一個事件,叫做 readyStateChange 事件
這個事件是專門用來監聽 ajax 對象的 readyState 值改變的的行為
也就是說隻要 readyState 的值發生變化了,那麼就會觸發該事件
是以我們就在這個事件中來監聽 ajax 的 readyState 是不是到 4 了
const xhr = new XMLHttpRequest()xhr.open('get', './data.php')xhr.send()xhr.onreadyStateChange = function () {
// 每次 readyState 改變的時候都會觸發該事件 // 我們就在這裡判斷 readyState 的值是不是到 4 // 并且 http 的狀态碼是不是 200 ~ 299 if (xhr.readyState === 4 && /^2d{2|$/.test(xhr.status)) {
// 這裡表示驗證通過 // 我們就可以擷取服務端給我們響應的内容了 }}
responseText
ajax 對象中的 responseText 成員
就是用來記錄服務端給我們的響應體内容的
是以我們就用這個成員來擷取響應體内容就可以
if (xhr.readyState === 4 && /^2d{2|$/.test(xhr.status)) {
// 我們在這裡直接列印 xhr.responseText 來檢視服務端給我們傳回的内容 console.log(xhr.responseText)
}}
使用 ajax 發送請求時攜帶參數
我們使用 ajax 發送請求也是可以攜帶參數的
參數就是和背景互動的時候給他的一些資訊
但是攜帶參數 get 和 post 兩個方式還是有差別的
發送一個帶有參數的 get 請求
get 請求的參數就直接在 url 後面進行拼接就可以
const xhr = new XMLHttpRequest()// 直接在位址後面加一個 ?,然後以 key=value 的形式傳遞// 兩個資料之間以 & 分割xhr.open('get', './data.php?a=100&b=200')xhr.send()
這樣服務端就能接受到兩個參數
一個是 a,值是 100
一個是 b,值是 200
發送一個帶有參數的 post 請求
post 請求的參數是攜帶在請求體中的,是以不需要再 url 後面拼接
const xhr = new XMLHttpRequest()xhr.open('post', './data.php')// 如果是用 ajax 對象發送 post 請求,必須要先設定一下請求頭中的 content-type// 告訴一下服務端我給你的是一個什麼樣子的資料格式xhr.setRequestHeader('content-type', 'application/x-www-form-urlencoded')// 請求體直接再 send 的時候寫在 () 裡面就行// 不需要問号,直接就是 'key=value&key=value' 的形式xhr.send('a=100&b=200')
application/x-www-form-urlencoded 表示的資料格式就是 key=value&key=value
同源政策
同源政策是由浏覽器給的
浏覽器不允許我們向别人發送請求,隻能向自己的伺服器發送請求
當我們想向别人的伺服器發送請求的時候,就會被浏覽器阻止了
什麼是 “别人的伺服器” 呢?
當 請求協定/域名/端口号 有任意一個不同的時候,那麼就算是别人的伺服器
這個時候就會觸發同源政策
我們管觸發了 同源政策 的請求叫做跨域請求
實作一個跨域請求
有的時候我們是需要實作跨域請求的
我們需要多個伺服器給一個頁面提供資料
那麼這個時候我們就要想辦法解決跨域問題
JSONP
jsonp 是我們實作跨域請求的手段,是把我們之前的東西組合在一起使用的技術手段而已
利用的是 script 标簽來實作
script 标簽的本質
浏覽器給我們提供了一個 script 标簽
它的本質就是請求一個外部資源,是不受到同源政策的影響的
同時 script 标簽的 src 屬性,也是一種請求,也能被伺服器接收到
并且:
script标簽的src屬性請求回來的東西是一個字元串,浏覽器會把這個字元串當作 js 代碼來執行
是以我們就可以利用這個 script 标簽的 src 屬性來進行跨域請求了
配置代理(了解)
代理,分成兩種,正向代理和反向代理
正向代理
有一個用戶端需要向一個非同源的伺服器B發送請求
我們搭建一個和用戶端同源的伺服器A
當用戶端發送請求的時候,由伺服器A來接受
再由伺服器A向伺服器B發送請求,因為 同源政策是由浏覽器給的,伺服器之間沒有
伺服器B接受到請求以後,會處理請求,并把響應傳回給伺服器A
再由伺服器A把響應給到用戶端就可以了
我們就可以用這個方式來進行跨域請求了
反向代理
反向代理一般是用來做負載均衡的
當我請求一個伺服器的時候,其實請求的是伺服器端設定的代理伺服器
由代理伺服器把若幹大量的請求分發給不同的伺服器進行處理
再由伺服器把響應給到代理伺服器
代理伺服器傳回給用戶端
封裝 AJAX
ajax 使用起來太麻煩,因為每次都要寫很多的代碼
那麼我們就封裝一個 ajax 方法來讓我們使用起來簡單一些
确定一下使用的方式
因為有一些内容可以不傳遞,我們可以使用預設值,是以選擇對象傳遞參數的方式
// 使用的時候直接調用,傳遞一個對象就可以
ajax({
url: '', // 請求的位址
type: '', // 請求方式
async: '', // 是否異步
data: '', // 攜帶的參數
dataType: '', // 要不要執行 json.parse
success: function () {} // 成功以後執行的函數
})
确定好使用方式以後,就開始書寫封裝函數
封裝
function ajax(options) {
// 先準備一個預設值 var defInfo = {
url: '', // 位址不需要預設值 type: 'GET', // 請求方式的預設值是 GET async: false, // 預設值是異步 data: '', // 參數沒有預設值 dataType: 'string', // 預設不需要執行 json.parse success () {}, // 預設是一個函數 }
// 先來判斷一下有沒有傳遞 url,如果沒有,直接抛出異常 if (!options.url) {
throw new Error('url 必須傳遞')
}
// 有了 url 以後就,我們就把使用者傳遞的參數和我們的預設資料合并 for (let key in options) {
defInfo[key] = options[key]
// 接下來的一切我們都是使用我們的 defInfo 就可以了 // 第一步就是判斷參數 data // data 可以不傳遞,可以為空 // data 也可以是一個 key=value&key=value 格式的字元串 // data 也可以是一個對象 // 否則就抛出異常 if (!(typeof defInfo.data === 'string' && /^(w+=w+&?)*$/.test(defInfo.data) || Object.prototype.toString.call(defInfo.data) === '[object Object]')) {
throw new Error('請按照要求傳遞參數')
// 參數處理完畢以後,在判斷 async 的資料類型 // 隻能傳遞 布爾資料類型 if (typeof defInfo.async !== 'boolean') {
throw new Error('async 參數隻接受布爾資料類型')
// 在接下來就判斷 type // 請求方式我們隻接受 GET 或着 POST if (!(defInfo.type.toUpperCase() === 'GET' || defInfo.type.toUpperCase() === 'POST')) {
throw new Error('目前本插件隻接受 GET 和 POST 方式,請期待更新')
// 接下來就是判斷 success 的判斷,必須是一個函數 if (Object.prototype.toString.call(defInfo.success) !== '[object Function]') {
throw new Error('success 隻接受函數資料類型')
// 參數都沒有問題了 // 我們就要把 data 處理一下了 // 因為 data 有可能是對象,當 data 是一個對象的時候,我們要把它轉換成一個字元串 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
// 參數全部驗證過了以後,我們就可以開始進行正常的 ajax 請求了 // 1. 準備一個 ajax 對象 // 因為要處理相容問題,是以我們準備一個函數 function createXHR() {
if (XMLHttpRequest) {
return new XMLHttpRequest()
} else {
return new ActiveXObject('Microsoft.XMLHTTP')
}
// 2. 建立一個 ajax 對象 var xhr = createXHR()
// 3. 進行 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. 進行 send xhr.send((defInfo.type.toUpperCase() === 'POST' ?
${defInfo.data}
: ''))
// 5. 接受響應 xhr.onreadystatechange = function () {
if (xhr.readyState === 4 && /2\d{2}/.test(xhr.status)) {
// 表示成功,我們就要執行 success // 但是要進行 dataType 的判斷 if (defInfo.dataType === 'json') {
defInfo.success(JSON.parse(xhr.responseText))
} else {
defInfo.success()
}
}
}}