jQuery Ajax

Ajax(Asynchronous JavaScript and XML)是一種在不重新載入整個頁面的情況下,與伺服器交換資料並更新部分網頁內容的技術。jQuery 提供了簡潔且功能強大的 Ajax 方法。

$.ajax() - 核心方法

$.ajax() 是 jQuery Ajax 的核心方法,其他簡便方法都是基於它的封裝。

基本語法

$.ajax({
  url: '/api/data',
  method: 'GET',
  success: function (data) {
    console.log('成功:', data);
  },
  error: function (xhr, status, error) {
    console.log('錯誤:', error);
  },
});

完整範例

$.ajax({
  url: '/api/users',
  method: 'POST',
  data: {
    name: 'John',
    email: 'john@example.com',
  },
  dataType: 'json',
  contentType: 'application/x-www-form-urlencoded',
  timeout: 5000,
  headers: {
    'X-Custom-Header': 'value',
  },
  beforeSend: function (xhr) {
    console.log('即將發送請求');
  },
  success: function (data, status, xhr) {
    console.log('成功:', data);
  },
  error: function (xhr, status, error) {
    console.log('錯誤:', error);
  },
  complete: function (xhr, status) {
    console.log('請求完成');
  },
});

常用設定選項

選項說明
url請求的 URL
method / typeHTTP 方法(GET、POST、PUT、DELETE 等)
data發送的資料
dataType預期的回應資料類型(json、xml、html、text、script)
contentType發送資料的內容類型
headers自訂請求標頭
timeout超時時間(毫秒)
cache是否快取(預設 true,dataType 為 script/jsonp 時為 false)
async是否非同步(預設 true,強烈建議不要設為 false)
processData是否自動將 data 轉為查詢字串(預設 true)

回呼函式

回呼說明
beforeSend(xhr)發送請求前呼叫
success(data, status, xhr)請求成功時呼叫
error(xhr, status, error)請求失敗時呼叫
complete(xhr, status)請求完成時呼叫(不論成功或失敗)

簡便方法

$.get()

發送 GET 請求:

// 基本用法
$.get('/api/users', function (data) {
  console.log(data);
});

// 帶參數
$.get('/api/users', { page: 1, limit: 10 }, function (data) {
  console.log(data);
});

// 指定資料類型
$.get(
  '/api/users',
  { page: 1 },
  function (data) {
    console.log(data);
  },
  'json'
);

$.post()

發送 POST 請求:

// 基本用法
$.post('/api/users', { name: 'John', email: 'john@example.com' }, function (data) {
  console.log('已建立:', data);
});

// 指定資料類型
$.post(
  '/api/users',
  formData,
  function (data) {
    console.log(data);
  },
  'json'
);

$.getJSON()

發送 GET 請求並預期 JSON 回應:

$.getJSON('/api/users', function (data) {
  console.log(data); // 已自動解析為 JavaScript 物件
});

// 帶參數
$.getJSON('/api/users', { id: 123 }, function (data) {
  console.log(data);
});

$.getScript()

載入並執行 JavaScript 檔案:

$.getScript('/js/plugin.js', function () {
  console.log('Script 已載入並執行');
  // 現在可以使用 plugin.js 中定義的函式
});

load()

載入 HTML 並插入到元素中:

// 載入整個檔案
$('#content').load('/pages/about.html');

// 載入檔案的部分內容
$('#content').load('/pages/about.html #main');

// 帶回呼函式
$('#content').load('/pages/about.html', function (response, status, xhr) {
  if (status === 'error') {
    console.log('載入失敗');
  }
});

// 帶資料(會改用 POST)
$('#content').load('/pages/about.html', { section: 'intro' });

jqXHR 與 Promise

從 jQuery 1.5 開始,所有 Ajax 方法都返回一個 jqXHR 物件,它實作了 Promise 介面。

Promise 方法

var jqxhr = $.ajax('/api/users');

// 成功時
jqxhr.done(function (data, status, xhr) {
  console.log('成功:', data);
});

// 失敗時
jqxhr.fail(function (xhr, status, error) {
  console.log('失敗:', error);
});

// 完成時(不論成功或失敗)
jqxhr.always(function () {
  console.log('請求完成');
});

鏈式寫法

$.ajax('/api/users')
  .done(function (data) {
    console.log('成功:', data);
  })
  .fail(function (xhr, status, error) {
    console.log('失敗:', error);
  })
  .always(function () {
    console.log('完成');
  });

使用 then()

$.ajax('/api/users').then(
  function (data) {
    // 成功
    console.log('成功:', data);
  },
  function (xhr, status, error) {
    // 失敗
    console.log('失敗:', error);
  }
);

Promise 鏈

$.ajax('/api/user/1')
  .then(function (user) {
    console.log('使用者:', user);
    return $.ajax('/api/posts?userId=' + user.id);
  })
  .then(function (posts) {
    console.log('文章:', posts);
    return $.ajax('/api/comments?postId=' + posts[0].id);
  })
  .then(function (comments) {
    console.log('留言:', comments);
  })
  .fail(function (error) {
    console.log('發生錯誤:', error);
  });

$.when() - 並行請求

同時發送多個請求,等待全部完成:

$.when($.ajax('/api/users'), $.ajax('/api/posts'), $.ajax('/api/comments'))
  .done(function (usersResult, postsResult, commentsResult) {
    // 每個結果是 [data, status, xhr] 陣列
    var users = usersResult[0];
    var posts = postsResult[0];
    var comments = commentsResult[0];

    console.log('使用者:', users);
    console.log('文章:', posts);
    console.log('留言:', comments);
  })
  .fail(function () {
    console.log('至少一個請求失敗');
  });

表單序列化

serialize()

將表單資料序列化為查詢字串格式:

// 結果如:name=John&email=john%40example.com
var queryString = $('form').serialize();

// 用於 Ajax
$.post('/api/submit', $('form').serialize(), function (response) {
  console.log(response);
});

serializeArray()

將表單資料序列化為物件陣列:

var formData = $('form').serializeArray();
// [{name: "field1", value: "value1"}, {name: "field2", value: "value2"}]

// 轉換為物件
var formObject = {};
$.each($('form').serializeArray(), function (i, field) {
  formObject[field.name] = field.value;
});

Ajax 事件

局部事件

透過 $.ajax() 的回呼函式設定:

$.ajax({
  url: '/api/data',
  beforeSend: function () {
    console.log('發送前');
  },
  success: function (data) {
    console.log('成功');
  },
  error: function () {
    console.log('錯誤');
  },
  complete: function () {
    console.log('完成');
  },
});

全域事件

在任何元素上綁定,所有 Ajax 請求都會觸發:

// 任何 Ajax 請求開始時
$(document).on('ajaxStart', function () {
  $('#loading').show();
});

// 任何 Ajax 請求完成時
$(document).on('ajaxStop', function () {
  $('#loading').hide();
});

// 其他全域事件
$(document).on('ajaxSend', function (event, xhr, settings) {
  console.log('即將發送:', settings.url);
});

$(document).on('ajaxSuccess', function (event, xhr, settings, data) {
  console.log('成功:', settings.url);
});

$(document).on('ajaxError', function (event, xhr, settings, error) {
  console.log('錯誤:', settings.url, error);
});

$(document).on('ajaxComplete', function (event, xhr, settings) {
  console.log('完成:', settings.url);
});

事件觸發順序

  1. ajaxStart(第一個請求開始時)
  2. beforeSend(局部)
  3. ajaxSend(全域)
  4. successerror(局部)
  5. ajaxSuccessajaxError(全域)
  6. complete(局部)
  7. ajaxComplete(全域)
  8. ajaxStop(所有請求完成時)

關閉全域事件

$.ajax({
  url: '/api/data',
  global: false, // 不觸發全域事件
  success: function (data) {
    console.log(data);
  },
});

發送 JSON 資料

$.ajax({
  url: '/api/users',
  method: 'POST',
  contentType: 'application/json',
  data: JSON.stringify({
    name: 'John',
    email: 'john@example.com',
  }),
  success: function (data) {
    console.log(data);
  },
});

發送 FormData(檔案上傳)

var formData = new FormData();
formData.append('file', $('#fileInput')[0].files[0]);
formData.append('name', 'document');

$.ajax({
  url: '/api/upload',
  method: 'POST',
  data: formData,
  processData: false, // 不處理資料
  contentType: false, // 不設定內容類型
  success: function (data) {
    console.log('上傳成功:', data);
  },
});

全域設定

$.ajaxSetup()

設定所有 Ajax 請求的預設值:

$.ajaxSetup({
  headers: {
    Authorization: 'Bearer ' + token,
  },
  timeout: 10000,
  dataType: 'json',
});

// 之後的請求都會使用這些預設值
$.get('/api/users', function (data) {
  console.log(data);
});
注意$.ajaxSetup() 會影響所有 Ajax 請求,包括第三方程式碼的請求,請謹慎使用。

錯誤處理

$.ajax({
  url: '/api/data',
  method: 'GET',
})
  .done(function (data) {
    console.log('成功:', data);
  })
  .fail(function (xhr, status, error) {
    // status 可能的值:timeout, error, abort, parsererror
    console.log('狀態碼:', xhr.status);
    console.log('狀態文字:', xhr.statusText);
    console.log('錯誤類型:', status);
    console.log('錯誤訊息:', error);

    // 嘗試解析錯誤回應
    try {
      var response = JSON.parse(xhr.responseText);
      console.log('伺服器錯誤訊息:', response.message);
    } catch (e) {
      console.log('回應內容:', xhr.responseText);
    }
  });

跨域請求 (CORS)

瀏覽器的同源政策會阻止跨域請求。若伺服器支援 CORS,jQuery Ajax 可以正常運作:

// 伺服器需要設定正確的 CORS 標頭
$.ajax({
  url: 'https://api.example.com/data',
  method: 'GET',
  xhrFields: {
    withCredentials: true, // 如需傳送 cookies
  },
  success: function (data) {
    console.log(data);
  },
});

JSONP(舊式跨域方案)

$.ajax({
  url: 'https://api.example.com/data',
  dataType: 'jsonp',
  jsonpCallback: 'handleResponse',
  success: function (data) {
    console.log(data);
  },
});
JSONP 只支援 GET 請求,且存在安全風險。現代應用應優先使用 CORS。

實用範例

載入更多

var page = 1;
var loading = false;

$('#loadMore').on('click', function () {
  if (loading) return;

  loading = true;
  $(this).text('載入中...');

  $.get('/api/posts', { page: ++page }, function (posts) {
    $.each(posts, function (i, post) {
      $('#posts').append('<div class="post">' + post.title + '</div>');
    });
  }).always(function () {
    loading = false;
    $('#loadMore').text('載入更多');
  });
});

自動儲存

var saveTimer;

$('textarea').on('input', function () {
  clearTimeout(saveTimer);

  saveTimer = setTimeout(function () {
    $.post('/api/save', {
      content: $('textarea').val(),
    }).done(function () {
      $('#status').text('已儲存').fadeIn().delay(1000).fadeOut();
    });
  }, 1000); // 停止輸入 1 秒後自動儲存
});

搜尋建議

var searchTimer;

$('#search').on('input', function () {
  var query = $(this).val();

  clearTimeout(searchTimer);

  if (query.length < 2) {
    $('#suggestions').hide();
    return;
  }

  searchTimer = setTimeout(function () {
    $.get('/api/search', { q: query }, function (results) {
      var $suggestions = $('#suggestions').empty();

      $.each(results, function (i, item) {
        $suggestions.append('<div class="suggestion">' + item + '</div>');
      });

      $suggestions.show();
    });
  }, 300);
});