架构与库皆是辅助工具,原生植物为王序言:
评注责任编辑,专业委员会以反向观念回收那时市售较为流入的js库的PCB形式
想全盘搞清楚ajax允诺,全盘搞清楚Promise ,改写axios 库毫无疑问是最轻松的优先选择
要想展开axios的核心理念部份改写,须要对ajax其间端统计数据初步设计,Promise 后端微各项任务,都有很大的介绍才能展开。
axios核心理念库能全力支持服务器端允诺与后端允诺,责任编辑只特别针对axios后端常用方式展开改写。axios 是甚么 是不是用
axios 是如前所述Promise PCB了HTTTP 允诺的 库。
axios 的采用方式非常简单,能替代原生植物js 展开其间端统计数据允诺可视化。axios 导入后采用形式
形式一:透过axios 表达式传至允诺重要信息第一类axios({
method:“get”,
url:“url门牌号”,
params:{key:value}
}).then()
axios({
method:“post”,
url:“url门牌号”,
data:{key:value}
}).then()
形式二:透过axios 下方式别名调用axios.get(url门牌号?key=value).then()
axios.post(url门牌号,{key:value}).then()
形式三:透过内置方式实例化后调用const instance = axios.create({
baseURL: 服务器全局默认门牌号,
timeout: 1000,
headers: {允诺头第一类}
});
instance.get(允诺门牌号,{params:{key:value}}).then()
instance.post(允诺门牌号,{key,value}).then()
axios 返回值
axios 的返回值是一个第一类,其中 data属性才是后端真正的返回统计数据{
config: {…},
data: {code: 0, msg: 欢迎你,李四同学,get允诺成功啦~你离后端大神更近一步啦~},
headers: {content–length: 97, content–type: application/json; charset=utf-8},
request:…},
status: 200,
statusText: “OK”
}
以上就是关于axios 基本允诺的采用形式了,特殊的方式放到后面再展开介绍,下面我们开始核心理念库的改写~
源码分析
axios 即是一个表达式,又是一个第一类。
两个大层次划分:
第一层: 生成axios 特殊的表达式/第一类,关联这个表达式和Axios类的属性及方式,并暴露这个表达式
第二层:定义Axios 类,定义类的属性(全局配置,拦截器..),核心理念方式:request 批量定义类的原型方式 (get,post,put..)
反向回收实现axios的核心理念
1.实现三种不同的调用形式
axios.js// 生成axios 表达式
function createAxios() {
// 创建一个表达式
const instance = function () {
return axios调用中
}
// 给这个表达式添加get post 等属性 instance.get = function () {
return get调用成功
}
instance.post = function () {
return post调用成功
}
// 初始化方式 返回当前这个表达式 instance.create = function () {
return createAxios();
}
// 返回这个表达式
return instance;
}
// 生成axios 并暴露[因为直接在 原生植物html/js 环境测试 暂时不采用模块化语法暴露]const axios = createAxios();
test.html <script src=“./axios.js”></script>
<script>
// 形式一: axios(config)
console.log( axios() );
// 形式二: axios.get()
console.log(axios.get());
// 方式三:实例初始化 const instance = axios.create();
console.log( instance.post() );
</script>
问题:
能看到 目前三种调用形式都已全力支持
1.配置参数未传递过去 config
2.统计数据接收 .then() .catch() 还没有2.接收允诺参数,并发送允诺,返回promise
实现get允诺三种允诺形式,并返回统计数据/**
* createAxios 生成axios 表达式/第一类 * @returns function / Object
*/
function createAxios() {
// 创建一个表达式
const instance = function (config) {
// 根据不同允诺类型-调用instance 第一类上的方式
const { method } = config;
// 例:method==get 实现:this.get(url,config)
return instance[method](config)
}
// 给这个表达式添加get post 等属性
instance.get = function (config) {
// 为了满足两种形式必须写入 url 和 config // 有config 就是透过形式1调用 没有config就是形式2/3调用
// 形式2/3调用没有config 须要组装一个config
let newConfig = {};
if (typeof config == string) {
//形式2 和形式3
//第一个参数都是url
//如果后面继续跟参数第一类,必是 包含params第一类统计数据
let arg = […arguments];
let paramsData = arg.length > 1 ? arg[1] : {};
newConfig = { method: get, url: config, …paramsData }
} else {
newConfig = { …config, method: get }
}
return request(newConfig)
}
instance.post = function () {
return post调用成功
}
// 初始化方式 返回当前这个表达式
instance.create = function () {
return createAxios();
}
// 返回这个表达式
return instance;
}
/**
* request 发送axios
* @return Promise */
function request(config) {
const { url, method, params, data, headers } = config;
return new Promise((resolve, reject) => {
// 初始化 ajax
let xhr = new XMLHttpRequest();
// open send
if (method.toLowerCase() == get) {
xhr.open(method, url + paramsFormat(params));
xhr.send()
}
if (method == post) {
// 默认允诺头 json
let header = { Content-type: application/json }
// 设置允诺头
xhr.setRequestHeader({ …header, …headers })
xhr.open(method, url);
xhr.send()
}
xhr.onreadystatechange = function () {
if (xhr.status == 200 && xhr.readyState == 4) {
resolve(JSON.parse(xhr.responseText))
} else if (xhr.readyState == 4) {
reject(new Error(axios 错误))
}
}
})
}
/**
* paramsFormat 格式化get允诺统计数据* @params Object {key:value,key1:value1}
* @return String ?key=value&key1=value1
*/
function paramsFormat(params) {
let str = ;
//有统计数据,在展开处理
if (params) {
for (key in params) {
str += `&${key}=${params[key]}`;
}
str = ? + str.slice(1);
}
return str;
}
// 生成axios 并暴露[因为直接在 原生植物html/js 环境测试 暂时不采用模块化语法暴露]
const axios = createAxios();
测试三种get允诺注:以下接口只是用来展开测试时采用,请勿恶意攻击服务器 //1
axios({
method: get,
url: http://115.159.153.85:8001/getTest,
params: { name: “张三” }
}).then(res => {
console.log(res);
})
//2
axios.get(http://115.159.153.85:8001/getTest?name=李四).then(res => {
console.log(res);
})
//3
//这里的初始化 允诺头、拦截器参数这部份还未完善
let instance = axios.create();
instance.get(http://115.159.153.85:8001/getTest, { params: { name: 黄四郎 } }).then(res => {
console.log(res);
})
3.完善request 表达式,全力支持post允诺
/**
* createAxios 生成axios 表达式/第一类
* @returns function / Object
*/
function createAxios() {
// 创建一个表达式 const instance = function (config) {
// 根据不同允诺类型-调用instance 第一类上的方式
const { method } = config;
// 例:method==get 实现:this.get(url,config) return instance[method](config)
}
// 给这个表达式添加get post 等属性
instance.get = function (config) {
// 为了满足两种形式必须写入 url 和 config
// 有config 就是透过形式1调用 没有config就是形式2/3调用
// 形式2/3调用没有config 须要组装一个config let newConfig = {};
if (typeof config == string) {
//形式2 和形式3
//第一个参数都是url
//如果后面继续跟参数第一类,必是 包含params第一类统计数据
let arg = […arguments];
let paramsData = arg.length > 1 ? arg[1] : {};
newConfig = { method: get, url: config, …paramsData }
} else {
newConfig = { …config, method: get }
}
return request(newConfig)
}
instance.post = function (config) {
// 重新组装config 思路与get允诺一致 //暂时不考虑 config.headers 允诺头相关
let newConfig = {};
if (typeof config == string) {
//2/3
let arg = […arguments];
let data = arg.length > 1 ? arg[1] : {};
newConfig = { method: post, url: config, data }
} else {
//1
newConfig = { …config, method: post }
}
return request(newConfig);
}
// 初始化方式 返回当前这个表达式
instance.create = function () {
return createAxios();
}
// 返回这个表达式
return instance;
}
/**
* request 发送axios
* @return Promise
*/
function request(config) {
const { url, method, params, data, headers } = config;
return new Promise((resolve, reject) => {
// 初始化 ajax
let xhr = new XMLHttpRequest();
// open send
if (method.toLowerCase() == get) {
xhr.open(method, url + paramsFormat(params));
xhr.send()
}
if (method == post) {
xhr.open(method, url);
xhr.setRequestHeader(Content-Type, application/json);
// 默认允诺头 json ,后续透过其他表达式统一处理 允诺头和参数
let sendData = JSON.stringify(data);
xhr.send(sendData)
}
xhr.onreadystatechange = function () {
if (xhr.status == 200 && xhr.readyState == 4) {
resolve(JSON.parse(xhr.responseText))
} else if (xhr.readyState == 4) {
reject(new Error(axios 错误))
}
}
})
}
/**
* paramsFormat 格式化get允诺统计数据* @params Object {key:value,key1:value1}
* @return String ?key=value&key1=value1
*/
function paramsFormat(params) {
let str = ;
//有统计数据,在展开处理
if (params) {
for (key in params) {
str += `&${key}=${params[key]}`;
}
str = ? + str.slice(1);
}
return str;
}
// 生成axios 并暴露[因为直接在 原生植物html/js 环境测试 暂时不采用模块化语法暴露]
const axios = createAxios();
测试 //1
axios({
method: post,
url: http://115.159.153.85:8001/postTest,
data: { name: “张三1” }
}).then(res => {
console.log(res);
})
//2
axios.post(http://115.159.153.85:8001/postTest, { name: “张三2” }).then(res => {
console.log(res);
})
//3
const instance = axios.create();
instance.post(http://115.159.153.85:8001/postTest, { name: 张三3 }).then(res => {
console.log(res);
})
剩余问题:
1.透过axios.create() 方式初始化axios时 全力支持 headers baseUrl timeOut 等属性 的设置
2.不同的允诺头 的允诺参数的格式不同
3.目前只全力支持了 get 允诺 post 允诺 ,应该全力支持更多的允诺形式: put patch delete …按照axios 源码思路复现
1.评注源码-回收axios 源码
源码门牌号: https://github.com/axios/axios/tree/v1.x/lib/core
2.源码注释
Axios.js定义Axios核心理念库,实现所有的请求形式,requestajax 核心理念方式的PCB
https://github.com/axios/axios/blob/v1.x/lib/core/Axios.js
use strict;
import utils from ./../utils.js;
import buildURL from ../helpers/buildURL.js;
import InterceptorManager from ./InterceptorManager.js;
import dispatchRequest from ./dispatchRequest.js;
import mergeConfig from ./mergeConfig.js;
import buildFullPath from ./buildFullPath.js;
import validator from ../helpers/validator.js;
import AxiosHeaders from ./AxiosHeaders.js;
const validators = validator.validators;
/**
* Axios 类的创建
配置的参数第一类{headers:….} */
class Axios {
constructor(instanceConfig) {
this.defaults = instanceConfig;
// 这里是拦截器的初始化,放到之后实例第一类的属性上【允诺拦截器和响应拦截器】
this.interceptors = {
request: new InterceptorManager(),
response: new InterceptorManager()
};
}
/**
* request 方式是PCB了ajax 允诺的核心理念方式,主要PCB的是关于ajax的允诺头,参数格式化,各种允诺方式的原型方式挂载 *
* @param {String|Object} configOrUrl
* 有的调用方式,须要传递两个统计数据,第一个是url 和二个参数是一个第一类【允诺传递的统计数据,允诺头等等,对应不同的调用形式】 * @param {?Object} config
*
* 返回一个Promise 状态机,这个Promise就是能在 调用时写 .then() .catch()接收统计数据的原因 * @returns {Promise}
*/
request(configOrUrl, config) {
/**处理不同调佣形式传递的统计数据,只传递了一个url过来的重新组装一个config 第一类,传递了第2个参数,config第一类加一个url属性 */
if (typeof configOrUrl === string) {
config = config || {};
config.url = configOrUrl;
} else {
config = configOrUrl || {};
}
/** 将config 第一类展开格式化处理:和全局默认配置展开合并,如果都有允诺头这种,须要做合并处理 */
config = mergeConfig(this.defaults, config);
const {transitional, paramsSerializer} = config;
if (transitional !== undefined) {
validator.assertOptions(transitional, {
silentJSONParsing: validators.transitional(validators.boolean),
forcedJSONParsing: validators.transitional(validators.boolean),
clarifyTimeoutError: validators.transitional(validators.boolean)
}, false);
}
if (paramsSerializer !== undefined) {
validator.assertOptions(paramsSerializer, {
encode: validators.function,
serialize: validators.function
}, true);
}
// 处理多种调用形式,axios.get() axios({method:get}) 重新组装config 添加method属性
config.method = (config.method || this.defaults.method || get).toLowerCase();
// Flatten headers
const defaultHeaders = config.headers && utils.merge(
config.headers.common,
config.headers[config.method]
);
// 透过定义好的所有的允诺形式,匹配调用时传递进来的方式 重新组装 config的 method属性 defaultHeaders && utils.forEach(
[delete, get, head, post, put, patch, common],
function cleanHeaderConfig(method) {
delete config.headers[method];
}
);
config.headers = new AxiosHeaders(config.headers, defaultHeaders);
// 拿到所有的允诺拦截器 和响应拦截器的表达式代码
const requestInterceptorChain = [];
let synchronousRequestInterceptors = true;
this.interceptors.request.forEach(function unshiftRequestInterceptors(interceptor) {
if (typeof interceptor.runWhen === function && interceptor.runWhen(config) === false) {
return;
}
synchronousRequestInterceptors = synchronousRequestInterceptors && interceptor.synchronous;
requestInterceptorChain.unshift(interceptor.fulfilled, interceptor.rejected);
});
const responseInterceptorChain = [];
this.interceptors.response.forEach(function pushResponseInterceptors(interceptor) {
responseInterceptorChain.push(interceptor.fulfilled, interceptor.rejected);
});
let promise;
let i = 0;
let len;
//拦截器验证,给一个promise透过了拦截器之后 在展开下一步
if (!synchronousRequestInterceptors) {
const chain = [dispatchRequest.bind(this), undefined];
chain.unshift.apply(chain, requestInterceptorChain);
chain.push.apply(chain, responseInterceptorChain);
len = chain.length;
promise = Promise.resolve(config);
while (i < len) {
promise = promise.then(chain[i++], chain[i++]);
}
return promise;
}
len = requestInterceptorChain.length;
let newConfig = config;
i = 0;
while (i < len) {
const onFulfilled = requestInterceptorChain[i++];
const onRejected = requestInterceptorChain[i++];
try {
newConfig = onFulfilled(newConfig);
} catch (error) {
onRejected.call(this, error);
break;
}
}
try {
promise = dispatchRequest.call(this, newConfig);
} catch (error) {
return Promise.reject(error);
}
i = 0;
len = responseInterceptorChain.length;
while (i < len) {
promise = promise.then(responseInterceptorChain[i++], responseInterceptorChain[i++]);
}
return promise;
}
// 再次特别针对config.url展开格式化(防止参数里面有特殊字符和中文汉字等等) getUri(config) {
config = mergeConfig(this.defaults, config);
const fullPath = buildFullPath(config.baseURL, config.url);
return buildURL(fullPath, config.params, config.paramsSerializer);
}
}
// 全力支持 axios.get axios.delete 形式的调用,将这个允诺形式全部挂载到 原型方式,并特别针对这四个方式展开允诺头的处理
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,
url,
data: (config || {}).data
}));
};
});
// 全力支持 axios.post axios.put 形式的调用,将这个允诺形式全部挂载到 原型方式,并特别针对这四个方式展开允诺头的处理
utils.forEach([post, put, patch], function forEachMethodWithData(method) {
//PCB表达式 实现 允诺头 相关的属性的格式化
function generateHTTPMethod(isForm) {
return function httpMethod(url, data, config) {
return this.request(mergeConfig(config || {}, {
method,
headers: isForm ? {
Content-Type: multipart/form-data
} : {},
url,
data
}));
};
}
//向Axios 类的原型上挂载 所有的方式 get put patch delete get head options Axios.prototype[method] = generateHTTPMethod();
Axios.prototype[method + Form] = generateHTTPMethod(true);
});
//暴露Axios 类,透过另外一个js文件接收,并实例化
export default Axios;