Skip to content

Commit acbdc7f

Browse files
authored
fix(client): avoid mismatching between route path and page data (close #1249) (#1381)
1 parent 1c2125c commit acbdc7f

File tree

3 files changed

+52
-24
lines changed

3 files changed

+52
-24
lines changed
Lines changed: 13 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
import type { PageData } from '@vuepress/shared'
2-
import { readonly, ref } from 'vue'
3-
import type { Ref } from 'vue'
4-
import { pagesData } from './pagesData.js'
2+
import { inject, type InjectionKey, readonly, type Ref } from 'vue'
53

64
export type { PageData }
75

@@ -11,6 +9,13 @@ export type { PageData }
119
export type PageDataRef<T extends Record<any, any> = Record<never, never>> =
1210
Ref<PageData<T>>
1311

12+
/**
13+
* Injection key for page data
14+
*/
15+
export const pageDataSymbol: InjectionKey<PageDataRef> = Symbol(
16+
__VUEPRESS_DEV__ ? 'pageData' : '',
17+
)
18+
1419
/**
1520
* Empty page data to be used as the fallback value
1621
*/
@@ -23,24 +28,15 @@ export const pageDataEmpty = readonly({
2328
headers: [],
2429
} as PageData) as PageData
2530

26-
/**
27-
* Global page data ref
28-
*/
29-
export const pageData: PageDataRef = ref(pageDataEmpty)
30-
3131
/**
3232
* Returns the ref of the data of current page
3333
*/
3434
export const usePageData = <
3535
T extends Record<any, any> = Record<never, never>,
36-
>(): PageDataRef<T> => pageData as PageDataRef<T>
37-
38-
if (__VUEPRESS_DEV__ && (import.meta.webpackHot || import.meta.hot)) {
39-
// reuse vue HMR runtime
40-
__VUE_HMR_RUNTIME__.updatePageData = (data: PageData) => {
41-
pagesData.value[data.key] = () => Promise.resolve(data)
42-
if (data.key === pageData.value.key) {
43-
pageData.value = data
44-
}
36+
>(): PageDataRef<T> => {
37+
const pageData = inject(pageDataSymbol)
38+
if (!pageData) {
39+
throw new Error('pageData() is called without provider.')
4540
}
41+
return pageData as PageDataRef<T>
4642
}

packages/client/src/router.ts

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,10 @@ import {
44
createMemoryHistory,
55
createRouter,
66
createWebHistory,
7+
type Router,
78
START_LOCATION,
89
} from 'vue-router'
9-
import type { Router } from 'vue-router'
10-
import { pageData } from './composables/index.js'
10+
import type { PageData } from './composables/index.js'
1111
import { resolvers } from './resolvers.js'
1212
import { createRoutes } from './routes.js'
1313

@@ -32,10 +32,11 @@ export const createVueRouter = (): Router => {
3232
},
3333
})
3434

35+
// ensure page data and page component have been loaded before resolving the route,
36+
// and save page data to route meta
3537
router.beforeResolve(async (to, from) => {
3638
if (to.path !== from.path || from === START_LOCATION) {
37-
// ensure page data and page component have been loaded
38-
;[pageData.value] = await Promise.all([
39+
;[to.meta._data] = await Promise.all([
3940
resolvers.resolvePageData(to.name as string),
4041
pagesComponents[to.name as string]?.__asyncLoader(),
4142
])
@@ -44,3 +45,14 @@ export const createVueRouter = (): Router => {
4445

4546
return router
4647
}
48+
49+
declare module 'vue-router' {
50+
interface RouteMeta {
51+
/**
52+
* Store page data to route meta
53+
*
54+
* @internal only for internal use
55+
*/
56+
_data: PageData
57+
}
58+
}

packages/client/src/setupGlobalComputed.ts

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
1-
import { computedEager } from '@vueuse/core'
1+
import { computedEager, computedWithControl } from '@vueuse/core'
22
import { type App, computed } from 'vue'
33
import type { Router } from 'vue-router'
44
import {
55
type LayoutsRef,
66
layoutsSymbol,
77
type PageData,
8-
pageData,
98
type PageDataRef,
9+
pageDataSymbol,
1010
type PageFrontmatter,
1111
type PageFrontmatterRef,
1212
pageFrontmatterSymbol,
@@ -21,6 +21,7 @@ import {
2121
pageLangSymbol,
2222
type PageLayoutRef,
2323
pageLayoutSymbol,
24+
pagesData,
2425
type RouteLocale,
2526
type RouteLocaleRef,
2627
routeLocaleSymbol,
@@ -59,13 +60,31 @@ export const setupGlobalComputed = (
5960
router: Router,
6061
clientConfigs: ClientConfig[],
6162
): GlobalComputed => {
62-
const layouts = computed(() => resolvers.resolveLayouts(clientConfigs))
6363
// create eager computed for route path and locale, so that route changes
6464
// won't make all downstream computed re-evaluate
6565
const routePath = computedEager(() => router.currentRoute.value.path)
6666
const routeLocale = computedEager(() =>
6767
resolvers.resolveRouteLocale(siteData.value.locales, routePath.value),
6868
)
69+
70+
// load page data from route meta
71+
const pageData = computedWithControl(
72+
routePath,
73+
() => router.currentRoute.value.meta._data,
74+
)
75+
// handle page data HMR
76+
if (__VUEPRESS_DEV__ && (import.meta.webpackHot || import.meta.hot)) {
77+
__VUE_HMR_RUNTIME__.updatePageData = (data: PageData) => {
78+
pagesData.value[data.key] = () => Promise.resolve(data)
79+
if (data.key === router.currentRoute.value.meta._data.key) {
80+
router.currentRoute.value.meta._data = data
81+
pageData.trigger()
82+
}
83+
}
84+
}
85+
86+
// create other global computed
87+
const layouts = computed(() => resolvers.resolveLayouts(clientConfigs))
6988
const siteLocaleData = computed(() =>
7089
resolvers.resolveSiteLocaleData(siteData.value, routeLocale.value),
7190
)
@@ -91,6 +110,7 @@ export const setupGlobalComputed = (
91110

92111
// provide global computed
93112
app.provide(layoutsSymbol, layouts)
113+
app.provide(pageDataSymbol, pageData)
94114
app.provide(pageFrontmatterSymbol, pageFrontmatter)
95115
app.provide(pageHeadTitleSymbol, pageHeadTitle)
96116
app.provide(pageHeadSymbol, pageHead)

0 commit comments

Comments
 (0)