文章目录
- `axios`--`ajax`的封装
- 1、axios与Axios的关系
- 2、instance 与 axios 的区别
- 3、axios运行的整体流程
- 4、 Axios函数
- 5、Axios.prototype
MDN文档
HTTP 相关
**超文本传输协议(HTTP)**是一个用于传输超媒体文档(例如 HTML)的应用层协议。它是为 Web 浏览器与 Web 服务器之间的通信而设计的,但也可以用于其他目的。HTTP 遵循经典的客户端-服务端模型,客户端打开一个连接以发出请求,然后等待直到收到服务器端响应。HTTP 是无状态协议,这意味着服务器不会在两个请求之间保留任何数据(状态)。
HTTP请求交互的基本过程
HTTP 报文
请求报文
- 请求行
method url
GET /product_detail?id=2
POST /login
- 多个请求头
Host :
www.baidu.com
Cookie: xx
Content-Type: application/x-www-form-urlencoded 或者 application/json
- 请求体
username=tom&pwd=123
{“username”:“tom”, “pwd”:“123” }
响应报文
- 响应状态行
status statusText
- 多个响应头
Contetn-Type: text/html; charset=utf-8
set-cookies:BD_CK_SAM=1; path=/
- 响应体
html文本、 json文本 …
post 请求体参数格式
- Conten-Type: application/x-www-form-urlencoded; charset=utf-8
用于键值对参数,参数的键值用=连接,参数之间用&连接
https://search.bilibili.com/all?keyword=axios&from_source=nav_search_new
- Conten-Type:application/json; charset=utf-8
用于json 字符串参数
- Conten-Type:multipart/form-data
用于文件上传
常见的响应状态码
200 OK 请求成功。一般用于 GET 与 POST 请求
201 Created 已创建。成功请求并创建了新的资源
401 Unauthorized 未授权/请求要求用户的身份认证
404 Not Found 服务器无法根据客户端的请求找到资源
500 Internal Server Error 服务器内部错误,无法完成请求
不同类型的请求及其作用
- GET: 从服务器端读取数据
- POST: 向服务器端添加新数据
- PUT: 更新服务器端已经数据
- DELETE: 删除服务器端数据
API 分类
- restful api
同一个请求路径可以进行多个操作
GET/POST/PUT/DELETE
- restless api
请求方式不决定请求的CRUD操作
一个请求路径只有一个操作
一般只有 GET/POST
query
返回一个符合查询的数组
params
返回一个对象
理解XHR
区别一般http请求与ajax请求
- ajax 请求是一种特别的
http
请求 - 对服务器来说,没有任何区别,但对浏览器来说不一样
- 浏览器端发出请求
- 只有
XHR
或fetch
发出的请求才是ajax
请求
- ajax 引擎
帮我们发送ajax请求的一段内置代码
- 浏览器接收到的数据
- 一般请求:浏览器一般会直接显示响应体
刷新或者跳转页面
- ajax请求: 浏览器不会对界面做任何操作,只是调用
ajax
请求的函调的回调函数接收到数据
程序员自己操作
ajax
的常用API
constructor
- The
XMLHttpRequest()
constructor creates a newXMLHttpRequest
.
const xhr= new XMLHttpRequest()
properties
An
EventHandler
that is called whenever thereadyState
attribute changes.
XMLHttpRequest.readyState
Read only
Returns an
unsigned short
, the state of the request.
XMLHttpRequest.response
Read only
Returns an
ArrayBuffer
,Blob
,Document
, JavaScript object, or aDOMString
, depending on the value ofXMLHttpRequest.responseType
, that contains the response entity body.
It is an enumerated value that defines the response type
XMLHttpRequest.responseText
Read only
Returns a
DOMString
that contains the response to the request as text, ornull
if the request was unsuccessful or has not yet been sent.
methods
Initializes a request.
XMLHttpRequest.setRequestHeader()
Sets the value of an HTTP request header. You must call
setRequestHeader()
afteropen()
, but beforesend()
.
Sends the request. If the request is asynchronous (which is the default), this method returns as soon as the request is sent.
XMLHttpRequest.open(method, url[, async[, user[, password]]])
- method
The HTTP request method to use, such as
"GET"
,"POST"
,"PUT"
,"DELETE"
, etc. Ignored for non-HTTP(S) URLs.
- url
A
DOMString
representing the URL to send the request to.
Aborts the request if it has already been sent.
XMLHttpRequest.getResponseHeader(headerName)
Returns the string containing the text of the specified header, or
null
if either the response has not yet been received or the header doesn’t exist in the response.
const contentType = xhr.getResponseHeader("Content-Type");
XMLHttpRequest.getAllResponseHeaders()
Returns all the response headers, separated by CRLF, as a string, or
null
if no response has been received.
function success(text) { var textarea = document.getElementById('test-response-text'); textarea.value = text; } function fail(code) { var textarea = document.getElementById('test-response-text'); textarea.value = 'Error code: ' + code; } var request = new XMLHttpRequest(); // 新建XMLHttpRequest对象 request.onreadystatechange = function () { // 状态发生变化时,函数被回调 if (request.readyState === 4) { // 成功完成 // 判断响应结果: if (request.status === 200) { // 成功,通过responseText拿到响应的文本: return success(request.responseText); } else { // 失败,根据响应码判断失败原因: return fail(request.status); } } else { // HTTP请求还在继续... } } // 发送请求: request.open('GET', '/api/categories'); request.send(); alert('请求已发送,请等待响应...');
axios
–ajax
的封装
1、axios与Axios的关系
- 从语法上来说,axios不是Axios的实例
- 从功能上来说,axios是Axios的实例
axios
具备Axios
的 所有方法以及属性
axios
是Axios.prototype.request
函数bind( ) 返回的函数axios
作为对象有Axios
原型对象上的所有方法以及Axios
的所有属性
/** * Create an instance of Axios * * @param {Object} defaultConfig The default config for the instance * @return {Axios} A new instance of Axios */ function createInstance(defaultConfig) { var context = new Axios(defaultConfig); var instance = bind(Axios.prototype.request, context); // Copy axios.prototype to instance utils.extend(instance, Axios.prototype, context); // Copy context to instance utils.extend(instance, context); return instance; } // Create the default instance to be exported var axios = createInstance(defaults); // Expose Axios class to allow class inheritance axios.Axios = Axios; // Factory for creating new instances axios.create = function create(instanceConfig) { // 合并配置 return createInstance(mergeConfig(axios.defaults, instanceConfig)); }; // Expose Cancel & CancelToken axios.Cancel = require('./cancel/Cancel'); axios.CancelToken = require('./cancel/CancelToken'); axios.isCancel = require('./cancel/isCancel'); // Expose all/spread axios.all = function all(promises) { return Promise.all(promises); }; axios.spread = require('./helpers/spread'); // Expose isAxiosError axios.isAxiosError = require('./helpers/isAxiosError'); module.exports = axios; // Allow use of default import syntax in TypeScript module.exports.default = axios;
Axios
的属性
/** * Create a new instance of Axios * * @param {Object} instanceConfig The default config for the instance */ function Axios(instanceConfig) { this.defaults = instanceConfig; this.interceptors = { request: new InterceptorManager(), response: new InterceptorManager() }; }
2、instance 与 axios 的区别
- 相同
- 都是
Axios
的实例 - 都是一个可以发任意请求的函数
request(config)
- 具有默认配置和拦截器属性
- 不同点
instance
没有axios
后面添加的方法 (源码见#1
)- 也就是通过
const instance = axios.create()
创建的intance
具备Axios
的所有方法,但是没有axios
- 注意是小写的axios
后面添加的方法,比如Cancel\CancelToken \isCancel
等,具体见 (#1
)
3、axios运行的整体流程
- 整体流程:
request(config) ==> dispatchRequest(config) ==> xhrAdapter(config)
- request(config):
将请求拦截器 / dispatchRequest() / 响应拦截器 通过 promise 链串连起来, 返回 promise
- dispatchRequest(config):
转换请求数据 ===> 调用 xhrAdapter()发请求 ===> 请求返回后转换响应数据. 返回 promise
- xhrAdapter(config):
创建 XHR 对象, 根据 config 进行相应设置, 发送特定请求, 并接收响应数据, 返回 promise
- 整体图
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-dk7YCoPn-1619618181146)(.\pics\image-20210224105956948.png)]
4、 Axios函数
/** * Create a new instance of Axios * * @param {Object} instanceConfig The default config for the instance */ function Axios(instanceConfig) { this.defaults = instanceConfig; this.interceptors = { request: new InterceptorManager(), response: new InterceptorManager() }; }
5、Axios.prototype
/** * Dispatch a request * * @param {Object} config The config specific for this request (merged with this.defaults) */ Axios.prototype.request = function request(config) { /*eslint no-param-reassign:0*/ // Allow for axios('example/url'[, config]) a la fetch API if (typeof config === 'string') { config = arguments[1] || {}; config.url = arguments[0]; } else { config = config || {}; } config = mergeConfig(this.defaults, config); // Set config.method if (config.method) { config.method = config.method.toLowerCase(); } else if (this.defaults.method) { config.method = this.defaults.method.toLowerCase(); } else { config.method = 'get'; } // 拦截器中间件的执行流程 // reuqest interceptors : [{2},{1}] // response interceptors :[{11},{22}] // Hook up interceptors middleware var chain = [dispatchRequest, undefined]; var promise = Promise.resolve(config); this.interceptors.request.forEach(function unshiftRequestInterceptors(interceptor) { chain.unshift(interceptor.fulfilled, interceptor.rejected); }); this.interceptors.response.forEach(function pushResponseInterceptors(interceptor) { chain.push(interceptor.fulfilled, interceptor.rejected); }); while (chain.length) { promise = promise.then(chain.shift(), chain.shift()); } return promise; }; Axios.prototype.getUri = function getUri(config) { config = mergeConfig(this.defaults, config); return buildURL(config.url, config.params, config.paramsSerializer).replace(/^\?/, ''); }; // Provide aliases for supported request methods utils.forEach(['delete', 'get', 'head', 'options'], function forEachMethodNoData(method) { /*eslint func-names:0*/ Axios.prototype[method] = function(url, config) { return this.request(mergeConfig(config || {}, { method: method, url: url, data: (config || {}).data })); }; }); utils.forEach(['post', 'put', 'patch'], function forEachMethodWithData(method) { /*eslint func-names:0*/ Axios.prototype[method] = function(url, data, config) { return this.request(mergeConfig(config || {}, { method: method, url: url, data: data })); }; });
promise chain
while (chain.length) { promise = promise.then(chain.shift(), chain.shift()); }
6、dispatchRequest
'use strict'; var utils = require('./../utils'); var transformData = require('./transformData'); var isCancel = require('../cancel/isCancel'); var defaults = require('../defaults'); /** * Throws a `Cancel` if cancellation has been requested. */ function throwIfCancellationRequested(config) { if (config.cancelToken) { config.cancelToken.throwIfRequested(); } } /** * Dispatch a request to the server using the configured adapter. * * @param {object} config The config that is to be used for the request * @returns {Promise} The Promise to be fulfilled */ module.exports = function dispatchRequest(config) { throwIfCancellationRequested(config); // Ensure headers exist config.headers = config.headers || {}; // Transform request data config.data = transformData( config.data, config.headers, // 请求转换器, 源码位于default.js中 config.transformRequest ); // Flatten headers config.headers = utils.merge( config.headers.common || {}, config.headers[config.method] || {}, config.headers ); utils.forEach( ['delete', 'get', 'head', 'post', 'put', 'patch', 'common'], function cleanHeaderConfig(method) { delete config.headers[method]; } ); var adapter = config.adapter || defaults.adapter; return adapter(config).then(function onAdapterResolution(response) { throwIfCancellationRequested(config); // Transform response data response.data = transformData( response.data, response.headers, config.transformResponse ); return response; }, function onAdapterRejection(reason) { if (!isCancel(reason)) { throwIfCancellationRequested(config); // Transform response data if (reason && reason.response) { reason.response.data = transformData( reason.response.data, reason.response.headers, config.transformResponse ); } } return Promise.reject(reason); }); };
7、XHR对象 ( axios核心 )
- axios的部分xhr源码 (Ajax请求)
var request = new XMLHttpRequest(); var fullPath = buildFullPath(config.baseURL, config.url); request.open(config.method.toUpperCase(), buildURL(fullPath, config.params, config.paramsSerializer), true); request.onreadystatechange = function handleLoad(){...} // Handle browser request cancellation (as opposed to a manual cancellation) request.onabort = function handleAbort() {...} request.onerror = function handleError() {...} request.ontimeout = function handleTimeout() {...} // Send the request request.send(requestData);
axios
取消请求
简介
You can create a cancel token by passing an executor function to the CancelToken
constructor:
const CancelToken = axios.CancelToken; let cancel; axios.get('/user/12345', { cancelToken: new CancelToken(function executor(c) { // An executor function receives a cancel function as a parameter cancel = c; }) }); // cancel the request cancel();
Note: you can cancel several requests with the same cancel token.
注意事项
cancelToken
的首字母小写new CancelToken(function executor(c)
注意大写
等价于 new CancelToken(c=>{ } )
- 使用拦截器的时候注意不要定义cancel
拦截器版本
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <meta http-equiv="X-UA-Compatible" content="IE=edge" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>Document</title> <script src="https://cdn.bootcdn.net/ajax/libs/axios/0.21.1/axios.min.js"></script> </head> <body> <button onclick="get()">获取产品1</button> <button onclick="post()">获取产品2</button> <button onclick="quxiao()">取消请求</button> <script> const CancelToken = axios.CancelToken; let cancel ; const instance = axios.create({ baseURL: "http://localhost:3000", }); instance.interceptors.request.use((config) => { if (typeof cancel === 'function') { cancel() } config.cancelToken = new axios.CancelToken(c=>{ cancel = c }) return config; }); instance.interceptors.response.use( response=>{ cancel = null console.log('请求成功') return response }, err=>{ if(axios.isCancel(err)){ // 取消请求的错误不用处理 console.log('请求取消的错误',err) // 中断promise链 return new Promise(()=>{}) } else{ // 请求出错 console.log('请求1失败了', err) // 将错误向下传递 return Promise.reject(err) } } ) function get() { instance({ url: "/product1", }) .then((response) => { console.log(response); }) .catch(err=>{ console.log(err) }) } function post() { instance({ url:'/product2' }) .then((response) => { console.log(response); }); } function quxiao() { if ((typeof cancel) === 'function') { cancel('强制取消请求'); }else{ console.log('没有可以取消的请求') } } </script> </body> </html>
菜鸡版本
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <meta http-equiv="X-UA-Compatible" content="IE=edge" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>Document</title> <script src="https://cdn.bootcdn.net/ajax/libs/axios/0.21.1/axios.min.js"></script> </head> <body> <button onclick="get()">获取产品1</button> <button onclick="post()">获取产品2</button> <button onclick="quxiao()">取消请求</button> <script> const CancelToken = axios.CancelToken; let cancel; const instance = axios.create({ baseURL: "http://localhost:3000", }); function get() { if ((typeof cancel) === 'function') { cancel('强制取消请求'); } instance({ url: "/product1", cancelToken: new axios.CancelToken(c=>{ cancel = c }) }) .then((response) => { cancel = null console.log(response); }) .catch(err=>{ if(axios.isCancel(err)){ // 注意如果在这里将cancel设为null 的话, 会在回调中执行, 导致后一个请求无法取消 // cancel = null console.log('cancel取消请求1发生的错误',err.message) } else{ cancel = null console.log('发送请求1发生的错误',err.message) } }) } function post() { if ((typeof cancel) === 'function') { cancel('强制取消请求'); } instance({ url:'/product2', cancelToken: new axios.CancelToken(c=>{ cancel = c }) }) .then((response) => { // 请求成功 令牌设null cancel = null console.log(response); }) .catch(err=>{ if(axios.isCancel(err)){ // 注意如果在这里将cancel设为null 的话, 会在回调中执行, 导致后一个请求无法取消 // cancel = null console.log('cancel取消请求2发生的错误',err.message) } else{ cancel = null console.log('发送请求2发生的错误',err.message) } }) } function quxiao() { if ((typeof cancel) === 'function') { cancel('强制取消请求'); }else{ console.log('没有可以取消的请求') } } </script> </body> </html>
技术参考:
- 尚硅谷
- MDN