Полное руководство: AJAX-запросы без jQuery для начинающих
Освойте AJAX на JavaScript с руководством по XMLHttpRequest и Fetch API. Узнайте лучшие практики, обработку ошибок и способы оптимизации производительности.
Как можно выполнить AJAX‑запрос, используя чистый JavaScript без jQuery? Какие существуют методы и лучшие практики реализации AJAX‑функционала в ванильном JavaScript?
Вы можете выполнять AJAX‑запросы с помощью чистого JavaScript без jQuery, используя либо объект XMLHttpRequest для обратной совместимости, либо современный API Fetch. Оба подхода имеют свои преимущества: XMLHttpRequest обеспечивает более широкую совместимость и более детальный контроль, а Fetch предлагает более чистый синтаксис с обработкой на основе промисов, что делает код более читаемым и поддерживаемым.
Содержание
- XMLHttpRequest: традиционный подход
- Fetch API: современное решение
- Лучшие практики для Vanilla AJAX
- Стратегии обработки ошибок
- Продвинутые техники и шаблоны
- Совместимость с браузерами
- Советы по оптимизации производительности
XMLHttpRequest: традиционный подход
Объект XMLHttpRequest является традиционным способом реализации AJAX‑функциональности в JavaScript и остаётся широко поддерживаемым во всех браузерах. Как объясняет Mozilla Developer Network, «XMLHttpRequest представляет собой простой способ получить данные с URL без полной перезагрузки страницы».
Базовая реализация XMLHttpRequest
Ниже приведён простой GET‑запрос с использованием XMLHttpRequest:
const xhr = new XMLHttpRequest();
xhr.open('GET', 'https://api.example.com/data', true);
xhr.onload = function() {
if (xhr.status >= 200 && xhr.status < 300) {
const data = JSON.parse(xhr.responseText);
console.log('Success:', data);
} else {
console.error('Request failed with status:', xhr.status);
}
};
xhr.onerror = function() {
console.error('Network error occurred');
};
xhr.send();
XMLHttpRequest для разных HTTP‑методов
POST‑запросы с данными:
const xhr = new XMLHttpRequest();
xhr.open('POST', 'https://api.example.com/submit', true);
xhr.setRequestHeader('Content-Type', 'application/json');
xhr.onload = function() {
if (xhr.status >= 200 && xhr.status < 300) {
const response = JSON.parse(xhr.responseText);
console.log('Submission successful:', response);
}
};
xhr.send(JSON.stringify({ name: 'John', email: 'john@example.com' }));
Отслеживание прогресса:
Согласно руководству SitePoint, вы можете отслеживать прогресс AJAX‑запроса с помощью обработчика события onprogress:
const xhr = new XMLHttpRequest();
xhr.open('GET', 'https://api.example.com/large-file', true);
xhr.onprogress = function(event) {
if (event.lengthComputable) {
const percentComplete = (event.loaded / event.total) * 100;
console.log('Download progress: ' + percentComplete + '%');
}
};
xhr.send();
Fetch API: современное решение
API Fetch, введённый с ES6 (ECMAScript 2015), предоставляет более современный и упрощённый подход к выполнению AJAX‑запросов. Как отмечает Go Make Things, «Fetch полностью поддерживается в Deno и Node 18, позволяя использовать один и тот же API как на сервере, так и на клиенте».
Базовая реализация Fetch
fetch('https://api.example.com/data')
.then(response => {
if (!response.ok) {
throw new Error('Network response was not ok');
}
return response.json();
})
.then(data => {
console.log('Success:', data);
})
.catch(error => {
console.error('Fetch error:', error);
});
Fetch с async/await
async function fetchData() {
try {
const response = await fetch('https://api.example.com/data');
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const data = await response.json();
console.log('Success:', data);
} catch (error) {
console.error('Fetch error:', error);
}
}
fetchData();
POST‑запрос с Fetch
async function postData() {
try {
const response = await fetch('https://api.example.com/submit', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
name: 'John',
email: 'john@example.com'
})
});
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const result = await response.json();
console.log('Submission successful:', result);
} catch (error) {
console.error('Fetch error:', error);
}
}
Лучшие практики для Vanilla AJAX
1. Всегда обрабатывайте ошибки
Для XMLHttpRequest:
function makeRequest(url, method = 'GET', data = null) {
return new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest();
xhr.open(method, url, true);
xhr.onload = function() {
if (xhr.status >= 200 && xhr.status < 300) {
try {
const response = xhr.responseText ? JSON.parse(xhr.responseText) : null;
resolve(response);
} catch (parseError) {
reject(new Error('Failed to parse response'));
}
} else {
reject(new Error(`Request failed with status ${xhr.status}`));
}
};
xhr.onerror = function() {
reject(new Error('Network error occurred'));
};
xhr.send(data ? JSON.stringify(data) : null);
});
}
Для Fetch API:
async function safeFetch(url, options = {}) {
try {
const response = await fetch(url, {
...options,
headers: {
'Content-Type': 'application/json',
...options.headers
}
});
if (!response.ok) {
const errorData = await response.json().catch(() => ({}));
throw new Error(errorData.message || `HTTP ${response.status}`);
}
const contentType = response.headers.get('content-type');
if (contentType && contentType.includes('application/json')) {
return await response.json();
}
return await response.text();
} catch (error) {
console.error('Fetch error:', error);
throw error;
}
}
2. Создавайте переиспользуемые утилиты AJAX
Как предлагает Ibrahim Diallo, можно обернуть код в объект Ajax и использовать его во всём проекте.
const Ajax = {
xhr: null,
request: function(url, method, data, success, failure) {
if (!this.xhr) {
this.xhr = new XMLHttpRequest();
}
this.xhr.open(method, url, true);
this.xhr.setRequestHeader('Content-Type', 'application/json');
this.xhr.onload = function() {
if (this.status >= 200 && this.status < 300) {
try {
const response = this.responseText ? JSON.parse(this.responseText) : null;
success(response);
} catch (e) {
failure('Invalid JSON response');
}
} else {
failure(`Request failed with status ${this.status}`);
}
};
this.xhr.onerror = function() {
failure('Network error occurred');
};
this.xhr.send(data ? JSON.stringify(data) : null);
},
get: function(url, success, failure) {
this.request(url, 'GET', null, success, failure);
},
post: function(url, data, success, failure) {
this.request(url, 'POST', data, success, failure);
}
};
3. Используйте перехватчики запросов/ответов
class HTTPClient {
constructor(baseURL = '') {
this.baseURL = baseURL;
}
async request(endpoint, options = {}) {
const url = `${this.baseURL}${endpoint}`;
const defaultOptions = {
headers: {
'Content-Type': 'application/json',
},
};
const config = { ...defaultOptions, ...options };
// Request interceptor
if (this.requestInterceptor) {
const interceptedConfig = await this.requestInterceptor(config);
config.headers = { ...config.headers, ...interceptedConfig.headers };
}
try {
const response = await fetch(url, config);
// Response interceptor
let data;
if (response.ok) {
data = await this.parseResponse(response);
} else {
data = await this.parseResponse(response);
throw new Error(data.message || `HTTP ${response.status}`);
}
if (this.responseInterceptor) {
return await this.responseInterceptor(data);
}
return data;
} catch (error) {
if (this.errorInterceptor) {
return await this.errorInterceptor(error);
}
throw error;
}
}
parseResponse(response) {
const contentType = response.headers.get('content-type');
if (contentType && contentType.includes('application/json')) {
return response.json();
}
return response.text();
}
get(endpoint, options = {}) {
return this.request(endpoint, { ...options, method: 'GET' });
}
post(endpoint, data, options = {}) {
return this.request(endpoint, {
...options,
method: 'POST',
body: JSON.stringify(data)
});
}
// Set interceptors
setRequestInterceptor(interceptor) {
this.requestInterceptor = interceptor;
}
setResponseInterceptor(interceptor) {
this.responseInterceptor = interceptor;
}
setErrorInterceptor(interceptor) {
this.errorInterceptor = interceptor;
}
}
Стратегии обработки ошибок
Полноценная обработка ошибок для XMLHttpRequest
function robustXHR(url, options = {}) {
return new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest();
const {
method = 'GET',
data = null,
timeout = 10000,
headers = {}
} = options;
xhr.open(method, url, true);
// Установка заголовков
Object.entries(headers).forEach(([key, value]) => {
xhr.setRequestHeader(key, value);
});
// Таймаут
xhr.timeout = timeout;
xhr.ontimeout = function() {
reject(new Error('Request timeout'));
};
// Отслеживание прогресса
if (options.onProgress) {
xhr.onprogress = options.onProgress;
}
// Handlers
xhr.onload = function() {
try {
if (xhr.status >= 200 && xhr.status < 300) {
const contentType = xhr.getResponseHeader('content-type');
let response;
if (contentType && contentType.includes('application/json')) {
response = JSON.parse(xhr.responseText);
} else {
response = xhr.responseText;
}
resolve({
data: response,
status: xhr.status,
headers: parseHeaders(xhr.getAllResponseHeaders())
});
} else {
let errorData;
try {
errorData = JSON.parse(xhr.responseText);
} catch (e) {
errorData = { message: xhr.statusText };
}
reject({
error: errorData,
status: xhr.status,
headers: parseHeaders(xhr.getAllResponseHeaders())
});
}
} catch (parseError) {
reject({
error: { message: 'Failed to parse response' },
status: xhr.status,
originalError: parseError
});
}
};
xhr.onerror = function() {
reject({
error: { message: 'Network error occurred' },
status: 0
});
};
xhr.onabort = function() {
reject({
error: { message: 'Request was aborted' },
status: 0
});
};
xhr.send(data);
});
}
function parseHeaders(headerString) {
const headers = {};
if (!headerString) return headers;
const headerPairs = headerString.split('\u000d\u000a');
for (let i = 0; i < headerPairs.length; i++) {
const headerPair = headerPairs[i];
const index = headerPair.indexOf('\u003a\u0020');
if (index > 0) {
const key = headerPair.substring(0, index);
const value = headerPair.substring(index + 2);
headers[key] = value;
}
}
return headers;
}
Продвинутая обработка ошибок для Fetch API
class FetchError extends Error {
constructor(message, status, response) {
super(message);
this.name = 'FetchError';
this.status = status;
this.response = response;
}
}
async function enhancedFetch(url, options = {}) {
const {
method = 'GET',
headers = {},
body,
timeout = 10000,
retries = 3,
retryDelay = 1000
} = options;
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), timeout);
try {
for (let attempt = 0; attempt < retries; attempt++) {
try {
const response = await fetch(url, {
method,
headers,
body,
signal: controller.signal
});
clearTimeout(timeoutId);
if (!response.ok) {
let errorData;
try {
errorData = await response.json();
} catch (e) {
errorData = await response.text();
}
throw new FetchError(
errorData.message || `HTTP ${response.status}`,
response.status,
errorData
);
}
const contentType = response.headers.get('content-type');
let data;
if (contentType && contentType.includes('application/json')) {
data = await response.json();
} else {
data = await response.text();
}
return {
data,
status: response.status,
headers: Object.fromEntries(response.headers.entries())
};
} catch (error) {
if (attempt === retries - 1) {
throw error;
}
await new Promise(resolve => setTimeout(resolve, retryDelay * (attempt + 1)));
}
}
} finally {
clearTimeout(timeoutId);
}
}
Продвинутые техники и шаблоны
1. AJAX с кэшированием
class CachedHTTPClient {
constructor(cacheSize = 100, ttl = 5 * 60 * 1000) { // 5 минут по умолчанию
this.cache = new Map();
this.cacheSize = cacheSize;
this.ttl = ttl;
}
async request(url, options = {}) {
const cacheKey = this.generateCacheKey(url, options);
const cached = this.cache.get(cacheKey);
if (cached && Date.now() - cached.timestamp < this.ttl) {
return cached.data;
}
const data = await this.fetchWithCache(url, options);
this.cache.set(cacheKey, {
data,
timestamp: Date.now()
});
// Сохраняем размер кэша
if (this.cache.size > this.cacheSize) {
const oldestKey = this.cache.keys().next().value;
this.cache.delete(oldestKey);
}
return data;
}
generateCacheKey(url, options) {
const sortedOptions = Object.keys(options)
.sort()
.reduce((obj, key) => {
obj[key] = options[key];
return obj;
}, {});
return `${url}_${JSON.stringify(sortedOptions)}`;
}
async fetchWithCache(url, options) {
// Реализация с использованием предпочитаемого метода fetch
return enhancedFetch(url, options);
}
}
2. Бatching запросов
class BatchRequestManager {
constructor(batchSize = 5, batchDelay = 100) {
this.batchSize = batchSize;
this.batchDelay = batchDelay;
this.pendingRequests = [];
this.batchTimer = null;
}
async addRequest(requestConfig) {
return new Promise((resolve, reject) => {
this.pendingRequests.push({
config: requestConfig,
resolve,
reject
});
if (this.pendingRequests.length >= this.batchSize) {
this.processBatch();
} else if (!this.batchTimer) {
this.batchTimer = setTimeout(() => {
this.processBatch();
}, this.batchDelay);
}
});
}
async processBatch() {
if (this.batchTimer) {
clearTimeout(this.batchTimer);
this.batchTimer = null;
}
const batch = this.pendingRequests.splice(0, this.batchSize);
try {
// Можно оптимизировать для параллельных запросов
const results = await Promise.all(
batch.map(async (item) => {
try {
const result = await enhancedFetch(item.config.url, item.config.options);
item.resolve(result);
} catch (error) {
item.reject(error);
}
})
);
} catch (error) {
batch.forEach(item => item.reject(error));
}
// Обрабатываем оставшиеся запросы, если есть
if (this.pendingRequests.length > 0) {
this.batchTimer = setTimeout(() => {
this.processBatch();
}, this.batchDelay);
}
}
}
3. AJAX с TypeScript‑подобным интерфейсом
interface RequestOptions {
method?: 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH';
headers?: Record<string, string>;
body?: any;
timeout?: number;
retries?: number;
retryDelay?: number;
}
interface Response<T = any> {
data: T;
status: number;
headers: Record<string, string>;
}
interface APIError {
message: string;
status?: number;
response?: any;
originalError?: Error;
}
class APIClient {
private baseURL: string;
private defaultHeaders: Record<string, string>;
constructor(baseURL: string = '', defaultHeaders: Record<string, string> = {}) {
this.baseURL = baseURL;
this.defaultHeaders = {
'Content-Type': 'application/json',
...defaultHeaders
};
}
async request<T = any>(endpoint: string, options: RequestOptions = {}): Promise<Response<T>> {
const url = `${this.baseURL}${endpoint}`;
const {
method = 'GET',
headers = {},
body,
timeout = 10000,
retries = 3,
retryDelay = 1000
} = options;
const mergedHeaders = { ...this.defaultHeaders, ...headers };
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), timeout);
try {
for (let attempt = 0; attempt < retries; attempt++) {
try {
const response = await fetch(url, {
method,
headers: mergedHeaders,
body: body ? JSON.stringify(body) : undefined,
signal: controller.signal
});
clearTimeout(timeoutId);
if (!response.ok) {
let errorData;
try {
errorData = await response.json();
} catch (e) {
errorData = await response.text();
}
throw {
message: errorData.message || `HTTP ${response.status}`,
status: response.status,
response: errorData
};
}
const contentType = response.headers.get('content-type');
let data: T;
if (contentType && contentType.includes('application/json')) {
data = await response.json();
} else {
data = await response.text() as T;
}
return {
data,
status: response.status,
headers: Object.fromEntries(response.headers.entries())
};
} catch (error) {
if (attempt === retries - 1) {
throw error;
}
await new Promise(resolve => setTimeout(resolve, retryDelay * (attempt + 1)));
}
}
} finally {
clearTimeout(timeoutId);
}
}
async get<T = any>(endpoint: string, options: Omit<RequestOptions, 'method'> = {}): Promise<Response<T>> {
return this.request<T>(endpoint, { ...options, method: 'GET' });
}
async post<T = any>(endpoint: string, data: any, options: Omit<RequestOptions, 'method' | 'body'> = {}): Promise<Response<T>> {
return this.request<T>(endpoint, { ...options, method: 'POST', body: data });
}
async put<T = any>(endpoint: string, data: any, options: Omit<RequestOptions, 'method' | 'body'> = {}): Promise<Response<T>> {
return this.request<T>(endpoint, { ...options, method: 'PUT', body: data });
}
async delete<T = any>(endpoint: string, options: Omit<RequestOptions, 'method'> = {}): Promise<Response<T>> {
return this.request<T>(endpoint, { ...options, method: 'DELETE' });
}
}
Совместимость с браузерами
Совместимость XMLHttpRequest
XMLHttpRequest имеет отличную поддержку во всех современных браузерах и даже в старых, таких как Internet Explorer 8 и выше. Как отмечает Mozilla Developer Network, «Запрос, выполненный через XMLHttpRequest, может получать данные как асинхронно, так и синхронно».
Совместимость Fetch API
Fetch API поддерживается:
- Во всех современных браузерах (Chrome, Firefox, Safari, Edge)
- В Internet Explorer нет нативной поддержки
Fetch - В Node.js 18+ поддерживается
Fetch
Для старых браузеров может потребоваться polyfill:
<!-- Для браузеров, которые не поддерживают Fetch API -->
<script src="https://unpkg.com/whatwg-fetch@3.6.2/dist/fetch.umd.js"></script>
Обнаружение возможностей
function isFetchSupported() {
return 'fetch' in window && typeof window.fetch === 'function';
}
function isXHRSupported() {
return 'XMLHttpRequest' in window && typeof window.XMLHttpRequest === 'function';
}
function getBestAvailableMethod() {
if (isFetchSupported()) {
return 'fetch';
} else if (isXHRSupported()) {
return 'xhr';
} else {
throw new Error('No AJAX method available in this browser');
}
}
// Использование
const ajaxMethod = getBestAvailableMethod();
console.log('Using AJAX method:', ajaxMethod);
Прогрессивное улучшение
class ProgressiveAjax {
constructor() {
this.method = this.determineBestMethod();
}
determineBestMethod() {
try {
if (window.fetch) {
return 'fetch';
} else if (window.XMLHttpRequest) {
return 'xhr';
} else {
throw new Error('No AJAX support available');
}
} catch (e) {
console.error('AJAX not supported:', e);
return null;
}
}
async request(url, options = {}) {
if (!this.method) {
throw new Error('AJAX not supported in this browser');
}
if (this.method === 'fetch') {
return this.fetchRequest(url, options);
} else {
return this.xhrRequest(url, options);
}
}
async fetchRequest(url, options) {
const fetchOptions = {
method: options.method || 'GET',
headers: options.headers || {},
body: options.body ? JSON.stringify(options.body) : undefined
};
const response = await fetch(url, fetchOptions);
if (!response.ok) {
throw new Error(`HTTP ${response.status}`);
}
return response.json();
}
xhrRequest(url, options) {
return new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest();
xhr.open(options.method || 'GET', url, true);
// Установка заголовков
Object.entries(options.headers || {}).forEach(([key, value]) => {
xhr.setRequestHeader(key, value);
});
xhr.onload = function() {
if (xhr.status >= 200 && xhr.status < 300) {
try {
resolve(JSON.parse(xhr.responseText));
} catch (e) {
reject(new Error('Invalid JSON response'));
}
} else {
reject(new Error(`HTTP ${xhr.status}`));
}
};
xhr.onerror = function() {
reject(new Error('Network error'));
};
xhr.send(options.body ? JSON.stringify(options.body) : null);
});
}
}
Советы по оптимизации производительности
1. Debounce запросов
class DebouncedRequest {
constructor(delay = 300) {
this.delay = delay;
this.timeout = null;
this.pendingRequest = null;
}
async request(requestFunction, ...args) {
return new Promise((resolve, reject) => {
if (this.timeout) {
clearTimeout(this.timeout);
}
this.pendingRequest = { resolve, reject };
this.timeout = setTimeout(async () => {
try {
const result = await requestFunction(...args);
this.pendingRequest.resolve(result);
} catch (error) {
this.pendingRequest.reject(error);
} finally {
this.timeout = null;
this.pendingRequest = null;
}
}, this.delay);
});
}
}
// Использование
const debouncedFetch = new DebouncedRequest(500);
// Множественные быстрые вызовы приведут к одному фактическому запросу
debouncedFetch.fetch('/api/data');
debouncedFetch.fetch('/api/data');
debouncedFetch.fetch('/api/data');
2. Стратегия кэширования запросов
class RequestCache {
constructor(maxSize = 100, ttl = 5 * 60 * 1000) {
this.cache = new Map();
this.maxSize = maxSize;
this.ttl = ttl;
}
get(key) {
const item = this.cache.get(key);
if (item && Date.now() - item.timestamp < this.ttl) {
return item.data;
}
return null;
}
set(key, data) {
if (this.cache.size >= this.maxSize) {
// Удаляем старейший элемент
const oldestKey = this.cache.keys().next().value;
this.cache.delete(oldestKey);
}
this.cache.set(key, {
data,
timestamp: Date.now()
});
}
clear() {
this.cache.clear();
}
generateKey(url, options) {
const sortedOptions = Object.keys(options || {})
.sort()
.reduce((obj, key) => {
obj[key] = options[key];
return obj;
}, {});
return `${url}_${JSON.stringify(sortedOptions)}`;
}
}
3. Обработка запросов с учётом состояния соединения
class ConnectionAwareHTTP {
constructor() {
this.connection = navigator.connection || navigator.mozConnection || navigator.webkitConnection;
this.updateConnectionInfo();
// Слушаем изменения соединения
if (this.connection) {
this.connection.addEventListener('change', () => {
this.updateConnectionInfo();
});
}
}
updateConnectionInfo() {
this.effectiveType = this.connection ? this.connection.effectiveType : '4g';
this.downlink = this.connection ? this.connection.downlink : 10;
this.rtt = this.connection ? this.connection.rtt : 100;
}
async request(url, options = {}) {
const adjustedOptions = this.adjustOptionsForConnection(options);
return enhancedFetch(url, adjustedOptions);
}
adjustOptionsForConnection(options) {
const adjustedOptions = { ...options };
if (this.effectiveType === 'slow-2g' || this.effectiveType === '2g') {
// Для медленных соединений уменьшаем таймауты и включаем retries
adjustedOptions.timeout = 30000; // 30 секунд
adjustedOptions.retries = 5;
adjustedOptions.retryDelay = 2000;
// Можно уменьшить размер данных
if (options.body && typeof options.body === 'object') {
// Здесь можно реализовать сжатие данных
}
} else if (this.effectiveType === '4g') {
// Для быстрых соединений используем агрессивные таймауты
adjustedOptions.timeout = 5000;
adjustedOptions.retries = 2;
adjustedOptions.retryDelay = 500;
}
return adjustedOptions;
}
}
4. Приоритезация запросов
class RequestPriorityManager {
constructor() {
this.highPriorityQueue = [];
this.normalPriorityQueue = [];
this.lowPriorityQueue = [];
this.isProcessing = false;
}
addRequest(request, priority = 'normal') {
const requestWrapper = {
request,
priority,
timestamp: Date.now()
};
switch (priority) {
case 'high':
this.highPriorityQueue.push(requestWrapper);
break;
case 'normal':
this.normalPriorityQueue.push(requestWrapper);
break;
case 'low':
this.lowPriorityQueue.push(requestWrapper);
break;
}
if (!this.isProcessing) {
this.processQueue();
}
}
async processQueue() {
this.isProcessing = true;
try {
// Обрабатываем запросы высокого приоритета
while (this.highPriorityQueue.length > 0) {
const requestWrapper = this.highPriorityQueue.shift();
await this.executeRequest(requestWrapper);
}
// Затем обычные
while (this.normalPriorityQueue.length > 0) {
const requestWrapper = this.normalPriorityQueue.shift();
await this.executeRequest(requestWrapper);
}
// И наконец низкие
while (this.lowPriorityQueue.length > 0) {
const requestWrapper = this.lowPriorityQueue.shift();
await this.executeRequest(requestWrapper);
}
} finally {
this.isProcessing = false;
}
}
async executeRequest(requestWrapper) {
try {
const result = await requestWrapper.request();
return result;
} catch (error) {
console.error('Request failed:', error);
throw error;
}
}
}
Источники
- How can I make an AJAX call without jQuery? - Stack Overflow
- How to Make an AJAX Call Without jQuery Using Vanilla JavaScript — codestudy.net
- Ajax Requests – You Don’t Need jQuery! – Garstasio.com
- A Guide to Vanilla Ajax Without jQuery — SitePoint
- How to Make an AJAX Call without jQuery - StackAbuse
- Ajax without jQuery - JavaScript - Ibrahim Diallo
- Making AJAX requests with native JavaScript | Go Make Things
- CoffeeScript Cookbook » Ajax Request Without jQuery
- How to Make AJAX Calls Without jQuery? - DevBabu
- How can I make an AJAX call without jQuery? - Matheus Mello
- Fetch API vs XMLHttpRequest - Stack Overflow
- XMLHttpRequest API - Web APIs | MDN
- The Fetch API performance vs. XHR in vanilla JS | Go Make Things
- Fetch API Vs XMLHttpRequest - Tutorialspoint
- Using XMLHttpRequest - Web APIs | MDN
Заключение
Ключевые выводы
- Выберите подходящий инструмент:
XMLHttpRequestобеспечивает более широкую совместимость и более детальный контроль, в то время какFetchпредлагает более чистый синтаксис с промисами для современных приложений. - Всегда реализуйте обработку ошибок: Оба подхода требуют всесторонней обработки сетевых ошибок, ошибок сервера и проблем с разбором данных.
- Создавайте переиспользуемые утилиты: Оберните AJAX‑функциональность в собственные классы или утилиты, чтобы поддерживать единообразие и уменьшить дублирование кода.
- Учитывайте совместимость браузеров: Используйте обнаружение возможностей и прогрессивное улучшение, чтобы гарантировать работу в разных браузерах и устройствах.
- Оптимизируйте производительность: Реализуйте кэширование, дебаунсинг, батчинг и приоритезацию запросов для улучшения пользовательского опыта и снижения нагрузки на сервер.
Практические рекомендации
- Для новых проектов: Начните с
Fetch API, если не требуется поддержка Internet Explorer или специфический контроль над запросами. - Для наследуемых приложений: Рассмотрите использование
XMLHttpRequestили polyfill для более широкой совместимости. - Для сложных приложений: Постройте надёжный HTTP‑клиент с интерсепторами, кэшированием и продвинутой обработкой ошибок.
- Для мобильных пользователей: Реализуйте обработку запросов с учётом состояния соединения, чтобы оптимизировать работу в условиях слабой сети.
Часто задаваемые вопросы
- Что лучше: XMLHttpRequest или Fetch API? В большинстве случаев предпочтительнее
Fetch APIиз‑за более чистого синтаксиса и промис‑обработки, ноXMLHttpRequestостаётся ценным для совместимости с устаревшими браузерами и специфических случаев. - Как обрабатывать кросс‑доменные запросы? Оба подхода автоматически обрабатывают CORS, но сервер должен включать соответствующие заголовки CORS для успешных запросов.
- Можно ли использовать AJAX в серверном JavaScript? Да, как
XMLHttpRequest, так иFetch APIработают в Node.js 18+ и других средах серверного JavaScript, что делает их пригодными для изоморфных приложений.