análisis del código fuente de axios cómo implementar la biblioteca de solicitudes HTTP
Descripción general Durante el proceso de desarrollo front-end, a menudo nos encontramos con la necesidad de enviar solicitudes asincrónicas. El uso de una biblioteca de solicitudes HTTP con funciones completas e interfaces completas puede reducir en gran medida nuestros costos de desarrollo y mejorar nuestra eficiencia de desarrollo.
axios es una biblioteca de solicitudes HTTP muy popular en los últimos años. Actualmente tiene más de 40.000 estrellas en GitHub y ha sido recomendada por muchas personas.
Hoy veremos cómo está diseñado axios y qué podemos aprender de él. Mientras escribo esto, axios tiene la versión 0.18.0. Tomemos esta versión del código como ejemplo para leer y analizar el código fuente. Actualmente, todos los archivos fuente de axios están en la carpeta lib, por lo que todas las rutas que mencionamos a continuación están en la carpeta lib.
El contenido principal de este artículo incluye:
Cómo utilizar axios
Cómo se diseñan e implementan los módulos principales de axios (solicitud, interceptor, retracción)
Qué vale la pena aprender sobre el diseño de axios
Cómo usar axios Para comprender el diseño de axios, primero debemos entender cómo se usa axios. Entendamos la API de axios a través de un ejemplo simple.
Enviar solicitud axios({
método:'get',
url:'http://bit.ly/2mTM3nY',
tipo de respuesta:'flujo'
})
.then(function(respuesta) {
respuesta.data.pipe(fs.createWriteStream(' ada_lovelace.jpg'))
}); Este es un ejemplo de API oficial. Del código anterior, podemos ver que el uso de axios es muy similar al ajax de jQuery, los cuales continúan las operaciones posteriores devolviendo una Promesa (o mediante una devolución de llamada exitosa, pero se recomienda usar Promise o await).
Este ejemplo de código es muy simple por lo que no entraré en detalles, pero veamos cómo agregar una función de filtro.
Agregar función de interceptor // Agregar un interceptor de solicitudes. Tenga en cuenta que hay dos funciones, una es exitosa y la otra falla. El motivo se explicará más adelante
axios.interceptors. request.use(función (config) {
(config)). respuesta.use(function (respuesta) {
// Procesa los datos de respuesta en respuesta
return respuesta;
}, función (error) { p>
// Procesa la respuesta después de un error
return Promise.reject(error);
});
A través del ejemplo anterior, Nosotros, como puede ver, antes de enviar la solicitud, podemos procesar los datos de los parámetros de configuración de la solicitud y, después de la respuesta de la solicitud, podemos realizar operaciones específicas con los datos devueltos. Además, podemos tener un manejo de errores específico para solicitudes fallidas y respuestas fallidas.
Cancelar solicitud HTTP En funciones relacionadas con la búsqueda, a menudo necesitamos enviar solicitudes frecuentes para consultar datos. A menudo, necesitamos cancelar la solicitud anterior al enviar la siguiente solicitud.
const CancelToken = axios.CancelToken;
const fuente = CancelToken.source();
axios .get('/user/12345', {
cancelToken: source.token
}).catch(function(thrown) {
if (axios.isCancel(thrown)) {
console.log('Solicitud cancelada', throw.message);
} else {
// manejar el error
}
});
axios.post('/user/12345', {
nombre: 'nuevo nombre'
}, {
cancelToken: source.
})
//Cancelar solicitud (los parámetros del mensaje son opcionales)
source.cancel('El usuario cancela la operación '); A través del ejemplo anterior, podemos ver que axios está utilizando una propuesta de cancelación basada en CancelToken. Sin embargo, esta propuesta ahora ha sido retirada, cuyos detalles se pueden encontrar aquí. Explicaremos la implementación del retiro más adelante cuando analicemos el código fuente.
Cómo se diseña e implementa el módulo principal de axios A través de los ejemplos anteriores, creo que todos tienen una comprensión general de cómo usar axios. A continuación analizaremos el diseño e implementación de axios por módulo. La siguiente imagen muestra los archivos axios relevantes que cubriremos en este blog. Si los lectores están interesados, pueden clonar el código relevante y leerlo junto con este blog, lo que puede profundizar su comprensión de los módulos relevantes.
El módulo de solicitud HTTP es el módulo principal y el código relevante para que axios envíe solicitudes se encuentra en el archivo core/dispatchReqeust.js.
Debido al espacio limitado, he seleccionado algunos códigos fuente clave para una breve introducción a continuación:
module.exports = function despachoRequest(config) {
throwIfCancellationRequested(config);
// Otro código fuente
// El adaptador predeterminado es un módulo que determina el entorno actual y elige usar Node o XHR para enviar solicitudes
var adaptor = config.adapter || defaults.adaptador;
return adaptor(config).then(function onAdapterResolution(response) {
throwIfCancellationRequested(config);
// otro código fuente
devolver respuesta;
}, función onAdapterRejection(motivo) {
if (!isCancel(motivo)) {
throwIfCancellationRequested(config );
// Otro código fuente
return Promise.reject(reason);
});
}; código anterior y, por ejemplo, sabemos que el método despachoRequest obtiene el módulo que envía la solicitud obteniendo config.adapter. También podemos reemplazar el módulo nativo pasando una función de adaptador conforme (aunque esto generalmente no se hace, es un punto de extensión débilmente acoplado).
En el archivo default.js, podemos ver la lógica de selección del adaptador, que se basa en algunas propiedades y constructores específicos del contenedor actual.
función getDefaultAdapter() {
var adaptor;
// Solo las clases de Node.js tienen variables de tipo de proceso
if (typeof proceso!== 'undefinido' && Object.prototype.toString.call(proceso) === '[Proceso de objeto]'){
// Módulo de solicitud de Node.js
adaptador = require('. /adapters/http');
} else if (typeof XMLHttpRequest ! == 'undefinido') {
// Módulo de solicitud del navegador
adaptador = require('. /adapters/xhr');
} else if (typeof XMLHttpRequest ! == 'undefinido') {
// Módulo de solicitud del navegador
adaptador = require('./adapters/xhr');
}
return adaptador;
}axios El módulo XHR es un contenedor relativamente simple alrededor del objeto XMLHTTPRequest.
El módulo XHR es un contenedor relativamente simple alrededor del objeto XMLHTTPRequest.
Para comprender el módulo de envío de solicitudes HTTP implementado por despachoRequest del módulo interceptor, echemos un vistazo a cómo axios maneja las funciones del interceptor de solicitud y respuesta. Echemos un vistazo a la función de solicitud, que es el punto de entrada unificado para solicitudes en axios.
Axios.prototype.request = function request(config) {
// Otro código
var chain = [dispatchRequest, indefinido];
var promesa = Promise.resolve(config);
this.interceptors.request.forEach(function unshift.request)forEach(function unshiftRequestInterceptors(interceptor) {
cadena. unshift(interceptor.fulfilled, interceptor.rejected);
});
this.interceptors.response.forEach(function pushResponseInterceptors(interceptor) {
cadena .push(interceptor.cumplido, interceptor.rechazado);
});
while (cadena. longitud) {
promesa = promesa.entonces(cadena .shift(), chain.shift());
}
}
promesa de retorno;
}; El punto de entrada para que axios envíe solicitudes. Dado que la implementación de la función es bastante larga, explicaré brevemente la idea de diseño:
la cadena es una cola de ejecución. El valor inicial de la cola es una Promesa con parámetros de configuración.
En la cola de ejecución encadenada se inserta la función despachoReqeust inicial utilizada para enviar la solicitud y su correspondiente función no definida. Las funciones no definidas se agregaron más tarde porque en una Promesa, es necesario que haya un retorno exitoso y un fracaso. Por lo tanto, podemos tratar a despachoReqeust y undefinido como un par de funciones.
DispatchReqeust es la función que envía la solicitud y se ubica en medio de la cola de ejecución de la cadena. Antes está el interceptor de solicitudes, que se ingresa mediante el método unshift; después está el interceptor de respuestas, que se ingresa mediante el método push. Vale la pena señalar que estas funciones se ponen de dos en dos, es decir, de dos en dos.
A través del código de solicitud anterior, tenemos una comprensión general de cómo utilizar los interceptores. A continuación, veamos cómo cancelar una solicitud HTTP.
Módulo de cancelación de solicitudes Los módulos relevantes para cancelar solicitudes se encuentran en la carpeta Cancelar/. Echemos un vistazo al código relevante resaltado.
Primero, veamos la clase Cancel de metadatos.
Es una clase utilizada para registrar el estado de cancelación. El código es el siguiente:
function Cancel(message) {
this.message = message;
}
p>Cancelar.prototype.toString = function toString() {
return 'Cancelar' + (este.mensaje ? ': ' + este.message = 'Cancelar'). message ? ': ' + this.message : '');
};
Cancel.prototype.__CANCEL__ = true En la clase CancelToken, se implementa pasando la Promesa; método Cancelación de solicitudes HTTP.
función CancelToken(ejecutor) {
if (tipo de ejecutor! == 'función') {
throw new TypeError('el ejecutor debe ser una función .');
}
var resolvePromise;
this.promise = nueva promesa (función promesaExecutor(resolver) {
resolverPromise = resolver;
});
var token = this;
ejecutor(función cancelar( mensaje) {
if (token .reason) {
// Se ha solicitado la cancelación
return;
}
token.reason = new Cancelar( mensaje)
resolvePromise(token.reason);
});
}
CancelToken.source = función fuente() {
p>
var cancelar;
var token = nuevo CancelToken(función ejecutor(c) {
cancelar = c;
}); p>
devolver {
token: token,
cancelar: cancelar
};
}; y adaptor/xhr El código correspondiente en el archivo .js.
if (config.cancelToken) {
// espera cancelar
config.cancelToken.promise.then( function onCanceled(cancel) {
if (!request) {
devolver;
}
request.abort();
rechazar(cancelar) );
// Restablecer solicitud
request = null;
});
} Combinado con el ejemplo anterior de cancelar una Solicitud HTTP Con este código, hablemos brevemente sobre la lógica de implementación relevante:
En el caso de que sea necesario cancelar la solicitud, la inicializamos llamando al método fuente, que devolverá una instancia A de la clase CancelToken y la función cancelar.
En el método fuente que devuelve la instancia A, inicializamos una promesa en un estado pendiente que se usará como disparador para cancelar la solicitud después de pasar la instancia A completa a axios.
Cuando se llama al método de cancelación devuelto por el método fuente, el estado de la promesa en la instancia A cambiará de pendiente a cumplido, lo que activará inmediatamente la función de devolución de llamada en ese momento, activando así la lógica de cancelación de axios. - solicitar .abortar().
El diseño de axios es digno de referencia. La lógica de la función de solicitud de envío se mencionó en la sección anterior. Cuando axios procesa la función de envío de solicitud, no la trata como una especial. función, pero use el mismo método y colóquelo en el medio de la cola, lo que no solo garantiza la coherencia del procesamiento de la cola, sino que también mejora la legibilidad del código.
En la lógica de procesamiento del adaptador, axios no considera los módulos http y xhr (uno para que Node.js envíe solicitudes y el otro para que el navegador envíe solicitudes) como sus propios módulos, consumidos. directamente mediante el archivo despachoRequest.default.js. Esto garantiza un bajo acoplamiento entre los dos módulos y al mismo tiempo deja espacio para una futura personalización del módulo de envío de solicitudes.
Cancelar la lógica de procesamiento de solicitudes HTTP Al cancelar la lógica de solicitudes HTTP, axios utiliza inteligentemente Promise como disparador y pasa la función de análisis al exterior a través de los parámetros en la devolución de llamada. Esto no solo garantiza la coherencia de la lógica interna, sino que también garantiza que cuando necesite cancelar una solicitud, no necesite cambiar directamente los datos de muestra de la clase relevante, minimizando así la intrusión en otros módulos.
Resumen Este artículo detalla el uso, diseño e implementación de axios. Los lectores pueden aprender sobre las ideas de diseño de axios a través de los artículos anteriores y, al mismo tiempo, también pueden aprender experiencias relacionadas, como la encapsulación de módulos y la interacción en el código de axios.
Por razones de espacio, este artículo solo descompone e introduce los módulos principales de axios. Si está interesado en otros códigos, puede ir a GitHub para verlos.