axios 与 TypeScript新闻案例
准备项目的基础结构
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数据获取和类型定义
-
封装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
-
使用
<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>