mirror of
https://github.com/nuxt/nuxt.git
synced 2024-11-22 05:35:13 +00:00
fix(app): improve composables (#183)
This commit is contained in:
parent
960523c4b8
commit
451fc29b60
@ -1,6 +1,7 @@
|
||||
import { Ref, ref, onMounted, watch, getCurrentInstance, onUnmounted } from 'vue'
|
||||
import { Nuxt, useNuxt } from 'nuxt/app'
|
||||
import { useData } from './data'
|
||||
|
||||
import { ensureReactive, useData } from './data'
|
||||
|
||||
export type AsyncDataFn<T> = (ctx?: Nuxt) => Promise<T>
|
||||
|
||||
@ -12,7 +13,7 @@ export interface AsyncDataOptions {
|
||||
export interface AsyncDataObj<T> {
|
||||
data: Ref<T>
|
||||
pending: Ref<boolean>
|
||||
refresh: Function
|
||||
refresh: () => Promise<void>
|
||||
error?: any
|
||||
}
|
||||
|
||||
@ -20,10 +21,10 @@ export function useAsyncData (defaults?: AsyncDataOptions) {
|
||||
const nuxt = useNuxt()
|
||||
const vm = getCurrentInstance()
|
||||
|
||||
let data = useData(nuxt, vm)
|
||||
const data = useData(nuxt, vm)
|
||||
let dataRef = 1
|
||||
|
||||
const onMountedCbs = []
|
||||
const onMountedCbs: Array<() => void> = []
|
||||
|
||||
if (process.client) {
|
||||
onMounted(() => {
|
||||
@ -31,13 +32,13 @@ export function useAsyncData (defaults?: AsyncDataOptions) {
|
||||
onMountedCbs.splice(0, onMountedCbs.length)
|
||||
})
|
||||
|
||||
onUnmounted(() => {
|
||||
onMountedCbs.splice(0, onMountedCbs.length)
|
||||
data = null
|
||||
})
|
||||
onUnmounted(() => onMountedCbs.splice(0, onMountedCbs.length))
|
||||
}
|
||||
|
||||
return async function asyncData<T = any> (handler: AsyncDataFn<T>, options?: AsyncDataOptions): Promise<AsyncDataObj<T>> {
|
||||
return async function asyncData<T = Record<string, any>> (
|
||||
handler: AsyncDataFn<T>,
|
||||
options?: AsyncDataOptions
|
||||
): Promise<AsyncDataObj<T>> {
|
||||
if (typeof handler !== 'function') {
|
||||
throw new TypeError('asyncData handler must be a function')
|
||||
}
|
||||
@ -51,6 +52,8 @@ export function useAsyncData (defaults?: AsyncDataOptions) {
|
||||
const key = String(dataRef++)
|
||||
const pending = ref(true)
|
||||
|
||||
const datastore = ensureReactive(data, key)
|
||||
|
||||
const fetch = async () => {
|
||||
pending.value = true
|
||||
const _handler = handler(nuxt)
|
||||
@ -59,11 +62,11 @@ export function useAsyncData (defaults?: AsyncDataOptions) {
|
||||
// Let user resolve if request is promise
|
||||
// TODO: handle error
|
||||
const result = await _handler
|
||||
if (!data[key]) {
|
||||
data[key] = result
|
||||
} else {
|
||||
Object.assign(data[key], result)
|
||||
|
||||
for (const _key in result) {
|
||||
datastore[_key] = result[_key]
|
||||
}
|
||||
|
||||
pending.value = false
|
||||
} else {
|
||||
// Invalid request
|
||||
@ -81,14 +84,10 @@ export function useAsyncData (defaults?: AsyncDataOptions) {
|
||||
}
|
||||
// 2. Initial load (server: false): fetch on mounted
|
||||
if (nuxt.isHydrating && !options.server) {
|
||||
// Force tracking it
|
||||
data[key] = {}
|
||||
// Fetch on mounted (initial load or deferred fetch)
|
||||
onMountedCbs.push(fetch)
|
||||
} else if (!nuxt.isHydrating) {
|
||||
if (options.defer) {
|
||||
// Force tracking it
|
||||
data[key] = {}
|
||||
// 3. Navigation (defer: true): fetch on mounted
|
||||
onMountedCbs.push(fetch)
|
||||
} else {
|
||||
@ -105,13 +104,16 @@ export function useAsyncData (defaults?: AsyncDataOptions) {
|
||||
await fetch()
|
||||
}
|
||||
return {
|
||||
data: data[key],
|
||||
data: datastore,
|
||||
pending,
|
||||
refresh: fetch
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function asyncData<T = any> (handler: AsyncDataFn<T>, options?: AsyncDataOptions): Promise<AsyncDataObj<T>> {
|
||||
export function asyncData<T = Record<string, any>> (
|
||||
handler: AsyncDataFn<T>,
|
||||
options?: AsyncDataOptions
|
||||
): Promise<AsyncDataObj<T>> {
|
||||
return useAsyncData()(handler, options)
|
||||
}
|
||||
|
@ -1,12 +1,28 @@
|
||||
import { getCurrentInstance, reactive, isReactive } from 'vue'
|
||||
import { getCurrentInstance, isReactive, reactive, UnwrapRef } from 'vue'
|
||||
|
||||
import { useNuxt } from 'nuxt/app'
|
||||
|
||||
export function ensureReactive<
|
||||
T extends Record<string, any>,
|
||||
K extends keyof T
|
||||
> (data: T, key: K): UnwrapRef<T[K]> {
|
||||
if (!isReactive(data[key])) {
|
||||
data[key] = reactive(data[key] || ({} as T[K]))
|
||||
}
|
||||
return data[key]
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a unique string suitable for syncing data between server and client.
|
||||
* @param nuxt (optional) A Nuxt instance
|
||||
* @param vm (optional) A Vue component - by default it will use the current instance
|
||||
*/
|
||||
export function useSSRRef (nuxt = useNuxt(), vm = getCurrentInstance()): string {
|
||||
if (!vm) {
|
||||
throw new Error('This must be called within a setup function.')
|
||||
}
|
||||
|
||||
// Server
|
||||
if (process.server) {
|
||||
if (!vm.attrs['data-ssr-ref']) {
|
||||
nuxt._refCtr = nuxt._refCtr || 1
|
||||
@ -15,9 +31,9 @@ export function useSSRRef (nuxt = useNuxt(), vm = getCurrentInstance()): string
|
||||
return vm.attrs['data-ssr-ref'] as string
|
||||
}
|
||||
|
||||
if (process.client) {
|
||||
return vm.vnode.el?.dataset?.ssrRef || String(Math.random()) /* TODO: unique value for multiple calls */
|
||||
}
|
||||
// Client
|
||||
/* TODO: unique value for multiple calls */
|
||||
return vm.vnode.el?.dataset?.ssrRef || String(Math.random())
|
||||
}
|
||||
|
||||
/**
|
||||
@ -25,14 +41,13 @@ export function useSSRRef (nuxt = useNuxt(), vm = getCurrentInstance()): string
|
||||
* @param nuxt (optional) A Nuxt instance
|
||||
* @param vm (optional) A Vue component - by default it will use the current instance
|
||||
*/
|
||||
export function useData (nuxt = useNuxt(), vm = getCurrentInstance()): ReturnType<typeof reactive> {
|
||||
export function useData<T = Record<string, any>> (
|
||||
nuxt = useNuxt(),
|
||||
vm = getCurrentInstance()
|
||||
): UnwrapRef<T> {
|
||||
const ssrRef = useSSRRef(nuxt, vm)
|
||||
|
||||
nuxt.payload.data = nuxt.payload.data || {}
|
||||
|
||||
if (!isReactive(nuxt.payload.data[ssrRef])) {
|
||||
nuxt.payload.data[ssrRef] = reactive(nuxt.payload.data[ssrRef] || {})
|
||||
}
|
||||
|
||||
return nuxt.payload.data[ssrRef]
|
||||
return ensureReactive(nuxt.payload.data, ssrRef)
|
||||
}
|
||||
|
@ -1,9 +1,9 @@
|
||||
import { getCurrentInstance } from 'vue'
|
||||
import type { Nuxt } from 'nuxt/app'
|
||||
|
||||
let currentNuxtInstance: Nuxt
|
||||
let currentNuxtInstance: Nuxt | null
|
||||
|
||||
export const setNuxtInstance = (nuxt: Nuxt) => {
|
||||
export const setNuxtInstance = (nuxt: Nuxt | null) => {
|
||||
currentNuxtInstance = nuxt
|
||||
}
|
||||
|
||||
@ -15,21 +15,20 @@ export const setNuxtInstance = (nuxt: Nuxt) => {
|
||||
export async function callWithNuxt (nuxt: Nuxt, setup: () => any) {
|
||||
setNuxtInstance(nuxt)
|
||||
const p = setup()
|
||||
setNuxtInstance(undefined)
|
||||
setNuxtInstance(null)
|
||||
await p
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current Nuxt instance.
|
||||
*/
|
||||
export function useNuxt () {
|
||||
export function useNuxt (): Nuxt {
|
||||
const vm = getCurrentInstance()
|
||||
|
||||
if (!vm && !currentNuxtInstance) {
|
||||
throw new Error('nuxt instance unavailable')
|
||||
}
|
||||
|
||||
if (!vm) {
|
||||
if (!currentNuxtInstance) {
|
||||
throw new Error('nuxt instance unavailable')
|
||||
}
|
||||
return currentNuxtInstance
|
||||
}
|
||||
|
||||
|
@ -15,8 +15,8 @@ export interface Nuxt {
|
||||
|
||||
ssrContext?: Record<string, any>
|
||||
payload: {
|
||||
serverRendered?: true,
|
||||
data?: object
|
||||
serverRendered?: true
|
||||
data?: Record<string, any>
|
||||
rendered?: Function
|
||||
[key: string]: any
|
||||
}
|
||||
@ -36,7 +36,6 @@ export interface CreateOptions {
|
||||
|
||||
export function createNuxt (options: CreateOptions) {
|
||||
const nuxt: Nuxt = {
|
||||
app: undefined,
|
||||
provide: undefined,
|
||||
globalName: 'nuxt',
|
||||
state: {},
|
||||
@ -69,6 +68,8 @@ export function createNuxt (options: CreateOptions) {
|
||||
serverRendered: true // TODO: legacy
|
||||
}
|
||||
|
||||
nuxt.ssrContext = nuxt.ssrContext || {}
|
||||
|
||||
// Expose to server renderer to create window.__NUXT__
|
||||
nuxt.ssrContext.payload = nuxt.payload
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user