架构与库皆是辅助工具,原生植物为王
序言:
评注责任编辑,专业委员会以反向观念回收那时市售较为流入的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()); // 形式三:示例初始化 constinstance = axios.create();console.log( instance.post() ); </script>问题:
能看到 目前三种初始化形式都已全力支持 1.配置参数未传递过去 config 2.统计数据接收 .then() .catch() 还没有
2.接收允诺参数,并发送允诺,返回promise
实现get允诺三种允诺形式,并返回统计数据/** * createAxios 生成axios 表达式/第一类 *@returns function / Object */ function createAxios() { // 创建一个表达式 constinstance =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 } } returnrequest(newConfig) } instance.post =function () { return post初始化成功 } // 初始化方式 返回当前这个表达式 instance.create = function(){ return createAxios(); } // 返回这个表达式 return instance; } /** * request 发送axios * @returnPromise */ 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); } returnstr; }// 生成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 //这里的初始化 允诺头、拦截器参数这部份还未完善 letinstance = 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) returninstance[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]; letparamsData = 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(); } // 返回这个表达式 returninstance; }/** * 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 constinstance = 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/core2.源码注释
Axios.js定义Axios核心理念库,实现所有的允诺形式,requestajax 核心理念方式的封装
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; importdispatchRequestfrom ./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; 的参数第一类{headers:….} */ class Axios { constructor(instanceConfig) { this.defaults = instanceConfig;// 这里是拦截器的初始化,放到之后示例第一类的属性上【允诺拦截器和响应拦截器】 this.interceptors = { request: newInterceptorManager(),response: new InterceptorManager() }; } /** * request 方式是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) { deleteconfig.headers[method]; } ); config.headers =new AxiosHeaders(config.headers, defaultHeaders); // 拿到所有的允诺拦截器 和响应拦截器的表达式代码 constrequestInterceptorChain = [];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; letnewConfig = config; i =0; while (i < len) { const onFulfilled = requestInterceptorChain[i++]; constonRejected = 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); constfullPath = 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 optionsAxios.prototype[method] = generateHTTPMethod(); Axios.prototype[method +Form] = generateHTTPMethod(true); }); //暴露Axios 类,透过另外一个js文件接收,并示例化 export default Axios;