前端杂文 axios的AxiosResponse分析

Posted 京金

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了前端杂文 axios的AxiosResponse分析相关的知识,希望对你有一定的参考价值。

文章目录

1. axios的AxiosResponse分析

import		AxiosResponse	 from 'axios' //引入

//在axios的response中的使用。
service.interceptors.response.use(
  (function(response: AxiosResponse<Recordable>) 
      //...
  
)

接下来可以看下这个接口

export interface AxiosResponse<T = any, D = any>  
  data: T;
  status: number;
  statusText: string;
  headers: AxiosResponseHeaders;
  config: AxiosRequestConfig<D>;
  request?: any;

然后是:Recordable

declare type Recordable<T = any, K = string> = 
			Record<K extends null | undefined ? string : K, T>

这个类型是我自己定义的(忘记哪里抄的了,好像是官网给出的方案)

然后是:Record,这个是ts内置的

type Record<K extends keyof any, T> = 
    [P in K]: T;
;
Record Recordable AxiosResponse 我们的函数参数response

从最顶层开始分析

1.1 Record的展开讲解

type Record<K extends keyof any, T> = 
    [P in K]: T;
;

讲这个类型之前,先讲下其他

1.1.1 keyof

索引类型查询:

  • 通过索引类型查询能够获取给定类型中的属性名类型。

  • 索引类型查询的结果是由字符串字面量类型构成的联合类型,该联合类型中的每个字符串字面量类型都表示一个属性名类型。索引类型查询的语法如下所示:

    keyof Type
    

    keyof是关键字,Type表示任意一种类型。

实例:

interface Point 
  x: number;
  y: number;

type T = keyof Point; // 'x' | 'y'
let t:T = 'x'

所以keyof any等同于string | number | symbol

1.1.2 type 类型别名

类型别名与接口相似,它们都可以给类型命名并通过该名字来引用表示的类型:

  • 区别一:类型别名够表示非对象类型,而接口则能表示对象类型。因此,当我们想要表示原始类型、联合类型和交叉类型等类型时只能使用类型别名。示例如

    type NumbericType = number | bigint;
    
  • 区别二:接口可以继承其他的接口、类等对象类型,而类型别名则不支持继承

    interface Shape 
    	name:string;
    
    interface Circle extends Shape 
    	radius: number;
    
    

若要对**类型别名实现类似继承的功能,**则需要使用一些变通方法。例如,当类型别名表示对象类型时,可以借助于交叉类型来实现继承的效果。

type Shape = name:string;
type Circle = Shape & radius:number;
function foo(circle:Circle) 
	const name = circle.name;
	const radius = circle.radius;

此例中的方法只适用于表示对象类型的类型别名。如果类型别名表示非对象类型,则无法使用该方法。

交叉类型的会详细介绍:

  • 区别三:接口名总是会显示在编译器的诊断信息(例如,错误提示和警告)和代码编辑器的智能提示信息中,而类型别名的名字只在特定情况下才会显示出来。

  • 区别四:接口具有声明合并的行为,而类型别名则不会进行声明合并

    interface A 
        x:number
    
    interface A 
        y:number
    
    

    最终合并如下:

    interface A 
        x:number;
        y:number
    
    
1.1.3 extends 泛型约束

除了接口中使用extends。在泛型约束中extends也是一种应用

  • 形式类型参数-泛型约束声明

  • 基约束

  • 形式类型参数-泛型约束声明

在泛型的形式类型参数上允许定义一个约束条件,它能够限定类型参数的实际类型的最大范围。我们将
类型参数的约束条件称为泛型约束

TypeParameter extends ConstraintType

该语法中,TypeParameter表示形式类型参数名;extends是关键字;ConstraintType表示一个类型,
该类型用于约束TypeParameter的可选类型范围。

interface Point 
  x:number;
  y:number;

function identity<T extends Point>(x:T):T 
  console.log(x)
  return x;

identity(x:0,y:0) // x:0,y:0
identity(x:0,y:0,z:'0') // x:0,y:0,z:'0'
// identity(x:0) ,缺少y,编译错误

对于一个形式类型参数,可以同时定义泛型约束和默认类型,但默认类型必须满足泛型约束

function identity<T extends number = 0|1>()
identity<0>()

在泛型约束中,约束类型允许引用当前形式类型参数列表中的其他类型参数。例如,下例中形式类型参
数U引用了在其左侧定义的形式类型参数T作为约束类型:

<T,U extends T>

下例中,形式类型参数T引用了在其右侧定义的形式类型参数U:

<T extends U, U> #一个被U限制的T,还有一个U,两个泛型。

需要注意的是,一个形式类型参数不允许直接或间接地将其自身作为约束类型,否则将产生循环引用的
编译错误。例如,下例中的泛型约束定义都是错误的:

<T extends T> //错误
<T extends U,U extends T>//错误
  • 基约束

本质上,每个类型参数都有一个基约束(Base Constraint),它与是否在形式类型参数上定义了泛型约束无关。类型参数的实际类型一定是其基约束的子类型。对于任意的类型参数T,其基约束的计算规则有三个。

  • 规则一:如果类型参数T声明了泛型约束,且泛型约束为另一个类型参数U,那么类型参数T的基约束为类型参数U。示例如下:

    <T extends U> //类型参数T的基约束为类型参数U
    
  • 规则二: 如果类型参数T声明了泛型约束,且泛型约束为某一具体类型Type,**那么类型参数T的基约束为类型Type。**示例如下:

    <T extends boolean>
    
  • 规则三: 如果类型参数T没有声明泛型约束,那么类型参数T的基约束为空对象类型字面量“”。除了undefined类型和null类型外,其他任何类型都可以赋值给空对象类型字面量。

    <T>
    

常见错误:

interface Point 
    x:number;
    y:number;


function f<T extends Point>(args: T):T 
    return x:0,y:0 //报错

此例第7行,第一感觉可能是这段代码没有错误,因为返回值“ x: 0, y: 0 ”的类型是泛型约束Point类型的子类型。实际上,这段代码是错误的,因为f函数的返回值类型应该与传入参数arg的类型相同而不能仅满足泛型约束。

interface Point 
    x:number;
    y:number;


function f<T extends Point>(args: T):T 
    return x:0,y:0 as T

1.1.4 [P in K]: T

映射对象类型:

  • 映射对象类型是一种独特的对象类型,它能够将已有的对象类型映射为新的对象类型。
  • 例如,我们想要将已有对象类型T中的所有属性修改为可选属性,那么我们可以直接修改对象类型T的类
    型声明,将每个属性都修改为可选属性。除此之外,更好的方法是使用映射对象类型将原对象类型T映射
    为一个新的对象类型T‘,同时在映射过程中将每个属性修改为可选属性。

这里只讲解:映射对象类型声明

映射对象类型是一个类型运算符,它能够遍历联合类型并以该联合类型的类型成员作为属性名类型来构
造一个对象类型。映射对象类型声明的语法如下所示:

 readonly [P in K]?:T 

在该语法中,readonly是关键字,表示该属性是否为只读属性,该关键字是可选的;“?”修饰符表示该属
性是否为可选属性,该修饰符是可选的;in是遍历语法的关键字;K表示要遍历的类型,由于遍历的结果
类型将作为对象属性名类型,因此类型K必须能够赋值给联合类型“string | number |symbol”,因为
**只有这些类型的值才能作为对象的键;**P是类型变量,代表每次遍历出来的成员类型;T是任意类型,表
示对象属性的类型,并且在类型T中允许使用类型变量P。

映射对象类型的运算结果是一个对象类型。

type K = 'x' | 'y';
type T = number;
type MapddedObjectType = 
  readonly [P in K]?:T;

/*等同。
type MapddedObjectType = 
 readonly x?: number | undefined;
 readonly y?: number | undefined;

*/
1.1.5 汇总
type Record<K extends keyof any, T> = 
    [P in K]: T;
;
  • Record的键名可以是string | number | symbol三种类型其中一种,比如

    
    	"xx":T;
    	11:T;
    
    

    他的的键值是T,泛型。提供了上一层扩展的能力。

1.2 Recordable的展开讲解

declare type Recordable<T = any, K = string> = 
			Record<K extends null | undefined ? string : K, T>
1.2.1 declare

.d.ts 文件中的顶级声明必须以 “declare” 或 “export” 修饰符开头。

通过declare声明的类型或者变量或者模块,在include包含的文件范围内,都可以直接引用而不用去import或者import type相应的变量或者类型。

1.2.2 条件类型

条件类型与条件表达式类似,它表示一种非固定的类型。条件类型能够根据条件判断从可选类型中选择其一作为结果类型。

条件类型的定义

T extends U ? X: Y

在该语法中,extends是关键字;T、U、X和Y均表示一种类型。若类型T能够赋值给类型U(T是U的子类型),则条件类型的结果为类型X,否则条件类型的结果为类型Y。条件类型的结果类型只可能为类型X或者类型Y。

实例:

//string
type T0 = true extends boolean ? string : number;

// number
type T1 = string extends boolean ? string : number;

此例中的条件类型实际意义很小,因为条件类型中的所有类型都是固定的,因此结果类型也是固定的

实例2:

type TypeName<T> = T extends string 
? 'string'
: T extends number
? 'number'
: T extends  boolean
? 'boolean'
: T extends undefined
? 'undefined'
: T extends Function
? 'function'
: 'object';

type T0 = TypeName<'a'> //type T0 = "string"
type T1 = TypeName<> //type T1 = "object"

还有一种就是分布式条件类型。这里略过。

1.2.3 总结
/*
type Record<K extends keyof any, T> = 
    [P in K]: T;
;
*/

declare type Recordable<T = any, K = string> = 
			Record<K extends null | undefined ? string : K, T>

//等同
type Recordable<T = any, K = string> =
	 [P in K extends null | undefined ? string : K]: T; 

全局声明一个类型 Recordable,K=string. 所以基本上可以得出

Recordable<T> =  
	[string]:T;

1.3 AxiosResponse<Recordable>展开讲解

import		AxiosResponse	 from 'axios' //引入

//自定义的拦截器:axios的response中的使用。
service.interceptors.response.use(
  (function(response: AxiosResponse<Recordable>) 
      //...
  
)


//axios源码
export interface AxiosResponse<T = any, D = any>  
  data: T;
  status: number;
  statusText: string;
  headers: AxiosResponseHeaders;
  config: AxiosRequestConfig<D>;
  request?: any;

这个Recordable占了T的位置,所以影响到了data,等同如下

//AxiosResponse<Recordable>
interface AxiosResponse<Recordable> 
	data:   
        [string]:T;
    

也就是说data的键名被约束了,只能是string.

axios response.data 返回 HTML 而不是对象

【中文标题】axios response.data 返回 HTML 而不是对象【英文标题】:Axios response.data return HTML instead of an object 【发布时间】:2020-10-06 04:35:01 【问题描述】:

我需要有关 Axios 的帮助。 我在 Laravel 6(带有包 SPARK)和 VueJS(版本 2)上开发了一个 SPA webapp。

在一个 Vue 组件上,我想从我的 bdd 中检索数据。 因此,我使用 Axios 在 API uri 上发出 get 请求。 但是,当我调用 Axios 请求时,响应对象中的数据字段是 HTML 代码。

这是我的代码:

routes/web.php

Route::get('/', function()
    return view('welcome');
);

Route::middleware('auth')->get('/any?', function ()
    return view('documents');
)->where('any', '[\/\w\.-]*');

“欢迎”视图是一个刀片页面,如果用户未通过身份验证,则重定向到“/login”。 否则,它会重定向到“/home”。 链接“/home”是在 app.js 中的 vue-router 上定义的。 另一条路线是显示 webapp 的独特方式(它是一个单页应用程序)。 Vue 实例化在“passeport”视图中。

resources/js/app.js

import 'es6-promise/auto'

require('spark-bootstrap');
require('./components/bootstrap');

window.Vue = require('vue');

import VueRouter from 'vue-router';
Vue.use(VueRouter);

import VueAxios from 'vue-axios';
import axios from 'axios';
Vue.use(VueAxios, axios);

Vue.component('index', require('./components/pages/index').default);

import Dashboard from './components/pages/dashboard.vue';
...

const routes = [ 
    
        name: 'dashboard',
        path: '/home',
        component: Dashboard,
    ,
    ...
]

const router = new VueRouter( 
    history: true,
    mode: 'history', 
    routes: routes
);

var app = new Vue(
    mixins: [require('spark')],
    router,
);

在Vue实例化中添加了router包。 它与 spark 组件处于相同的上下文中(由#spark-app 元素标识)

资源/视图/documents.blade.php

@extends('spark::layouts.app')

@section('content')
    <div id="app">
        <index :user="user"></index>
    </div>
@endsection

这是为任何路径返回的视图。 在 spark::layout.app 中,只有一个 id="spark-app" 和 @yield('content') 的 div。

资源/js/components/pages/index.vue

<template>
    <transition name="fade">
      <Component :is="layout" :user="user">
        <router-view :layout.sync="layout" :user="user"></router-view>
      </Component>
    </transition>
</template>
.
.
<script>
    const default_layout = "default";

    export default
      props: ['user'],

      data()
        return
          layout: 'div',
        
      ,
    
</script>

它只是带有布局的路由器视图配置的vue组件。

resources/js/components/pages/dashboard.vue

<template>
...
</template>

<script>
import Default from './../layouts/Default.vue'
    export default 
        props: ['user'],

        data: function () 
            return 
                documents: []
            
        ,
        created()  
            this.$emit('update:layout', Default);
        ,
        mounted()
            // extract passeports's informations
            this.axios.get('/api/documentslist').then(response => 
                console.log(response);
                this.documents= response.data.data;
            );
        
    
</script>

这里我调用了routes/api.php中定义的documentslist API。

routes/api.php

Route::middleware('auth:api')->group(function () 
    Route::get('/user', function (Request $request) 
        return $request->user();
    );
    Route::get('/documentslist', 'DocumentController@list');
);

app/http/Controllers/DocumentController.php

...
public function list(Request $request)
   return DocumentCollection(Document::all());

...

当我转到“/home”时,我在 Vue(或 javascript 控制台日志)中验证了“文档”数据,并且 response.data = "\r\n\r\n\r\n (.. .) v-if=\"notification.creator\" :src=\"notification.creator.photo_url\" class=... (10024 总长度)"

但是,DocumentController 中的 list 方法必须返回文档列表,而不是 HTML 代码。

此外,我使用 Passport Laravel 通过登录和 API 令牌来统一身份验证。 并且 Axios 请求在没有 SPA 结构的同一个项目中工作。

我希望我在问题解释中很清楚,并且我忘记了要理解的任何细节。

谢谢。

【问题讨论】:

【参考方案1】:

你可以简单地从你的控制器中返回

return response()->json(Document::all());

【讨论】:

感谢您的回答。但这并不能解决问题。我认为Axios无法访问API链接。这不是错误,因为响应发送状态 200。也许这是我在 app.js 中使用“import ... from ...”声明组合的方式。在 NO SPA 版本中,我使用 Vue.component(...) 在 js 文件中定义我的组件,并在 components/bootstrap.js 中使用 require() 声明它,它可以工作。但是,如何声明这样的组件并将其添加到路由器中?【参考方案2】:

我怀疑 API 链接使用 SPA 路由重定向(在 routes/web.php 中,任何路径都返回“home”视图)。 我尝试在 routes/web.php 中更改路由。

而不是:

Route::middleware('auth')->get('/any?', function ()
    return view('home');
)->where('any', '[\/\w\.-]*');

我把这个:

Route::middleware('auth')->get('/home', function ()
    return view('home');
);

这样,这条路由不考虑所有路径。

而且...它有效! 但是……这不是我想要的:( 事实上,如果没有带有 '/any?' 的路由,SPA 就无法工作。

我不明白为什么这种方法适用于 Laravel 中的另一个项目(没有 Spark 包),并且不适用于这个项目。

【讨论】:

【参考方案3】:

我不了解 laravel,但是当您调用的路线不正确时,就会出现此问题。做一些努力并检查应用程序最终调用的路由,同时查看您的代理设置。

****我是 NodeJs 开发者而不是 Laravel

【讨论】:

以上是关于前端杂文 axios的AxiosResponse分析的主要内容,如果未能解决你的问题,请参考以下文章

axios

axios实现

前端-杂文-数据压缩&模块化开发

找不到模块:错误:无法解析“./lib/axios”

如何在项目声明文件中使用 Axios 类型

vue中axios怎么分服务