25 axios 与 TypeScript 新闻案例

本文介绍了如何在TypeScript项目中使用axios进行数据请求,包括泛型设置响应数据类型、封装axios、频道渲染、导航切换以及列表更新的实现,同时展示了如何处理接口数据和自定义事件的使用。

在这里插入图片描述
准备项目的基础结构

1 axios 与 TypeScript{#axios-and-ts}

掌握:掌握axios配合泛型设置响应数据类型

<script setup lang="ts">
import axios from 'axios'
// 频道对象
type ChannelItem = {
  id: number;
  name: string;
};

// 频道接口响应数据
type ChannelResData = {
  data: {
    channels: ChannelItem[];
  };
  message: string;
};
axios
  .request<ChannelResData>({
    url: 'http://geek.itheima.net/v1_0/channels',
  })
  .then((res) => {
    // res.data 的类型就是 ChannelResData
    console.log(res.data.data.channels[0].name);
  });
</script>

小结:

  • 使用axios的时候怎么给返回数据提供类型?
    • axios.request<数据类型>() 其他请求方法类似
  • 提供的类型要注意啥?
    • 类型需要根据接口返回的数据类声明,或者根据接口文档

2 axios封装优化

优化axios数据获取和类型定义

  1. 封装axios

    import axios from 'axios'
    
    const request = axios.create({
      baseURL: 'http://geek.itheima.net/v1_0'
    })
    
    request.interceptors.request.use((config) => {
      return config
    })
    
    request.interceptors.response.use((res) => {
      return res.data?.data
    })
    
    export default request
    
  2. 使用

    <script setup lang="ts">
    import request from '../utils/request';
    
    // 频道对象
    type ChannelItem = {
      id: number;
      name: string;
    };
    
    // 频道接口响应数据
    type ChannelResData =  {
      channels: ChannelItem[];
    }
    
    axios
      .request<any, ChannelData>({
        url: '/channels',
      })
      .then((res) => {
        // res 的类型就是 ChannelResData
        console.log(res.channels[0].name);
      });
    </script>
    

3 频道渲染{#case-channel}

完成:axios获取数据后频道列表渲染

步骤:

  • 提取类型到 types 目录
  • 在组件初始化通过 axios.get 获取数据
  • 进行渲染

types/data.d.ts

// 频道对象
export type ChannelItem = {
  id: number;
  name: string;
};

// 频道接口响应数据
export type ChannelResData =  {
    channels: ChannelItem[];
}

ChannelNav.vue

<script setup lang="ts">
import request from '../utils/request';
import { onMounted, ref } from 'vue';
import { ChannelItem, ChannelResData } from '../types/data'
// 创建响应式数据
const channels = ref<ChannelItem[]>([])
onMounted(async ()=>{
  const res = await request.get<any, ChannelResData>('/channels')
  // 给响应式数据赋值
  channels.value = res.channels
})
</script>

<template>
  <div class="channel-nav">
    <nav class="list">
      <a
        class="item"
        href="javascript:;"
        v-for="(item, i) in channels"
        :key="item.id"
      >
        {{item.name}}
      </a>
    </nav>
  </div>
</template>

4 导航切换{#case-nav-toogel}

完成:频道导航切换效果

  • 切换的频道ID将来需要给 ArticleList 组件使用,所以在 App 组件定义数据
  • ChannelNav 改变频道的时候,通过自定义事件传递给 App 组件使用和修改

默认选中

App.vue

<script setup lang="ts">
import ChannelNav from './components/ChannelNav.vue';
import ArticleList from './components/ArticleList.vue'
import { ref } from 'vue';

-// 存储选中频道ID,因为切换频道ID的时候 列表需要根据频道ID更新
+ const channelId = ref(0)
</script>

<template>
+  <ChannelNav :channelId="channelId" />
  <ArticleList />
</template>

ChannelNav.vue

// 2.完成切换效果
+defineProps<{ channelId: number }>()
</script>

<template>
  <div class="channel-nav">
    <nav class="list">
      <a
        class="item"
+        :class="{active: channelId === item.id}"
        href="javascript:;"
        v-for="item in channels"

进行切换

ChannelNav.vue

+const emit = defineEmits<{
+  (e: 'changeChannel', id: number): void;
+}>();
</script>

<template>
  <div class="channel-nav">
    <nav class="list">
      <a
        class="item"
        :class="{ active: channelId === item.id }"
        href="javascript:;"
        v-for="item in channels"
        :key="item.id"
+        @click="emit('changeChannel', item.id)"
      >
        {{ item.name }}
      </a>
    </nav>
  </div>
</template>

App.vue

<ChannelNav :channelId="channelId" @change-channel="channelId = $event"/>

5 列表更新{#case-list}

实现:频道切换后列表更新

步骤:

  • 声明接口数据的类型
  • 监听频道ID变化,开启默认执行
  • 发起请求,获取数据
  • 完成渲染

代码:

  • 类型
    types/data.d.ts
// 文章对象
export type ArticleItem = {
  art_id: string;
  aut_id: string;
  aut_name: string;
  comm_count: number;
  cover: {
    cover: number;
    images: string[];
  };
  is_top: number;
  pubdate: string;
  title: string;
};

// 文章接口响应数据
export type ArticleResData = {
  pre_timestamp: string;
  results: ArticleItem[];
}
  • 监听频道ID变化,开启默认执行,获取数据

App.vue

<ArticleList :channelId="channelId"/>

ArticleList.vue

import request from '../utils/request';
import { ref, watch } from 'vue';
import { ArticleItem, ArticleResData } from '../types/data';

const props = defineProps<{ channelId: number }>();

const articles = ref<ArticleItem[]>([]);
watch(
  () => props.channelId,
  async () => {
    const res = await request.get<any, ArticleResData>(
      `/articles`,
      {
        params: {
          channel_id: props.channelId,
          timestamp: Date.now(),
        },
      },
    );
    articles.value = res.results;
  },
  { immediate: true }
);
  • 渲染
<template>
  <div class="article-list">
    <div class="article-item" v-for="item in articles" :key="item.art_id">
      <p class="title">{{ item.title }}</p>
      <img
        v-for="(src, i) in item.cover.images"
        :key="i"
        class="img"
        :src="src"
        alt=""
      />
      <div class="info">
        <span>{{ item.aut_name }}</span>
        <span>{{ item.comm_count }}评论</span>
        <span>{{ item.pubdate }}</span>
      </div>
    </div>
  </div>
</template>
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

THE ORDER

你的鼓励是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值