Vue 中获取接口数据的一些方法
前言
在具有多个组件的大型应用,获取数据的顺序是一个常见的问题——在加载组件之前或之后?。
在这篇文章中,我们将了解在 Vue 复杂场景中获取数据的一些方法。同时我们还将了解如何使用 Vue 3 的组合 API 结合 stale-while-revalidate
库进行数据获取。
路由导航时获取数据
当涉及各种路由组件时,数据获取可能会变得复杂。在某些情况下,我们可能希望在导航特定时机显示一些数据。 Vue Router 通过提供在导航之前或之后获取数据的生命周期来处理。让我们来看看这两种情况。
导航前获取数据
Vue Router 提供了许多生命周期 / 钩子 来处理导航前的数据获取:
beforeRouteEnter()
钩子接受三个参数——to
、from
和next
。它用于在对组件进行导航之前获取组件中所需的数据beforeRouteUpdate()
钩子也采用与beforeRouteEnter()
相同的参数,用于使用已获取的数据更新组件
我们创建带有两个路由组件,显示我们银行账户的当前和分类帐余额。
当显示我们的分类帐余额时,我们希望在导航到组件之前获取数据。首先,我们将创建为分类帐余额提供数据的模拟API数据返回:
// LedgerBalance.js
export default (callback) => {
const LedgerBalance = "1500 元";
setTimeout(() => {
callback(null, LedgerBalance);
}, 3000);
};
接下来,我们将创建分类帐余额组件:
<!-- LedgerBalance.vue -->
<template>
<div>分类帐余额: {{ balance }}</div>
</template>
<script>
import ledgerBalance from "../scripts/LedgerBalance";
export default {
name: "Ledger",
data() {
return { balance: null };
},
// 组件已被加载
beforeRouteEnter(to, from, next) {
ledgerBalance((err, balance) => {
next(vm => vm.setBalance(err, balance));
});
},
// 在调用 beforeRouteUpdate 时,组件被加载并且路由发生变化
beforeRouteUpdate(to, from, next) {
this.balance = null;
ledgerBalance((err, balance) => {
this.setBalance(err, balance);
next();
});
},
methods: {
setBalance(err, balance) {
if (err) {
console.error(err);
} else {
this.balance = balance;
}
}
}
};
</script>
在 LedgerBalance.vue
中, beforeRouteEnter()
钩子确保在加载 Ledger
组件之前从 LedgerBalance.js
获取数据。
接下来当 Ledger
被加载,路由发生变化时, beforeRouteUpdate()
中的 setBalance()
方法就会被用来设置数据。
然后我们将为我们的分类账余额定义路由路径:
// main.js
import Vue from "vue";
import VueRouter from "vue-router";
import App from "./App";
import Ledger from "./components/LedgerBalance";
Vue.use(VueRouter);
const router = new VueRouter({
routes: [
{ path: "/Ledger", component: Ledger }
]
});
new Vue({
render: (h) => h(App),
router
}).$mount("#app");
定义路由路径后,我们将其包含在 APP 中:
<!-- App.vue -->
<template>
<div id="app">
<div class="nav">
<router-link to="/Ledger">分类账余额</router-link>
</div>
<hr>
<div class="router-view">
<router-view></router-view>
</div>
</div>
</template>
<script>
export default { name: "App" };
</script>
当导航到 Ledger
组件时,我们可以观察到路由没有及时跳转,而是等待 LedgerBalance.js
中的 setTimeout()
函数执行完毕之后才进行跳转
导航后获取数据
在某些情况下,我们可能希望在导航到组件后获取数据。当我们处理实时变化的数据时很有用。比如一个账户的当前余额。在这种情况下,我们首先定义处理当前余额数据的函数:
// CurrentBalance.js
export default (callback) => {
const CurrentBalance = "1000 元";
setTimeout(() => {
callback(null, CurrentBalance);
}, 3000);
};
接下来,在创建我们的 CurrentBalance
组件时,我们将使用 created()
生命周期钩子来调用我们的数据获取方法 fetchBalance()
:
/<!-- CurrentBalance.vue -->
<template>
<div>你的当前余额是{{ balance }}</div>
</template>
<script>
import currentBalance from "../scripts/CurrentBalance";
export default {
name: "Current",
data() {
return { balance: null };
},
created() {
this.fetchBalance();
},
methods: {
fetchBalance() {
currentBalance((err, balance) => {
if (err) {
console.error(err);
} else {
this.balance = balance;
}
});
}
}
};
</script>
考虑到数据将在导航后获取,通过增加一个加载组件(如进度条或骨架屏)交互会更好一点
Stale-while-revalidate
传统上,Vue 应用程序使用 mounted()
钩子来获取数据。从 API 获取数据类似于下面的代码示例:
<template>
<div :key="car.carmodel" v-for="car in cars">
{{ car.carmodel }}
</div>
</template>
<script>
export default {
name: 'Cars',
data() {
return {
cars: []
}
},
mounted() {
fetch('/api/cars')
.then(res => res.json())
.then(carJson => {
this.cars = carJson
})
}
}
</script>
假设此处需要更多数据,这可能会导致从多个地方获取数据。在不同组件之间导航将导致为每个导航发出 API 请求。这些 API 请求可能会造成堵塞并给用户带来糟糕的体验:
<template>
<div v-if="about">
<div>{{ about.car.model }}</div>
<div>{{ about.bio }}</div>
</div>
<div v-else>
<Loading />
</div>
</template>
<script>
export default {
name: 'Bio',
props: {
model: {
type: String,
required: true
}
},
data() {
return {
about: null
}
},
mounted() {
fetch(`/api/car/${this.model}`)
.then(res => res.json())
.then(car => fetch(`/api/car/${car.id}/about`))
.then(res => res.json())
.then(about => {
this.about = {
...about,
car
}
})
}
}
</script>
理想情况下,需要一种有效的方法来缓存已经访问过的组件,这样每个已经访问过的组件都可以很容易地呈现数据,即使它被导航离开和返回时也是如此。这就需要永达stale-while-revalidate。
Stale-while-revalidate (swr)
使我们能够在获取额外请求的数据时缓存已获取的数据。在上面的代码示例中,每个查看汽车简介的请求也会重新触发查看汽车模型的请求。
使用 swr
,我们可以缓存汽车的模型,这样当用户请求汽车的简介时,可以在获取汽车简介的数据时看到模型。
在 Vue 中,stale-while-revalidate 概念是通过一个库 (swrv) 实现的,该库主要使用 Vue 的组合 API。采用更具可扩展性的方法,我们可以使用 swrv
来获取汽车的详细信息,如下所示:
import carInfo from './carInfo'
import useSWRV from 'swrv'
export default {
name: 'Bio',
props: {
model: {
type: String,
required: true
}
},
setup(props) {
const { data: car, error: carError } = useSWRV(
`/api/cars/${props.model}`,
carInfo
)
const { data: about, error: aboutError } = useSWRV(
() => `/api/cars/${car.value.id}/about`,
carInfo
)
return {
about
}
}
}
第一个 useSWRV
挂钩使用 API 的 URL 作为每个请求的唯一标识符,并且还接受 carInfo
函数。 carInfo
是一个异步函数,用于获取有关汽车的详细信息,它还使用 API 的 URL 作为唯一标识符。
第二个 useSWRV
挂钩监视第一个挂钩的变化,并根据这些变化重新验证其数据。 data
和 error
值分别针对 carInfo
函数发出的请求的成功和失败响应进行填充。
总结
- 要在路由跳转前获取数据,使用 Vue-Router 提供的生命周期
beforeRouterEnter
- 在页面加载后获取数据,使用 Vue 生命周期
created
或者mounted
,并结合 loading 提高用户体验 - 要在路由跳转前、页面加载后 等多处获取数据,提高交互体验,用第三方库 Stale-while-revalidate
(swr)
全文完
谢谢!
转载自:https://juejin.cn/post/7202796308608041019