将 onMounted 中定义的常量提供给上下文
Provide const defined in onMounted to the context
我面临这样一种情况,我需要创建一个 API 调用,但在安装组件之前不应触发它。
这就是我到目前为止所得到的:
onMounted(async() => {
const {
data,
refresh,
pending,
error
} = await useApi(`/profile/${$auth.user.profile.sid}`, null, ['data']);
});
const computedWaiting = computed<boolean>(() => {
return pending.value === true;
});
现在的问题是,computedWaiting
抛出错误,因为 pending
未定义。所以我需要破坏 useApi
-response,它基本上是一个 https://github.com/unjs/ohmyfetch-result.
这是 useApi 方法(取自此 GitHub-问题:https://github.com/nuxt/framework/discussions/4504):
import {FetchOptions} from "ohmyfetch";
const csrf_cookie: string = "XSRF-TOKEN";
/**
* Return the cookies needed by "Sanctum", browser will handle them automatically.
*/
export const useFetchCookies = async () => {
await $fetch.raw("/backend/sanctum/csrf-cookie", {
credentials: "include" // Allow browser to handle cookies
});
};
/**
* Api call using nuxt `useFetch`
*
* @see {@link https://github.com/unjs/ohmyfetch#readme} ~ ohmyfetch Docs
* @param url
* @param options
* @param pick
*/
export const useApi = async (url: string, options?: FetchOptions, pick?: any) => {
// First we verify if the `xsrf-token` is present on the browser cookies
let token = useCookie(csrf_cookie)?.value;
if (!token) {
// If not present we will re fetch all cookies, the browser will
// handle them automatically so we don't need to do anything
await useFetchCookies();
// Load the new token value to use it in the `headers`
token = useCookie(csrf_cookie).value;
}
// Here we will create a default set of headers for every request
// if present we will also spread the `headers` set by the user
// then we will delete them to avoid collision in next spread
const headers: HeadersInit = {
Accept: "application/json",
"Cache-Control": "no-cache",
"X-XSRF-TOKEN": token,
...options?.headers
};
// At this point all the `headers` passed by the user where correctly
// set in the defaults, now we will spread `options` to remove the
// `headers` attribute so we don't spread it again in `useFetch`
const opts: FetchOptions = options ? (({headers, ...opts}) => opts)(options) : null;
return useLazyFetch(`/backend/api${url}`, {
server: false,
credentials: "include", // Allow browser to handle cookies
headers,
...opts,
pick: pick
});
};
了解 useFetch
来自 https://v3.nuxtjs.org/guide/features/data-fetching, which basically uses ohmyfetch as a composable itself: https://github.com/nuxt/framework/blob/46c656c4b85c3622b99a7c4f6a01f5b11c830be6/packages/nuxt/src/app/composables/fetch.ts#L18-L59
可能会有所帮助
那么如何在 onMounted 之后最优雅地暴露 data
、refresh
、pending
和 error
?
在 onMounted
中调用可组合项而没有特定目的是不正确的。 Vue 可组合项通常直接在设置函数中使用,因此可以立即访问结果。是否可以在其他地方使用它们取决于它们的实现。
如果可组合项涉及异步副作用,它应该 return 反应结果,return 承诺结果会限制其使用。 useFetch
是 Nuxt 可组合的,return 是一个承诺,似乎旨在用于 Nuxt 生命周期挂钩,而 useLazyFetch
是通用的 Vue 可组合。
这在 the usage of useLazyFetch
中显示,结果的属性是引用,因此是反应性的。如果用自定义可组合项包装,useLazyFetch
的结果需要通过 Vue 组合 API 进行 组合,例如pending
应该反映当前操作的整体状态。
useFetchCookies
和 useApi
都需要更改为常规可组合项。
export const useToken = () => {
const data = ref(null);
const error = ref(null);
const pending = ref(true);
const token = useCookie(csrf_cookie).value;
if (!token) {
const result = useLazyFetch("/backend/sanctum/csrf-cookie", {
credentials: "include"
});
watch(result.pending, (val) => {
pending.value = val;
if (!val)
data.value = useCookie(csrf_cookie).value;
});
watch(result.error, (val) => {
error.value = val;
});
} else {
pending.value = false;
data.value = useCookie(csrf_cookie).value;
}
return { data, error, pending };
};
export const useApi = (url: string, options?: FetchOptions, pick?: any) => {
const data = ref(null);
const error = ref(null);
const pending = ref(true);
const refresh = () => fetchResult.value?.refresh();
const fetchResultRef = ref(null);
const tokenResult = useToken();
watch(tokenResult.data, (token) => {
if (!token)
return;
const headers = ...;
const opts = ...;
fetchResultRef.value = useLazyFetch(`/backend/api${url}`, {
server: false,
credentials: "include",
headers,
...opts,
pick: pick
});
});
watch([
tokenResult.pending,
() => fetchResultRef.value?.pending ?? true
], ([tokenPending, fetchPending]) => {
pending.value = tokenPending || fetchPending;
});
watch([
tokenResult.error,
() => fetchResultRef.value?.error ?? null
], ([tokenError, fetchError]) => {
error.value = tokenError || fetchError;
});
watch(() => fetchResultRef.value?.data ?? null, (value) => {
data.value = value;
});
return { data, error, pending, refresh };
};
请注意,在此实现中,useCookie
结果是反应性的,但反应性被丢弃,因此 useToken
中的 refresh
仍未使用。 useLazyFetch
立即执行请求,不管名字是什么,所以它的调用需要推迟到 headers 可用。它可以在设置函数之后异步调用,这是它的实现允许的。
我面临这样一种情况,我需要创建一个 API 调用,但在安装组件之前不应触发它。
这就是我到目前为止所得到的:
onMounted(async() => {
const {
data,
refresh,
pending,
error
} = await useApi(`/profile/${$auth.user.profile.sid}`, null, ['data']);
});
const computedWaiting = computed<boolean>(() => {
return pending.value === true;
});
现在的问题是,computedWaiting
抛出错误,因为 pending
未定义。所以我需要破坏 useApi
-response,它基本上是一个 https://github.com/unjs/ohmyfetch-result.
这是 useApi 方法(取自此 GitHub-问题:https://github.com/nuxt/framework/discussions/4504):
import {FetchOptions} from "ohmyfetch";
const csrf_cookie: string = "XSRF-TOKEN";
/**
* Return the cookies needed by "Sanctum", browser will handle them automatically.
*/
export const useFetchCookies = async () => {
await $fetch.raw("/backend/sanctum/csrf-cookie", {
credentials: "include" // Allow browser to handle cookies
});
};
/**
* Api call using nuxt `useFetch`
*
* @see {@link https://github.com/unjs/ohmyfetch#readme} ~ ohmyfetch Docs
* @param url
* @param options
* @param pick
*/
export const useApi = async (url: string, options?: FetchOptions, pick?: any) => {
// First we verify if the `xsrf-token` is present on the browser cookies
let token = useCookie(csrf_cookie)?.value;
if (!token) {
// If not present we will re fetch all cookies, the browser will
// handle them automatically so we don't need to do anything
await useFetchCookies();
// Load the new token value to use it in the `headers`
token = useCookie(csrf_cookie).value;
}
// Here we will create a default set of headers for every request
// if present we will also spread the `headers` set by the user
// then we will delete them to avoid collision in next spread
const headers: HeadersInit = {
Accept: "application/json",
"Cache-Control": "no-cache",
"X-XSRF-TOKEN": token,
...options?.headers
};
// At this point all the `headers` passed by the user where correctly
// set in the defaults, now we will spread `options` to remove the
// `headers` attribute so we don't spread it again in `useFetch`
const opts: FetchOptions = options ? (({headers, ...opts}) => opts)(options) : null;
return useLazyFetch(`/backend/api${url}`, {
server: false,
credentials: "include", // Allow browser to handle cookies
headers,
...opts,
pick: pick
});
};
了解 useFetch
来自 https://v3.nuxtjs.org/guide/features/data-fetching, which basically uses ohmyfetch as a composable itself: https://github.com/nuxt/framework/blob/46c656c4b85c3622b99a7c4f6a01f5b11c830be6/packages/nuxt/src/app/composables/fetch.ts#L18-L59
那么如何在 onMounted 之后最优雅地暴露 data
、refresh
、pending
和 error
?
在 onMounted
中调用可组合项而没有特定目的是不正确的。 Vue 可组合项通常直接在设置函数中使用,因此可以立即访问结果。是否可以在其他地方使用它们取决于它们的实现。
如果可组合项涉及异步副作用,它应该 return 反应结果,return 承诺结果会限制其使用。 useFetch
是 Nuxt 可组合的,return 是一个承诺,似乎旨在用于 Nuxt 生命周期挂钩,而 useLazyFetch
是通用的 Vue 可组合。
这在 the usage of useLazyFetch
中显示,结果的属性是引用,因此是反应性的。如果用自定义可组合项包装,useLazyFetch
的结果需要通过 Vue 组合 API 进行 组合,例如pending
应该反映当前操作的整体状态。
useFetchCookies
和 useApi
都需要更改为常规可组合项。
export const useToken = () => {
const data = ref(null);
const error = ref(null);
const pending = ref(true);
const token = useCookie(csrf_cookie).value;
if (!token) {
const result = useLazyFetch("/backend/sanctum/csrf-cookie", {
credentials: "include"
});
watch(result.pending, (val) => {
pending.value = val;
if (!val)
data.value = useCookie(csrf_cookie).value;
});
watch(result.error, (val) => {
error.value = val;
});
} else {
pending.value = false;
data.value = useCookie(csrf_cookie).value;
}
return { data, error, pending };
};
export const useApi = (url: string, options?: FetchOptions, pick?: any) => {
const data = ref(null);
const error = ref(null);
const pending = ref(true);
const refresh = () => fetchResult.value?.refresh();
const fetchResultRef = ref(null);
const tokenResult = useToken();
watch(tokenResult.data, (token) => {
if (!token)
return;
const headers = ...;
const opts = ...;
fetchResultRef.value = useLazyFetch(`/backend/api${url}`, {
server: false,
credentials: "include",
headers,
...opts,
pick: pick
});
});
watch([
tokenResult.pending,
() => fetchResultRef.value?.pending ?? true
], ([tokenPending, fetchPending]) => {
pending.value = tokenPending || fetchPending;
});
watch([
tokenResult.error,
() => fetchResultRef.value?.error ?? null
], ([tokenError, fetchError]) => {
error.value = tokenError || fetchError;
});
watch(() => fetchResultRef.value?.data ?? null, (value) => {
data.value = value;
});
return { data, error, pending, refresh };
};
请注意,在此实现中,useCookie
结果是反应性的,但反应性被丢弃,因此 useToken
中的 refresh
仍未使用。 useLazyFetch
立即执行请求,不管名字是什么,所以它的调用需要推迟到 headers 可用。它可以在设置函数之后异步调用,这是它的实现允许的。