在使用Nuxt3
框架开发的时候,我们会经常要从接口请求数据, 必然会遇到 useFetch
但是会有一些使用上的注意事项
环境
"nuxt": "^3.11.2",
"vue": "^3.4.27",
node: 'v20.12.2'
测试环境,截止2024-06-07
Nuxt 和 vue 均为最新版本
code
案例1
<template>
<div>
<h3>useFetch陷阱</h3>
<hr />
<div>
<input type="text" v-model="name" />
<button @click="onSubmit">提交</button>
</div>
</div>
</template>
<script setup>
const name = ref('')
const onSubmit = async () => {
const res = await useFetch('/api/checkName', {
method: 'post',
body: {
name
}
})
}
</script>
代码看上去没啥毛病, 很基础,很简单,
但是, 我们在 input 中持续输入, 查看 network
发现,一直在调用接口
啥情况?
因为 useFetch 是响应式的, 会监听他的 options
的值 和 url
, 值发生变化,就会触发他重新执行,也就导致,输入的时候, 一直在发请求
案例2
<script setup>
let age = ref(12)
let testUrl = computed(() => {
return '/api/checkName?age='+ age.value
})
// url在下面变动
let r2 = await useFetch(testUrl, {
method: 'post'
})
onMounted(() => {
setInterval(() => {
age.value++
console.log('r2 定时器里面的', r2);
}, 2000);
})
</script>
url的改变,导致请求也会自动触发
现在解决一下 案例1 里面的问题
既然 useFetch 有坑,我们在使用的时候,要分清楚场景
解决方式一:
$fetch 替代 useFetch
<template>
<div>
<h3>useFetch陷阱</h3>
<hr />
<div>
<input type="text" v-model="name" />
<button @click="onSubmit">提交</button>
</div>
</div>
</template>
<script setup>
const name = ref('')
const onSubmit = async () => {
// 解决方式, 使用 $fetch
const res = await $fetch('/api/checkName', {
method: 'post',
body: {
name: name.value
}
})
}
</script>
$fetch 不会去监听 url 和 options的改变
解决方式2:
手动调用 execute
或者 refresh
, 这2个是普通函数, 效果一样。
<template>
<div>
<h3>useFetch陷阱</h3>
<hr />
<div>
<input type="text" v-model="name" />
<button @click="onSubmit">提交</button>
</div>
</div>
</template>
<script setup>
const name = ref('')
const {error, execute, refresh} = useFetch('/api/checkName', {
method: 'post',
// 不立刻发出请求
immediate: false,
// 关闭监听
watch: false,
body: {
name
}
})
const onSubmit = async () => {
// 手动调用
await execute()
// await refresh()
// console.log('错误', error);
}
</script>
- 复杂了点,不方便,单个SFC 组件代码复杂的时候,不利于维护
注意事项
useFetch 调用场景是有要求的
- 顶层 setup函数 (上面
案例1
就不是在顶层调用 useFetch, 在函数里面调用, 不行。) - plugin 插件
- route middleware 路由中间件
useFetch 这个组合函数,只能在上面的场景中调用。
$fetch 注意事项
<script setup>
// 有问题的写法
await $fetch('/api/getXXX')
</script>
会触发2次调用,一次在服务端,一次在客户端, 所以 $fetch 适合 手动触发,与用户交互的场景调用接口, 不在 顶层 setup上下文 的场景调用接口
const btn = document.querySelector('.btn')
btn.addEventListener('click', async() => {
// 适合
const res = await $fetch('/api/xxxxx')
})
<script setup>
// 不行, 这里就在 setup 的上下文中。
// $fetch('/api/xxxx')
// 不在setup 顶层上下文-适合
onMounted(async () => {
// 适合
await $fetch('/api/xxxx')
})
</script>
也提供一个在 plugin
中调用的案例
plugins/check.ts
export default defineNuxtPlugin(async (nuxtApp) => {
const token = useCookie('token')
const {data, error} = await useFetch('/api/ping', {
query: {
token: token.value
}
})
if (error.value) {
console.log('请求报错了');
console.log(error.value);
} else{
console.log('请求得到的数据');
console.log(data.value);
}
})
express 另外启的一个服务,提供一个基础接口。
router.get('/api/ping', (req, res) => {
console.log('解析一下参数');
console.log(req.query.token);
res.json({
code: 0,
data: {
token: req.query.token,
txt: req.query.token ? '有值' : '没值'
},
message: 'suceess'
})
})