前端 AJAX 全攻略 從把妹角度理解前後端如何和平相處

me
林彥成
2022-09-22 | 4 min.
文章目錄
  1. 1. 什麼是 AJAX?
    1. 1.1. XMLHttpRequest
      1. 1.1.1. jQuery AJAX
    2. 1.2. Promise
      1. 1.2.1. async await
      2. 1.2.2. Fetch
      3. 1.2.3. Axios
  2. 2. AJAX 打 API 避免快取

在前面幾篇文章,小編介紹了什麼是 APIAPI 系統設計方法Web API 實作解析,接下來就來介紹怎麼透過 AJAX 這個技術和後端的 Web API 溝通進行資料交換。

  1. 確認 API 的介面
  2. 用戶端透過瀏覽器發出 AJAX 請求
  3. 伺服器依據請求的方法、內容與資料庫資源整合後進行回應

以男女交往來說重要的溝通是

  • 丟球: 會不會丟球,不會丟球對方就不知道該怎麼和你進一步互動
  • 接球: 對方丟球該怎麼接到,該怎麼看到球飛過來

當後端的 Web API 開好後,前端也要能夠知道該怎麼對後端發出請求,也才能夠做到良好的互動。

什麼是 AJAX?

AJAX(Asynchronous JavaScript And XML),直接用翻譯看就是非同步 JavaScript 與 XML,雖然是寫 XML 但也可以用現今廣泛使用的 JSON 來做資料交換。

因為 JavaScript 單線程一次只能做一件事,在發送請求到伺服器時會需要等待回覆,若這時伺服器一直沒回應,在單線程的情況下其他任務會被卡死,因此也才有了非同步請求,AJAX 可以讓網頁能夠只更新需要的部分,而無須重新載入整個頁面。

XMLHttpRequest

W3C 標準的非同步請求是利用 XMLHttpRequest 寫法:

  • 優點: 簡單直觀所有相關操作都從這個物件去取得
  • 缺點: 結構會不漂亮,所以後來 jQuery AJAX 以原生的標準為基礎,封裝後讓寫法更好

XMLHttpRequest 是一個能夠取得伺服器端的資料的物件,取得資料的方式可以為非同步 (asynchronously) 或同步 (synchronously)。

發出 request,需叫用 HTTP request 類別的 open() 及 send() 兩個方法

  • send(): 發出 request,若為 POST 可以帶入資料
  • open(): request 相關設定,有三個參數
    • 第一個參數為 HTTP request 的方法 (全大寫)
      • GET: 請求資源
      • POST: 新增資源
      • PUT: 取代資源
      • PATCH: 更新資源
      • DELETE: 刪除資源
    • 第二個參數為 URL: 預設會擋 CORS
    • 第三個參數為是否非同步,預設是非同步

發出 request 後,要檢查 request 目前的 readyState。

如果狀態值為 4 代表伺服器已經傳回所有資訊了,readyState 所有可能的值如下:

  • 0: 還沒開始
  • 1: 讀取中
  • 2: 已讀取
  • 3: 資訊交換中
  • 4: 完成
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
var data = null;

var xhr = new XMLHttpRequest();
xhr.withCredentials = true;

xhr.addEventListener("readystatechange", function () {
if (this.readyState === XMLHttpRequest.DONE) {
if (httpRequest.status === 200) {
// 萬事具備
console.log(this.responseText);
}
}
});

xhr.open(
"GET",
"https://mdn.github.io/learning-area/javascript/apis/fetching-data/can-store/products.json"
);
xhr.setRequestHeader("cache-control", "no-cache");

xhr.send(data);

jQuery AJAX

jQuery 的 Ajax 則以原生的標準為基礎,封裝後讓寫法更好,大幅簡化程式且提供多種方法

  • $.get(url,data,callback): get 傳送 data 到 url,並透過 callback 取得執行指令與 url 結果
  • $.post(url,data,callback) 同上,但以 post 傳送 data 到 url
  • $.ajax(setting) 完整的 Ajax 控制語法,setting 為多資料之物件結構陣列
1
2
3
4
5
6
7
8
9
10
11
12
13
var settings = {
async: true,
crossDomain: true,
url: "https://mdn.github.io/learning-area/javascript/apis/fetching-data/can-store/products.json",
method: "GET",
headers: {
"cache-control": "no-cache",
},
};

$.ajax(settings).done(function (response) {
console.log(response);
});

Promise

Promise 是承諾的意思,可以想成不管是否完成都會告知結果。

代表瀏覽器說著:「我承諾我會盡快給予你一個答覆」

Promise 是在 JavaScript 中非同步程式碼的一種新風格樣式,目前已經實作在各大主流瀏覽器中,定義可以看這個網頁

Promise 建立之後,要不成功要不失敗,一個好例子是 fetch API,它基本上就是新一代的 XMLHttpRequest,另外 Async/Await 是 Promise 下一代的解決方案,把 then 取代成 Async/Await 後,就能按照同步的語法撰寫。

1
2
3
4
5
6
7
function asyncFunction(value) {
return new Promise(function (resolve, reject) {
if (value) resolve(value);
// 已實現,成功
else reject(reason); // 有錯誤,已拒絕,失敗
});
}

主要使用 then 及丟進去的 callback 去取值出來,這裡的 callback 就都是非同步執行的喔!!!

1
2
3
asyncFunction("test").then(function (datums) {
console.log(datums);
});

promise.all,適合用在多支 API 要一起執行,並確保全部完成後才進行其他工作時。

1
2
3
Promise.all([promise(1), promise(2), promise(3, 5000)]).then((res) => {
console.log(res);
});

promise.race,適合用在站點不穩定,同時發送多支同行為 API 確保可行性使用。

1
2
3
Promise.race([promise(1), promise(2), promise(3, 5000)]).then((res) => {
console.log(res);
});

promise.allSettled 適合有多個不相互依賴的異步任務才能成功完成 (想知道每個承諾的結果),在所有都已完成或拒絕後完成的 Promise。

1
2
3
4
5
6
7
8
9
const promise1 = Promise.resolve(3);
const promise2 = new Promise((resolve, reject) =>
setTimeout(reject, 100, "fail")
);
const promises = [promise1, promise2];

Promise.allSettled(promises).then((results) =>
results.forEach((result) => console.log(result.status))
);

async await

Promise 在設置上可能會有些複雜並難以理解,因此現代瀏覽器實作出 async 以及 await。

  • async 能夠讓函式執行非同步行為
  • await 可以被用在 async 函式內部,讓程式碼繼續執行前去等待一個 Promise 完成

這能讓程式碼在一連串的 Promise 的情況時更加簡潔易懂,只要加上 await 就可以直接叫用一個返回 Promise 的函數,程式碼就會直接在那裏等待,直到 Promise 被完成。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
async function fetchProducts() {
try {
const response = await fetch(
"https://mdn.github.io/learning-area/javascript/apis/fetching-data/can-store/products.json"
);
if (!response.ok) {
throw new Error(`HTTP:${response.status}`);
}

const json = await response.json();
console.log(json[0].name);
} catch (error) {
console.error(`${error}`);
}
}

const jsonPromise = fetchProducts();
jsonPromise.then((json) => console.log(json[0].name));

從 Promise 改成 async/await 優化了什麼

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
function getData() {
promiseFn(1)
.then((res) => {
console.log(res); // 1, 成功
return promiseFn(2); // 鏈接第二次的 Promise 方法
})
.then((res) => {
console.log(res); // 2, 成功
});
}

async function getData() {
const data1 = await promiseFn(1); // 因為 await,promise 函式被中止直到回傳
const data2 = await promiseFn(2);
console.log(data1, data2); // 1, 成功 2, 成功
}

Fetch

在這裡我們看到 fetch() 帶一個 URL 參數並回傳一個 promise,可以看成是 AJAX 的新版本,詳情可看MDN 的簡介

看寫法的話就知道是已經基於 promise 來設計的,其中要注意的是預設 fetch 不會收或送 cookie 必須設定 credentials 這個參數才可以。

1
2
3
4
5
6
7
8
9
10
11
12
fetch("http://localhost:8080/api/v1/book/1", { method: "get" })
.then(function (response) {
//處理 response
return response.json();
})
.then(function (json) {
let products = json;
initialize(products);
})
.catch(function (err) {
// Error :(
});

Axios

axios 除了整合上面的功能外,提供了簡化的寫法

1
2
3
axios.get("http://localhost:8080/api/v1/book/1").then((res) => {
console.log(res);
});

配置基本設定把一些常用的設定或共用一些需要的防呆實作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
// 基本設定
const instance = axios.create({
baseURL: "https://some-domain.com/api/",
timeout: 1000,
headers: { "X-Custom-Header": "foobar" },
});

// request interceptor 統一處理邏輯
axios.interceptors.request.use(
function (config) {
// request 送出前
return config;
},
function (error) {
// request 錯誤
return Promise.reject(error);
}
);

// response interceptor 處理回應
axios.interceptors.response.use(
function (response) {
// 可統一針對各種 Status Code 處理
return response;
},
function (error) {
// 統一錯誤處理
return Promise.reject(error);
}
);

Vue.js 的文件也有介紹使用這套函式庫,透過配置將 client 和vue 的 instance 綁在一起,可以直接透過this.$http來叫用。

AJAX 打 API 避免快取

將時間或亂數以適當的 ?& 加在 URL 後方就可以做到避免快取且跨瀏覽器相容。

例如:

1
2
3
4
5
6
7
// 不快取網頁
const cachedURL = "http://test.com/index.html";
const unCachedURL = "http://test.com/index.html?12345";

// 不快取 API
const cachedAPI = "http://test.com/api/demo?name=test";
const unCachedAPI = "http://test.com/api/demo?name=test&12345";

喜歡這篇文章,請幫忙拍拍手喔 🤣


share