漳州市纤凝网

AJAX常见的几种封装方法实例详解

2026-03-24 23:34:02 浏览次数:1
详细信息

AJAX(Asynchronous JavaScript and XML)的封装可以提升代码复用性和可维护性。以下是几种常见的封装方法:

1. 原生XMLHttpRequest基础封装

function ajax(options) {
    const defaultOptions = {
        url: '',
        method: 'GET',
        data: null,
        headers: {},
        timeout: 10000,
        success: () => {},
        error: () => {}
    };

    const opts = Object.assign({}, defaultOptions, options);

    return new Promise((resolve, reject) => {
        const xhr = new XMLHttpRequest();

        // 处理查询参数(GET请求)
        let url = opts.url;
        if (opts.method.toUpperCase() === 'GET' && opts.data) {
            const params = new URLSearchParams(opts.data).toString();
            url += (url.includes('?') ? '&' : '?') + params;
        }

        xhr.open(opts.method, url, true);

        // 设置请求头
        Object.keys(opts.headers).forEach(key => {
            xhr.setRequestHeader(key, opts.headers[key]);
        });

        // 设置超时
        xhr.timeout = opts.timeout;

        // 监听状态变化
        xhr.onreadystatechange = function() {
            if (xhr.readyState === 4) {
                if (xhr.status >= 200 && xhr.status < 300) {
                    try {
                        const response = xhr.responseText;
                        const result = opts.dataType === 'json' 
                            ? JSON.parse(response) 
                            : response;
                        opts.success(result);
                        resolve(result);
                    } catch (e) {
                        opts.error(e);
                        reject(e);
                    }
                } else {
                    const error = new Error(`HTTP ${xhr.status}`);
                    opts.error(error);
                    reject(error);
                }
            }
        };

        // 错误处理
        xhr.onerror = function() {
            const error = new Error('Network Error');
            opts.error(error);
            reject(error);
        };

        xhr.ontimeout = function() {
            const error = new Error('Request Timeout');
            opts.error(error);
            reject(error);
        };

        // 发送请求
        const sendData = opts.method.toUpperCase() === 'POST' 
            ? (typeof opts.data === 'object' 
                ? JSON.stringify(opts.data) 
                : opts.data)
            : null;

        xhr.send(sendData);
    });
}

// 使用示例
ajax({
    url: '/api/users',
    method: 'GET',
    data: { page: 1, limit: 10 },
    dataType: 'json',
    success: (data) => console.log('Success:', data),
    error: (err) => console.error('Error:', err)
});

2. Fetch API封装

class HttpRequest {
    constructor(baseURL = '', defaultHeaders = {}) {
        this.baseURL = baseURL;
        this.defaultHeaders = {
            'Content-Type': 'application/json',
            ...defaultHeaders
        };
    }

    async request(endpoint, options = {}) {
        const url = `${this.baseURL}${endpoint}`;
        const headers = { ...this.defaultHeaders, ...options.headers };

        const config = {
            ...options,
            headers
        };

        try {
            const response = await fetch(url, config);

            if (!response.ok) {
                throw new Error(`HTTP ${response.status}: ${response.statusText}`);
            }

            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 {
                ok: true,
                status: response.status,
                data,
                headers: response.headers
            };
        } catch (error) {
            return {
                ok: false,
                error: error.message,
                status: error.status || 0
            };
        }
    }

    get(endpoint, params = {}, options = {}) {
        let url = endpoint;
        if (Object.keys(params).length > 0) {
            const queryString = new URLSearchParams(params).toString();
            url += `?${queryString}`;
        }
        return this.request(url, { ...options, method: 'GET' });
    }

    post(endpoint, data = {}, options = {}) {
        return this.request(endpoint, {
            ...options,
            method: 'POST',
            body: JSON.stringify(data)
        });
    }

    put(endpoint, data = {}, options = {}) {
        return this.request(endpoint, {
            ...options,
            method: 'PUT',
            body: JSON.stringify(data)
        });
    }

    delete(endpoint, options = {}) {
        return this.request(endpoint, { ...options, method: 'DELETE' });
    }

    // 上传文件
    upload(endpoint, formData, options = {}) {
        const headers = { ...options.headers };
        delete headers['Content-Type']; // 让浏览器自动设置multipart/form-data

        return this.request(endpoint, {
            ...options,
            method: 'POST',
            headers,
            body: formData
        });
    }
}

// 使用示例
const api = new HttpRequest('https://api.example.com');

// GET请求
api.get('/users', { page: 1 })
    .then(result => {
        if (result.ok) {
            console.log(result.data);
        }
    });

// POST请求
api.post('/users', { name: 'John', age: 25 })
    .then(result => console.log(result));

3. 基于Promise的完整封装

class AjaxService {
    constructor(config = {}) {
        this.config = {
            baseURL: '',
            timeout: 10000,
            headers: {
                'Content-Type': 'application/json'
            },
            withCredentials: false,
            ...config
        };

        // 请求拦截器
        this.interceptors = {
            request: [],
            response: []
        };
    }

    // 添加拦截器
    use(interceptor) {
        if (interceptor.request) {
            this.interceptors.request.push(interceptor.request);
        }
        if (interceptor.response) {
            this.interceptors.response.push(interceptor.response);
        }
    }

    // 执行拦截器链
    async runInterceptors(interceptorType, value) {
        let result = value;
        const interceptors = this.interceptors[interceptorType];

        for (const interceptor of interceptors) {
            result = await interceptor(result);
        }

        return result;
    }

    async request(method, url, data = null, options = {}) {
        const config = {
            method: method.toUpperCase(),
            url: this.config.baseURL + url,
            data,
            headers: { ...this.config.headers, ...options.headers },
            timeout: options.timeout || this.config.timeout,
            withCredentials: options.withCredentials || this.config.withCredentials
        };

        try {
            // 请求拦截
            const requestConfig = await this.runInterceptors('request', config);

            // 创建请求
            const controller = new AbortController();
            const timeoutId = setTimeout(() => controller.abort(), requestConfig.timeout);

            const fetchOptions = {
                method: requestConfig.method,
                headers: requestConfig.headers,
                signal: controller.signal,
                credentials: requestConfig.withCredentials ? 'include' : 'same-origin'
            };

            if (requestConfig.data && requestConfig.method !== 'GET') {
                fetchOptions.body = typeof requestConfig.data === 'object'
                    ? JSON.stringify(requestConfig.data)
                    : requestConfig.data;
            } else if (requestConfig.method === 'GET' && requestConfig.data) {
                const params = new URLSearchParams(requestConfig.data);
                requestConfig.url += `?${params.toString()}`;
            }

            const response = await fetch(requestConfig.url, fetchOptions);
            clearTimeout(timeoutId);

            // 处理响应
            let responseData;
            const contentType = response.headers.get('content-type');

            if (contentType && contentType.includes('application/json')) {
                responseData = await response.json();
            } else {
                responseData = await response.text();
            }

            const responseObj = {
                data: responseData,
                status: response.status,
                statusText: response.statusText,
                headers: response.headers,
                config: requestConfig
            };

            // 响应拦截
            return await this.runInterceptors('response', responseObj);

        } catch (error) {
            // 错误处理
            const errorObj = {
                message: error.name === 'AbortError' ? '请求超时' : error.message,
                config,
                error
            };

            throw errorObj;
        }
    }

    // 快捷方法
    get(url, params = {}, options = {}) {
        return this.request('GET', url, params, options);
    }

    post(url, data = {}, options = {}) {
        return this.request('POST', url, data, options);
    }

    put(url, data = {}, options = {}) {
        return this.request('PUT', url, data, options);
    }

    patch(url, data = {}, options = {}) {
        return this.request('PATCH', url, data, options);
    }

    delete(url, options = {}) {
        return this.request('DELETE', url, null, options);
    }
}

// 使用示例
const service = new AjaxService({
    baseURL: 'https://api.example.com'
});

// 添加拦截器
service.use({
    request: (config) => {
        // 添加认证token
        const token = localStorage.getItem('token');
        if (token) {
            config.headers.Authorization = `Bearer ${token}`;
        }
        return config;
    },
    response: (response) => {
        // 统一处理响应
        if (response.status === 401) {
            // 处理未授权
            window.location.href = '/login';
        }
        return response;
    }
});

// 发送请求
service.get('/users')
    .then(response => console.log(response.data))
    .catch(error => console.error(error));

4. 并发请求处理

class RequestManager {
    constructor(maxConcurrent = 5) {
        this.maxConcurrent = maxConcurrent;
        this.queue = [];
        this.activeCount = 0;
        this.requestMap = new Map();
    }

    async add(requestFn, id = Symbol()) {
        return new Promise((resolve, reject) => {
            this.queue.push({
                requestFn,
                resolve,
                reject,
                id
            });
            this.processQueue();
        });
    }

    async processQueue() {
        if (this.activeCount >= this.maxConcurrent || this.queue.length === 0) {
            return;
        }

        this.activeCount++;
        const { requestFn, resolve, reject, id } = this.queue.shift();

        try {
            const result = await requestFn();
            resolve(result);
        } catch (error) {
            reject(error);
        } finally {
            this.activeCount--;
            this.processQueue();
        }
    }

    // 批量请求
    async all(requests) {
        return Promise.all(requests.map(req => this.add(req.fn, req.id)));
    }

    // 取消请求
    cancel(id) {
        const index = this.queue.findIndex(item => item.id === id);
        if (index > -1) {
            this.queue.splice(index, 1);
        }
    }
}

// 使用示例
const manager = new RequestManager(3);

// 添加多个请求
const requests = [
    { fn: () => fetch('/api/users'), id: 'users' },
    { fn: () => fetch('/api/posts'), id: 'posts' },
    { fn: () => fetch('/api/comments'), id: 'comments' }
];

manager.all(requests)
    .then(results => console.log(results))
    .catch(error => console.error(error));

5. 简化的工厂函数封装

function createApiClient(config = {}) {
    const {
        baseURL = '',
        headers = {},
        transformRequest = data => data,
        transformResponse = response => response
    } = config;

    const client = {
        async request(method, url, data, options = {}) {
            const finalUrl = baseURL + url;
            const finalHeaders = { ...headers, ...options.headers };

            // 转换请求数据
            const transformedData = transformRequest(data);

            const response = await fetch(finalUrl, {
                method,
                headers: finalHeaders,
                body: method !== 'GET' ? JSON.stringify(transformedData) : undefined
            });

            const result = await response.json();
            return transformResponse(result);
        }
    };

    // 创建快捷方法
    ['get', 'post', 'put', 'delete', 'patch'].forEach(method => {
        client[method] = (url, data, options) => 
            client.request(method.toUpperCase(), url, data, options);
    });

    return client;
}

// 使用示例
const api = createApiClient({
    baseURL: '/api',
    transformRequest: (data) => {
        // 添加时间戳
        return { ...data, timestamp: Date.now() };
    },
    transformResponse: (response) => {
        // 统一处理响应格式
        return response.data || response;
    }
});

// 简洁的调用方式
api.get('/users').then(console.log);
api.post('/users', { name: 'John' }).then(console.log);

总结

封装方式 优点 适用场景
原生封装 无依赖,兼容性好 简单项目或需要最小化依赖
Fetch封装 现代API,Promise原生支持 现代浏览器项目
完整类封装 功能全面,可扩展性强 大型项目,需要拦截器等功能
并发管理 控制并发数,避免过多请求 批量请求场景
工厂函数 简洁灵活,配置化 需要快速创建多个不同配置的客户端

选择哪种封装方式取决于项目需求、团队习惯和技术栈。对于现代项目,推荐使用Fetch API或基于类的完整封装方案。

相关推荐