【前端】Vue项目:旅游App-(8)city:标签页Tabs动态数据:网络请求axios与request、数据管理store与pinia、各种封装

目标

上一篇搭建了搜索框和Tab栏:【前端】Vue项目:旅游App-(7)city:搜索框search和标签页Tabs

本篇目标:

样式不变:
在这里插入图片描述
数据改为动态的:

在这里插入图片描述
数据从服务器获取:
http://123.207.32.32:1888/api/city/allhttp://www.codercba.com:1888/api/city/all

注意将网络请求request和数据管理pinia 封装

过程与代码

安装相关库

本篇要将网络请求到的数据进行处理,要用到pinia

npm install pinia

本篇使用的网络请求库:axios

npm install axios

封装网络请求相关代码

相关参考:coderwhy ts封装axios库 除去冗余代码 可直接使用 - 掘金 (juejin.cn)

service文件夹用来提供封装好的各种服务。我们在其中建立一个文件夹request,表示用来提供网络请求服务。

其中的index.js文件:封装好的axios网络请求。

import axios from "axios";
import { useLoadingStore } from "@/store/modules/loading";
import { baseURL, TIMEOUT } from "./config";
const loadingStore = useLoadingStore();
class HYRequest {
  constructor(baseURL) {
    this.instance = axios.create({
      baseURL,
      timeout: TIMEOUT,
    });
  }

  request(config) {
    loadingStore.changeLoading(true);
    return new Promise((resolve, reject) => {
      this.instance
        .request(config)
        .then((res) => {
          resolve(res.data);
        })
        .catch((err) => {
          console.log("request err:", err);
          reject(err);
        })
        .finally(() => {
          loadingStore.changeLoading(false);
        });
    });
  }

  get(config) {
    return this.request({ ...config, method: "get" });
  }

  post(config) {
    return this.request({ ...config, method: "post" });
  }
}

export default new HYRequest(baseURL);

其中的config.js用来封装网络请求相关的配置:

const baseURL = "http://123.207.32.32:1888/api";
const TIMEOUT = 5000;

export { baseURL, TIMEOUT };

注意,index.js代码中还有一个useLoadingStore的导入。

store中新建modules文件夹,里面的loading.js文件:

import { defineStore } from "pinia";

export const useLoadingStore = defineStore("loadingStore", {
    state: () => {
        return {
            showLoading: false,
        };
    },
    getters: {
        isLoading: (state) => state.showLoading,
    },
    actions: {
        changeLoading(isLoading) {
            this.showLoading = !!isLoading;
        },
        toggleLoading() {
            this.showLoading = isLoading;
        },
    },
});

网络请求数据

我们用封装好的网络请求库来请求数据。

注意:

  • request中的index导出的是一个对象(new HYRequest())
  • HYRequest.get的参数是对象(...config

代码:

import HYRequest from '@/service/request'

function getAllCity() {
    // request的index导出的是一个对象
    return HYRequest.get({
        // 参数也是一个对象
        url:'/city/all'
    }).then(res => {
        console.log(res)
    })
}

getAllCity()

效果:

得到了数据。观察一下数据,可知:获取到的数据是一个对象,里面的data属性是我们页面需要的数据。

data中有两个属性,分别对应两个tab的数据。

在这里插入图片描述

网络请求数据操作封装

请求到数据之后,我们要进行一些思考。

city.vue写的是显示和选择城市的页面,我们在这个页面里写“网络请求数据”的逻辑是否合适?是否利于维护?答案是否定的。

实际上,我们可以将city页面的所有网络请求的操作都写到一个文件里,city.vue只需要在需要数据时调用即可。简而言之,我们需要对city页面的所有网络请求操作进行封装

我们在service文件夹中建立modules文件夹,所有页面的网络请求操作都放在这里。modules中建立city.js,所有city相关的网络请求操作都放在这里。

代码:

// 此文件保存所有city页面的网络请求
import HYRequest from '@/service/request'

export default function getAllCity() {
    // request的index导出的是一个对象
    return HYRequest.get({
        // 参数也是一个对象
        url: '/city/all'
    })
}

city.vue:

import getAllCity from '@/service/modules/city'

getAllCity()

接下来我们再进行一些思考。

我们在city中只需要导入就可以得到数据了。但是,显然city不会只有一次网络请求数据。也就是说,每次网络请求都需要import一次,这样也会让city.vue代码变得复杂。更重要的是,这些import是相似的,我们可以把它们也封装起来。

我们在service文件夹下新建index.js,里面保存所有会被使用的service:导出所有导入的模块。

// 此文件导入并导出所有要使用的service

export * from '@/service/modules/city'

在city.vue中调用:

import { getAllCity } from "@/service";

getAllCity()

效果:

在这里插入图片描述

pinia管理数据并封装

接下来我们再进行一些思考。

我们已经把所有的网络请求都封装了,在vue页面只需要调用网络请求获取数据使用就行了。但是,我们获取到的数据是一个对象,对象中的data才是我们需要的数据。

思考:在vue的页面中进行处理数据的逻辑是否合适?是否利于维护?

答案是否定的。显然,我们既然已经完成了网络请求的封装,自然也会想到要完成数据处理和存储的封装。

预想:我们把请求到的数据和对数据的处理封装到一个文件里,把处理好的数据导出。在vue的页面中只需要直接使用处理好的数据即可。

这里就要用到pinia。

在store的modules中新建city.js,city.vue页面所有的进行网络请求和数据都封装到这里。

// city.vue页面所有的进行网络请求和数据都封装到这里
import { getAllCity } from "@/service";
import { defineStore } from "pinia";

const useCityStore = defineStore('city', {
    state: () => {
        return {
            allCity: {}
        }
    },
    actions: {
        // 调用网络请求
        async fetchAllCity() {
            const res = await getAllCity()
            this.allCity = res.data
        }
    }
})

export default useCityStore

在vue中:

const cityStore=useCityStore()
cityStore.fetchAllCity()
// cityStore是响应式的
const { allCity } = storeToRefs(cityStore)
console.log(allCity)

效果:

在这里插入图片描述

tab栏改为动态数据

tab栏改为动态数据后,若服务器那边的数据发生了改变(如data有了三个属性),我们这里的代码是不用改的。

<van-tabs v-model:active="TabActive">
    <template v-for="(value, key, index) in allCity">
        <van-tab :title="value.title"></van-tab>
    </template>       
</van-tabs>

效果

不变。

在这里插入图片描述

本篇总结

本篇写了很多的封装,这里对它们之间的关系进行总结。

city.vue需要的数据都存在store中。
store会进行网络请求得到数据,网络请求的代码在service。

本项目views文件夹中所有页面需要的数据都在store的modules中,store的modules所有存储的数据来源都是网络请求得到的。所有的views页面的网络请求都存在service的modules中,store只需调用就可以得到数据。

service中的request是封装axios库。

在这里插入图片描述

总代码

修改或新建的文件

在这里插入图片描述

service

存放各种网络请求。

index

把要用的所有service导入并导出。

// 此文件导入并导出所有要使用的service

export * from '@/service/modules/city'
modules的city

封装city页面的所有网络请求。

// 此文件保存所有city页面的网络请求
import HYRequest from '@/service/request'

export function getAllCity() {
    // request的index导出的是一个对象
    return HYRequest.get({
        // 参数也是一个对象
        url: '/city/all'
    })
}


request的config

封装网络请求相关配置。

const baseURL = "http://123.207.32.32:1888/api";
const TIMEOUT = 5000;

export { baseURL, TIMEOUT };

request的index

封装axios。

import axios from "axios";
import { useLoadingStore } from "@/store/modules/loading";
import { baseURL, TIMEOUT } from "./config";
const loadingStore = useLoadingStore();
class HYRequest {
  constructor(baseURL) {
    this.instance = axios.create({
      baseURL,
      timeout: TIMEOUT,
    });
  }

  request(config) {
    loadingStore.changeLoading(true);
    return new Promise((resolve, reject) => {
      this.instance
        .request(config)
        .then((res) => {
          resolve(res.data);
        })
        .catch((err) => {
          console.log("request err:", err);
          reject(err);
        })
        .finally(() => {
          loadingStore.changeLoading(false);
        });
    });
  }

  get(config) {
    return this.request({ ...config, method: "get" });
  }

  post(config) {
    return this.request({ ...config, method: "post" });
  }
}

export default new HYRequest(baseURL);

store

封装pinia。

modules的city

city页面的所有数据。

// city.vue页面所有的进行网络请求和数据都封装到这里
import { getAllCity } from "@/service";
import { defineStore } from "pinia";

const useCityStore = defineStore('city', {
    state: () => {
        return {
            allCity: {}
        }
    },
    actions: {
        // 调用网络请求
        async fetchAllCity() {
            const res = await getAllCity()
            this.allCity = res.data
        }
    }
})

export default useCityStore
modules的loading

封装网络请求需要的模块。

import { defineStore } from "pinia";

export const useLoadingStore = defineStore("loadingStore", {
    state: () => {
        return {
            showLoading: false,
        };
    },
    getters: {
        isLoading: (state) => state.showLoading,
    },
    actions: {
        changeLoading(isLoading) {
            this.showLoading = !!isLoading;
        },
        toggleLoading() {
            this.showLoading = isLoading;
        },
    },
});

city.vue

将tab的数据改为动态的。

<template>
    <div class="city top-page">
        <!-- show-action:显示 “取消”  -->
        <van-search shape="round" v-model="value" show-action placeholder="城市/区域/位置" @search="onSearch"
            @cancel="onCancel" />
        <van-tabs v-model:active="TabActive">
            <template v-for="(value, key, index) in allCity">
                <van-tab :title="value.title"></van-tab>
            </template>
        </van-tabs>
    </div>
</template>

<script setup>
import { ref } from 'vue';
import { showToast } from 'vant';
import useCityStore from '@/store/modules/city'
import { storeToRefs } from 'pinia';

const value = ref('');
const TabActive = ref(0);
const onSearch = (val) => showToast(val);
const onCancel = () => {
    showToast('取消');
}

// tabs的数据
const cityStore = useCityStore()
cityStore.fetchAllCity()
// cityStore是响应式的
const { allCity } = storeToRefs(cityStore)

</script>

<style lang="less" scoped>

</style>

参考

Cannot read properties ofundefined(reading‘data‘)
coderwhy ts封装axios库 除去冗余代码 可直接使用 - 掘金 (juejin.cn)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

karshey

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

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

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

打赏作者

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

抵扣说明:

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

余额充值