-
Notifications
You must be signed in to change notification settings - Fork 100
/
static.ts
136 lines (118 loc) · 4.64 KB
/
static.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
import { onServerPrefetch, watch, computed, ref } from 'vue'
import type { Ref } from 'vue'
import { joinURL } from 'ufo'
import { ssrRef } from './ssr-ref'
import {
globalContext,
staticPath,
publicPath as _publicPath,
} from '@nuxtjs/composition-api/dist/runtime/globals'
const staticCache: Record<string, any> = {}
function writeFile(key: string, value: string) {
if (process.client || !process.static) return
const { writeFileSync }: typeof import('fs') = process.client
? ''
: require('fs')
const { join }: typeof import('upath') = process.client
? ''
: require('upath')
try {
writeFileSync(join(staticPath, `${key}.json`), value)
} catch (e) {
console.log(e)
}
}
/**
* You can pre-run expensive functions using `useStatic`.
*
* __SSG__
* If you are generating the whole app (or just prerendering some routes with `nuxt build && nuxt generate --no-build`) the following behaviour will be unlocked:
1. On generate, the result of a `useStatic` call will be saved to a JSON file and copied into the `/dist` directory.
2. On hard-reload of a generated page, the JSON will be inlined into the page and cached.
3. On client navigation to a generated page, this JSON will be fetched - and once fetched it will be cached for subsequent navigations. If for whatever reason this JSON doesn't exist, such as if the page *wasn't* pre-generated, the original factory function will be run on client-side.
If you are pregenerating some pages in your app note that you may need to increase `generate.interval`. (See [setup instructions](https://composition-api.nuxtjs.org/setup.html).)
*
* __SSR__
* If the route is not pre-generated (including in dev mode), then:
1. On a hard-reload, the server will run the factory function and inline the result in `nuxtState` - so the client won't rerun the API request. The result will be cached between requests.
2. On client navigation, the client will run the factory function.
In both of these cases, the return result of `useStatic` is a `null` ref that is filled with the result of the factory function or JSON fetch when it resolves.
* @param factory The async function that will populate the ref this function returns. It receives the param and keyBase (see below) as parameters.
* @param param A an optional param (such as an ID) to distinguish multiple API fetches using the same factory function.
* @param keyBase A key that should be unique across your project. If not provided, this will be auto-generated by `@nuxtjs/composition-api`.
* @example
```ts
import { defineComponent, useContext, useStatic, computed } from '@nuxtjs/composition-api'
import axios from 'axios'
export default defineComponent({
setup() {
const { params } = useContext()
const id = computed(() => params.value.id)
const post = useStatic(
id => axios.get(`https://jsonplaceholder.typicode.com/posts/${id}`),
id,
'post'
)
return { post }
},
})
```
*/
export const useStatic = <T>(
factory: (param: string, key: string) => Promise<T>,
param: Ref<string> = ref(''),
keyBase: string
): Ref<T | null> => {
const key = computed(() => `${keyBase}-${param.value}`)
const result = ssrRef<T | null>(null, key.value)
if (result.value) staticCache[key.value] = result.value
if (process.client) {
const publicPath =
(window as any)[globalContext].$config?.app?.cdnURL || _publicPath
const onFailure = () =>
factory(param.value, key.value).then(r => {
staticCache[key.value] = r
result.value = r
return
})
watch(
key,
key => {
if (key in staticCache) {
result.value = staticCache[key]
return
}
/* eslint-disable promise/always-return */
if (!process.static) onFailure()
else
fetch(joinURL(publicPath, `${key}.json`))
.then(response => {
if (!response.ok) throw new Error('Response invalid.')
return response.json()
})
.then(json => {
staticCache[key] = json
result.value = json
})
.catch(onFailure)
/* eslint-enable */
},
{
immediate: true,
flush: 'post',
}
)
} else {
if (key.value in staticCache) {
result.value = staticCache[key.value]
return result as Ref<T | null>
}
onServerPrefetch(async () => {
const [_key, _param] = [key.value, param.value]
result.value = await factory(_param, _key)
staticCache[_key] = result.value
writeFile(_key, JSON.stringify(result.value))
})
}
return result as Ref<T | null>
}