相关技巧
主页 > 网络编程 > 相关技巧 >

AJAX常见的几种封装方法实例介绍

2025-07-08 | 佚名 | 点击:

AJAX (Asynchronous JavaScript and XML) 封装是为了简化重复的异步请求代码,提高开发效率和代码复用性。下面我将介绍几种常见的 AJAX 封装方式。

方法1. 基于原生 XMLHttpRequest 的封装

XMLHttpRequest。其主要特点如下:

  1. 实现动态不刷新,通过异步?式,提升?户体验,优化了浏览器和服务器之间的传输。
  2. 把?部分原本由服务器负担的?作转移到客户端,利?客户端闲置的资源进?处理,减轻服务器和带宽的负担,节约空间和成本。
  3. ?刷新更新??,?户不?再像以前?样在服务器处理数据时,只能在死板的?屏前焦急的等待。AJAX使?XMLHttpRequest对象发送请求并得到服务器响应,在不需要重新载?整个??的情况下,就可以通过DOM及时将更新的内容显示在??上。

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

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

/**

 * 基于原生XHR的AJAX封装

 * @param {Object} options 配置对象

 * @param {string} options.url 请求地址

 * @param {string} [options.method='GET'] 请求方法

 * @param {Object} [options.data=null] 请求数据

 * @param {Object} [options.headers={}] 请求头

 * @param {function} [options.success] 成功回调

 * @param {function} [options.error] 失败回调

 */

function ajax(options) {

  const xhr = new XMLHttpRequest();

  const method = options.method || 'GET';

  let url = options.url;

  let data = options.data || null;

 

  // 处理GET请求的查询参数

  if (method === 'GET' && data) {

    const params = new URLSearchParams();

    for (const key in data) {

      params.append(key, data[key]);

    }

    url += '?' + params.toString();

    data = null;

  }

 

  xhr.open(method, url, true);

 

  // 设置请求头

  if (options.headers) {

    for (const key in options.headers) {

      xhr.setRequestHeader(key, options.headers[key]);

    }

  }

 

  xhr.onreadystatechange = function() {

    if (xhr.readyState === 4) {

      if (xhr.status >= 200 && xhr.status < 300) {

        let response = xhr.responseText;

        try {

          response = JSON.parse(response);

        } catch (e) {}

        options.success && options.success(response);

      } else {

        options.error && options.error(xhr.status, xhr.statusText);

      }

    }

  };

 

  xhr.onerror = function() {

    options.error && options.error(-1, 'Network Error');

  };

 

  // 发送请求

  if (data && typeof data === 'object') {

    xhr.setRequestHeader('Content-Type', 'application/json');

    xhr.send(JSON.stringify(data));

  } else {

    xhr.send(data);

  }

}

 

// 使用示例

ajax({

  url: '/api/user',

  method: 'POST',

  data: { name: 'John', age: 30 },

  headers: {

    'Authorization': 'Bearer token123'

  },

  success: function(response) {

    console.log('Success:', response);

  },

  error: function(status, statusText) {

    console.error('Error:', status, statusText);

  }

});

方法2. 基于 Fetch API 的封装

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

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

/**

 * 基于Fetch API的AJAX封装

 * @param {string} url 请求地址

 * @param {Object} [options={}] 请求配置

 * @returns {Promise} 返回Promise对象

 */

function fetchAjax(url, options = {}) {

  const defaultOptions = {

    method: 'GET',

    headers: {

      'Content-Type': 'application/json'

    },

    credentials: 'same-origin', // 携带cookie

    ...options

  };

 

  // 处理GET请求的查询参数

  if (defaultOptions.method === 'GET' && defaultOptions.body) {

    const params = new URLSearchParams();

    for (const key in defaultOptions.body) {

      params.append(key, defaultOptions.body[key]);

    }

    url += '?' + params.toString();

    delete defaultOptions.body;

  }

 

  // 处理非GET请求的body数据

  if (defaultOptions.body && typeof defaultOptions.body === 'object') {

    defaultOptions.body = JSON.stringify(defaultOptions.body);

  }

 

  return fetch(url, defaultOptions)

    .then(async response => {

      const data = await response.json().catch(() => ({}));

      if (!response.ok) {

        const error = new Error(response.statusText);

        error.response = response;

        error.data = data;

        throw error;

      }

      return data;

    });

}

 

// 使用示例

fetchAjax('/api/user', {

  method: 'POST',

  body: { name: 'John', age: 30 },

  headers: {

    'Authorization': 'Bearer token123'

  }

})

.then(data => console.log('Success:', data))

.catch(err => console.error('Error:', err));

方法 3. 基于 Axios 风格的封装

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

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

133

134

135

136

137

138

139

140

141

142

143

144

145

146

147

148

149

150

151

152

153

154

155

156

157

class Ajax {

  constructor(baseURL = '', timeout = 10000) {

    this.baseURL = baseURL;

    this.timeout = timeout;

    this.interceptors = {

      request: [],

      response: []

    };

  }

 

  request(config) {

    // 处理请求拦截器

    let chain = [this._dispatchRequest, undefined];

    this.interceptors.request.forEach(interceptor => {

      chain.unshift(interceptor.fulfilled, interceptor.rejected);

    });

    this.interceptors.response.forEach(interceptor => {

      chain.push(interceptor.fulfilled, interceptor.rejected);

    });

 

    let promise = Promise.resolve(config);

    while (chain.length) {

      promise = promise.then(chain.shift(), chain.shift());

    }

    return promise;

  }

 

  _dispatchRequest(config) {

    return new Promise((resolve, reject) => {

      const xhr = new XMLHttpRequest();

      let url = config.baseURL ? config.baseURL + config.url : config.url;

      let data = config.data;

 

      // 处理GET请求参数

      if (config.method === 'GET' && data) {

        const params = new URLSearchParams();

        for (const key in data) {

          params.append(key, data[key]);

        }

        url += '?' + params.toString();

        data = null;

      }

 

      xhr.timeout = config.timeout || 10000;

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

 

      // 设置请求头

      if (config.headers) {

        for (const key in config.headers) {

          xhr.setRequestHeader(key, config.headers[key]);

        }

      }

 

      xhr.onload = function() {

        if (xhr.status >= 200 && xhr.status < 300) {

          let response = xhr.responseText;

          try {

            response = JSON.parse(response);

          } catch (e) {}

          resolve({

            data: response,

            status: xhr.status,

            statusText: xhr.statusText,

            headers: xhr.getAllResponseHeaders()

          });

        } else {

          reject(new Error(`Request failed with status code ${xhr.status}`));

        }

      };

 

      xhr.onerror = function() {

        reject(new Error('Network Error'));

      };

 

      xhr.ontimeout = function() {

        reject(new Error('Timeout'));

      };

 

      // 发送请求

      if (data && typeof data === 'object') {

        xhr.setRequestHeader('Content-Type', 'application/json');

        xhr.send(JSON.stringify(data));

      } else {

        xhr.send(data);

      }

    });

  }

 

  get(url, config = {}) {

    return this.request({

      ...config,

      method: 'GET',

      url

    });

  }

 

  post(url, data, config = {}) {

    return this.request({

      ...config,

      method: 'POST',

      url,

      data

    });

  }

 

  // 添加拦截器

  useRequestInterceptor(fulfilled, rejected) {

    this.interceptors.request.push({ fulfilled, rejected });

    return this.interceptors.request.length - 1;

  }

 

  useResponseInterceptor(fulfilled, rejected) {

    this.interceptors.response.push({ fulfilled, rejected });

    return this.interceptors.response.length - 1;

  }

 

  // 移除拦截器

  ejectRequestInterceptor(id) {

    if (this.interceptors.request[id]) {

      this.interceptors.request.splice(id, 1);

    }

  }

 

  ejectResponseInterceptor(id) {

    if (this.interceptors.response[id]) {

      this.interceptors.response.splice(id, 1);

    }

  }

}

 

// 使用示例

const api = new Ajax('https://api.example.com');

 

// 添加请求拦截器

api.useRequestInterceptor(config => {

  config.headers = config.headers || {};

  config.headers['Authorization'] = 'Bearer token123';

  return config;

});

 

// 添加响应拦截器

api.useResponseInterceptor(response => {

  console.log('Response:', response);

  return response.data;

}, error => {

  console.error('Error:', error);

  return Promise.reject(error);

});

 

// 发起请求

api.get('/user/123')

  .then(data => console.log('User data:', data))

  .catch(err => console.error('Error:', err));

 

api.post('/user', { name: 'John', age: 30 })

  .then(data => console.log('Created user:', data))

  .catch(err => console.error('Error:', err));

4. 封装要点总结

统一接口:提供一致的调用方式,如get(), post()等方法

参数处理:

GET请求自动拼接查询参数

POST请求自动处理Content-Type

拦截器机制:支持请求/响应拦截

错误处理:统一错误处理逻辑

Promise支持:返回Promise便于链式调用

超时处理:设置合理的请求超时时间

扩展性:支持自定义配置和拦截器

5. 实际项目中的增强功能

1.自动重试机制:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

function withRetry(fn, retries = 3, delay = 1000) {

  return function(...args) {

    return new Promise((resolve, reject) => {

      function attempt(retryCount) {

        fn(...args)

          .then(resolve)

          .catch(err => {

            if (retryCount < retries) {

              setTimeout(() => attempt(retryCount + 1), delay);

            } else {

              reject(err);

            }

          });

      }

      attempt(0);

    });

  };

}

 

// 使用示例

const ajaxWithRetry = withRetry(ajax, 3, 1000);

2.请求取消功能:

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

function createCancelToken() {

  let cancel;

  const token = new Promise((resolve, reject) => {

    cancel = reject;

  });

  return { token, cancel };

}

 

// 在请求中检查取消token

function ajaxWithCancel(options) {

  const { token, cancel } = createCancelToken();

  const xhr = new XMLHttpRequest();

   

  const promise = new Promise((resolve, reject) => {

    // ...正常请求逻辑

     

    // 检查取消

    token.catch(err => {

      xhr.abort();

      reject(err);

    });

  });

   

  return { promise, cancel };

}

3.请求缓存:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

const cache = new Map();

 

function cachedAjax(options) {

  const cacheKey = JSON.stringify(options);

   

  if (cache.has(cacheKey)) {

    return Promise.resolve(cache.get(cacheKey));

  }

   

  return ajax(options).then(response => {

    cache.set(cacheKey, response);

    return response;

  });

}

根据项目需求选择合适的封装方式,小型项目可使用简单封装,大型项目建议使用成熟的库如Axios。

原文链接:
相关文章
最新更新