Skip to content

Commit 21e1f91

Browse files
committed
refactor(client): provide layouts with injection (close #1189)
1 parent 83e7b6c commit 21e1f91

File tree

13 files changed

+178
-100
lines changed

13 files changed

+178
-100
lines changed

packages/client/src/app.ts

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { clientConfigs } from '@internal/clientConfigs'
12
import { createApp, createSSRApp, h } from 'vue'
23
import { RouterView } from 'vue-router'
34
import { siteData } from './composables/index.js'
@@ -14,8 +15,6 @@ import type { CreateVueAppFunction } from './types/index.js'
1415
const appCreator = __VUEPRESS_DEV__ ? createApp : createSSRApp
1516

1617
export const createVueApp: CreateVueAppFunction = async () => {
17-
const { clientConfigs } = await import('@internal/clientConfigs')
18-
1918
// create vue app
2019
const app = appCreator({
2120
name: 'VuepressApp',
@@ -39,11 +38,11 @@ export const createVueApp: CreateVueAppFunction = async () => {
3938
})
4039

4140
// create vue-router instance
42-
const router = await createVueRouter()
41+
const router = createVueRouter()
4342

4443
// global components and computed
4544
setupGlobalComponents(app)
46-
const globalComputed = setupGlobalComputed(app, router)
45+
const globalComputed = setupGlobalComputed(app, router, clientConfigs)
4746

4847
// setup devtools in dev mode
4948
if (__VUEPRESS_DEV__ || __VUE_PROD_DEVTOOLS__) {
Lines changed: 10 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -1,58 +1,14 @@
1-
import { isString } from '@vuepress/shared'
2-
import type { Component } from 'vue'
3-
import { computed, defineComponent, h } from 'vue'
4-
import { usePageData } from '../composables/index.js'
5-
6-
const LAYOUT_NAME_DEFAULT = 'Layout'
7-
const LAYOUT_NAME_NOT_FOUND = 'NotFound'
1+
import { defineComponent, h } from 'vue'
2+
import { usePageLayout } from '../composables/index.js'
83

94
/**
105
* Global Layout
116
*/
12-
export const createVuepressComponent = async (): Promise<Component> => {
13-
const { clientConfigs } = await import('@internal/clientConfigs')
14-
15-
const layouts = clientConfigs.reduce(
16-
(prev, item) => ({
17-
...prev,
18-
...item.layouts,
19-
}),
20-
{} as Record<string, Component>
21-
)
22-
23-
const Vuepress = defineComponent({
24-
name: 'Vuepress',
25-
26-
setup() {
27-
const page = usePageData()
28-
29-
// resolve layout component
30-
const layoutComponent = computed(() => {
31-
// resolve layout name of current page
32-
let layoutName: string
33-
34-
if (page.value.path) {
35-
// if current page exists
36-
37-
// use layout from frontmatter
38-
const frontmatterLayout = page.value.frontmatter.layout
39-
40-
if (isString(frontmatterLayout)) {
41-
layoutName = frontmatterLayout
42-
} else {
43-
// fallback to default layout
44-
layoutName = LAYOUT_NAME_DEFAULT
45-
}
46-
} else {
47-
// if current page does not exist
48-
// use NotFound layout
49-
layoutName = LAYOUT_NAME_NOT_FOUND
50-
}
51-
return layouts[layoutName]
52-
})
53-
54-
return () => h(layoutComponent.value)
55-
},
56-
})
57-
return Vuepress
58-
}
7+
export const Vuepress = defineComponent({
8+
name: 'Vuepress',
9+
10+
setup() {
11+
const layout = usePageLayout()
12+
return () => h(layout.value)
13+
},
14+
})

packages/client/src/composables/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
1+
export * from './layouts.js'
12
export * from './pageData.js'
23
export * from './pageFrontmatter.js'
34
export * from './pageHead.js'
45
export * from './pageHeadTitle.js'
56
export * from './pageLang.js'
7+
export * from './pageLayout.js'
68
export * from './pagesData.js'
79
export * from './routeLocale.js'
810
export * from './siteData.js'
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import { type ComputedRef, inject, type InjectionKey } from 'vue'
2+
import type { Layouts } from '../types/index.js'
3+
4+
/**
5+
* Ref wrapper of `Layouts`
6+
*/
7+
export type LayoutsRef = ComputedRef<Layouts>
8+
9+
/**
10+
* Injection key for layouts
11+
*/
12+
export const layoutsSymbol: InjectionKey<LayoutsRef> = Symbol(
13+
__VUEPRESS_DEV__ ? 'layouts' : ''
14+
)
15+
16+
/**
17+
* Returns layouts component map
18+
*/
19+
export const useLayouts = (): LayoutsRef => {
20+
const layouts = inject(layoutsSymbol)
21+
if (!layouts) {
22+
throw new Error('useLayouts() is called without provider.')
23+
}
24+
return layouts
25+
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import {
2+
type Component,
3+
type ComputedRef,
4+
inject,
5+
type InjectionKey,
6+
} from 'vue'
7+
8+
/**
9+
* Ref wrapper of `PageLayout`
10+
*/
11+
export type PageLayoutRef = ComputedRef<Component>
12+
13+
/**
14+
* Injection key for page layout
15+
*/
16+
export const pageLayoutSymbol: InjectionKey<PageLayoutRef> = Symbol(
17+
__VUEPRESS_DEV__ ? 'pageLayout' : ''
18+
)
19+
20+
/**
21+
* Returns layout component of current page
22+
*/
23+
export const usePageLayout = (): ComputedRef<Component> => {
24+
const pageLayout = inject(pageLayoutSymbol)
25+
if (!pageLayout) {
26+
throw new Error('usePageLayout() is called without provider.')
27+
}
28+
return pageLayout
29+
}

packages/client/src/constants.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
/**
2+
* Name of the default layout
3+
*/
4+
export const LAYOUT_NAME_DEFAULT = 'Layout'
5+
6+
/**
7+
* Name of the 404 page layout
8+
*/
9+
export const LAYOUT_NAME_NOT_FOUND = 'NotFound'

packages/client/src/resolvers.ts

Lines changed: 53 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -4,25 +4,41 @@ import {
44
isString,
55
resolveLocalePath,
66
} from '@vuepress/shared'
7+
import type { Component } from 'vue'
78
import { reactive } from 'vue'
8-
import { pageDataEmpty, pagesData } from './composables/index.js'
9-
import type {
10-
PageData,
11-
PageFrontmatter,
12-
PageHead,
13-
PageHeadTitle,
14-
PageLang,
15-
RouteLocale,
16-
SiteData,
17-
SiteLocaleData,
9+
import {
10+
type PageData,
11+
pageDataEmpty,
12+
type PageFrontmatter,
13+
type PageHead,
14+
type PageHeadTitle,
15+
type PageLang,
16+
pagesData,
17+
type RouteLocale,
18+
type SiteData,
19+
type SiteLocaleData,
1820
} from './composables/index.js'
21+
import { LAYOUT_NAME_DEFAULT, LAYOUT_NAME_NOT_FOUND } from './constants.js'
22+
import type { ClientConfig, Layouts } from './types/index.js'
1923

2024
/**
2125
* Resolver methods to get global computed
2226
*
2327
* Users can override corresponding method for advanced customization
2428
*/
2529
export const resolvers = reactive({
30+
/**
31+
* Resolve layouts component map
32+
*/
33+
resolveLayouts: (clientConfigs: ClientConfig[]): Layouts =>
34+
clientConfigs.reduce(
35+
(prev, item) => ({
36+
...prev,
37+
...item.layouts,
38+
}),
39+
{} as Layouts
40+
),
41+
2642
/**
2743
* Resolve page data according to page key
2844
*/
@@ -78,7 +94,33 @@ export const resolvers = reactive({
7894
*
7995
* It would be used as the `lang` attribute of `<html>` tag
8096
*/
81-
resolvePageLang: (pageData: PageData): PageLang => pageData.lang || 'en',
97+
resolvePageLang: (page: PageData): PageLang => page.lang || 'en',
98+
99+
/**
100+
* Resolve layout component of current page
101+
*/
102+
resolvePageLayout: (page: PageData, layouts: Layouts): Component => {
103+
let layoutName: string
104+
105+
// if current page exists
106+
if (page.path) {
107+
// use layout from frontmatter
108+
const frontmatterLayout = page.frontmatter.layout
109+
110+
if (isString(frontmatterLayout)) {
111+
layoutName = frontmatterLayout
112+
} else {
113+
// fallback to default layout
114+
layoutName = LAYOUT_NAME_DEFAULT
115+
}
116+
}
117+
// if current page does not exist
118+
else {
119+
// use NotFound layout
120+
layoutName = LAYOUT_NAME_NOT_FOUND
121+
}
122+
return layouts[layoutName]
123+
},
82124

83125
/**
84126
* Resolve locale path according to route path and locales config

packages/client/src/router.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,11 @@ const historyCreator = __VUEPRESS_SSR__ ? createMemoryHistory : createWebHistory
2020
/**
2121
* Create vue-router instance
2222
*/
23-
export const createVueRouter = async (): Promise<Router> => {
23+
export const createVueRouter = (): Router => {
2424
const router = createRouter({
2525
// it might be an issue of vue-router that have to remove the ending slash
2626
history: historyCreator(removeEndingSlash(__VUEPRESS_BASE__)),
27-
routes: await createRoutes(),
27+
routes: createRoutes(),
2828
scrollBehavior: (to, from, savedPosition) => {
2929
if (savedPosition) return savedPosition
3030
if (to.hash) return { el: to.hash }

packages/client/src/routes.ts

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,12 @@
11
import { pagesRoutes } from '@internal/pagesRoutes'
22
import type { RouteRecordRaw } from 'vue-router'
3-
import { createVuepressComponent } from './components/Vuepress.js'
3+
import { Vuepress } from './components/Vuepress.js'
44

55
/**
66
* Create routes for vue-router
77
*/
8-
export const createRoutes = async (): Promise<RouteRecordRaw[]> => {
9-
const Vuepress = await createVuepressComponent()
10-
return pagesRoutes.reduce(
8+
export const createRoutes = (): RouteRecordRaw[] =>
9+
pagesRoutes.reduce(
1110
(result, [name, path, meta, redirects]) => {
1211
result.push(
1312
{
@@ -31,4 +30,3 @@ export const createRoutes = async (): Promise<RouteRecordRaw[]> => {
3130
},
3231
] as RouteRecordRaw[]
3332
)
34-
}

0 commit comments

Comments
 (0)