Skip to content

Commit

Permalink
feat(client): allow customizing global computed resolvers (close #338)
Browse files Browse the repository at this point in the history
  • Loading branch information
meteorlxy committed Jan 22, 2022
1 parent c1103c4 commit 405fc8d
Show file tree
Hide file tree
Showing 25 changed files with 418 additions and 337 deletions.
74 changes: 51 additions & 23 deletions docs/reference/client-api.md
Expand Up @@ -6,28 +6,6 @@ Client API is provided by [@vuepress/client](https://www.npmjs.com/package/@vuep

## Composition API

### useSiteData

- Details:

Returns the site data ref object.

### useSiteLocaleData

- Details:

Returns the site data ref object of current locale.

The properties of current locale have been merged into the root-level properties.

### useRouteLocale

- Details:

Returns the locale path ref object of current route.

The value is one of the keys of the [locales](./config.md#locales) config.

### usePageData

- Details:
Expand Down Expand Up @@ -70,7 +48,29 @@ Client API is provided by [@vuepress/client](https://www.npmjs.com/package/@vuep

The value is the `lang` property of the page data.

## Utils
### useRouteLocale

- Details:

Returns the locale path ref object of current route.

The value is one of the keys of the [locales](./config.md#locales) config.

### useSiteData

- Details:

Returns the site data ref object.

### useSiteLocaleData

- Details:

Returns the site data ref object of current locale.

The properties of current locale have been merged into the root-level properties.

## Helpers

### defineClientAppEnhance

Expand Down Expand Up @@ -157,3 +157,31 @@ To shim the types of these constants in client side code, add `@vuepress/client/
- Details:

An environment flag indicating whether it is currently running in server-side-rendering (SSR) build.

## Advanced

### resolvers <Badge text="experimental" />

- Type: `Record<string, Function>`

- Details:

An reactive object, methods of which determining how to resolve global computed.

- Example:

Customizing the format of `<title>` in `clientAppEnhance.ts` file:

```ts
import { defineClientAppEnhance, resolvers } from '@vuepress/client'

export default defineClientAppEnhance(({ app, router, siteData }) => {
// ...
resolvers.resolvePageHeadTitle = (page, siteLocale) =>
`${siteLocale.title} > ${page.title}`
})
```

::: danger
`resolvers` will affect the basic functionality of VuePress. Please make sure you have fully understood its purpose before modifying it.
:::
72 changes: 50 additions & 22 deletions docs/zh/reference/client-api.md
Expand Up @@ -6,28 +6,6 @@

## Composition API

### useSiteData

- 详情:

返回站点数据的 Ref 对象。

### useSiteLocaleData

- 详情:

返回当前 locale 的站点数据的 Ref 对象。

当前 locale 中的配置已经合并到顶层配置中。

### useRouteLocale

- 详情:

返回当前路由对应的 locale path 的 Ref 对象。

它的值是 [locales](./config.md#locales) 配置的键之一。

### usePageData

- 详情:
Expand Down Expand Up @@ -70,6 +48,28 @@

它的值是页面数据的 `lang` 属性。

### useRouteLocale

- 详情:

返回当前路由对应的 locale path 的 Ref 对象。

它的值是 [locales](./config.md#locales) 配置的键之一。

### useSiteData

- 详情:

返回站点数据的 Ref 对象。

### useSiteLocaleData

- 详情:

返回当前 locale 的站点数据的 Ref 对象。

当前 locale 中的配置已经合并到顶层配置中。

## 工具函数

### defineClientAppEnhance
Expand Down Expand Up @@ -157,3 +157,31 @@ export default defineClientAppSetup(() => {
- 详情:

一个环境标记,用于标识当前是否运行在服务端渲染 (SSR) 环境下。

## 进阶能力

### resolvers <Badge text="实验性能力" />

- 类型: `Record<string, Function>`

- 详情:

一个响应式对象,其中的方法决定了如何获取全局计算属性。

- 示例:

`clientAppEnhance.ts` 文件中自定义 `<title>` 的格式:

```ts
import { defineClientAppEnhance, resolvers } from '@vuepress/client'

export default defineClientAppEnhance(({ app, router, siteData }) => {
// ...
resolvers.resolvePageHeadTitle = (page, siteLocale) =>
`${siteLocale.title} > ${page.title}`
})
```

::: danger
`resolvers` 会直接影响 VuePress 的基础功能,在修改前请确保你已充分了解其用途。
:::
57 changes: 10 additions & 47 deletions packages/@vuepress/client/src/app.ts
@@ -1,41 +1,23 @@
import { clientAppEnhances } from '@internal/clientAppEnhances'
import { clientAppRootComponents } from '@internal/clientAppRootComponents'
import { clientAppSetups } from '@internal/clientAppSetups'
import { pagesComponents } from '@internal/pagesComponents'
import { pagesRoutes } from '@internal/pagesRoutes'
import { removeEndingSlash } from '@vuepress/shared'
import { createApp, createSSRApp, h } from 'vue'
import type { App } from 'vue'
import {
createMemoryHistory,
createRouter,
createWebHistory,
RouterView,
START_LOCATION,
} from 'vue-router'
import { RouterView } from 'vue-router'
import type { Router } from 'vue-router'
import {
pageData,
resolvePageData,
setupUpdateHead,
siteData,
} from './composables'
import { provideGlobalComputed } from './provideGlobalComputed'
import { registerGlobalComponents } from './registerGlobalComponents'
import { siteData } from './composables'
import { createVueRouter } from './router'
import { setupDevtools } from './setupDevtools'
import { setupGlobalComponents } from './setupGlobalComponents'
import { setupGlobalComputed } from './setupGlobalComputed'
import { setupUpdateHead } from './setupUpdateHead'

/**
* - use `createApp` in dev mode
* - use `createSSRApp` in build mode
*/
const appCreator = __VUEPRESS_DEV__ ? createApp : createSSRApp

/**
* - use `createWebHistory` in dev mode and build mode client bundle
* - use `createMemoryHistory` in build mode server bundle
*/
const historyCreator = __VUEPRESS_SSR__ ? createMemoryHistory : createWebHistory

export type CreateVueAppFunction = () => Promise<{
app: App
router: Router
Expand All @@ -62,31 +44,12 @@ export const createVueApp: CreateVueAppFunction = async () => {
},
})

// create vue-router
const router = createRouter({
// TODO: it might be an issue of vue-router that have to remove the ending slash
history: historyCreator(removeEndingSlash(siteData.value.base)),
routes: pagesRoutes,
scrollBehavior: (to, from, savedPosition) => {
if (savedPosition) return savedPosition
if (to.hash) return { el: to.hash }
return { top: 0 }
},
})

router.beforeResolve(async (to, from) => {
if (to.path !== from.path || from === START_LOCATION) {
// ensure page data and page component have been loaded
;[pageData.value] = await Promise.all([
resolvePageData(to.name as string),
pagesComponents[to.name as string]?.__asyncLoader(),
])
}
})
// create vue-router instance
const router = createVueRouter()

// global components and computed
registerGlobalComponents(app)
const globalComputed = provideGlobalComputed(app, router)
setupGlobalComponents(app)
const globalComputed = setupGlobalComputed(app, router)

// setup devtools in dev mode
if (__VUEPRESS_DEV__ || __VUE_PROD_DEVTOOLS__) {
Expand Down
14 changes: 1 addition & 13 deletions packages/@vuepress/client/src/composables/pageData.ts
Expand Up @@ -14,7 +14,7 @@ export type PageDataRef<T extends Record<any, any> = Record<never, never>> =
/**
* Empty page data to be used as the fallback value
*/
const pageDataEmpty = readonly({
export const pageDataEmpty = readonly({
key: '',
path: '',
title: '',
Expand All @@ -36,18 +36,6 @@ export const usePageData = <
T extends Record<any, any> = Record<never, never>
>(): PageDataRef<T> => pageData as PageDataRef<T>

/**
* Resolve page data according to page key
*/
export const resolvePageData = async (pageKey: string): Promise<PageData> => {
const pageDataResolver = pagesData.value[pageKey]
if (!pageDataResolver) {
return pageDataEmpty
}
const pageData = await pageDataResolver()
return pageData ?? pageDataEmpty
}

if (import.meta.webpackHot || import.meta.hot) {
// reuse vue HMR runtime
__VUE_HMR_RUNTIME__.updatePageData = (data: PageData) => {
Expand Down
8 changes: 1 addition & 7 deletions packages/@vuepress/client/src/composables/pageFrontmatter.ts
@@ -1,4 +1,4 @@
import type { PageData, PageFrontmatter } from '@vuepress/shared'
import type { PageFrontmatter } from '@vuepress/shared'
import { inject } from 'vue'
import type { ComputedRef, InjectionKey } from 'vue'

Expand Down Expand Up @@ -30,9 +30,3 @@ export const usePageFrontmatter = <
}
return pageFrontmatter as PageFrontmatterRef<T>
}

/**
* Resolve page frontmatter from page data
*/
export const resolvePageFrontmatter = (pageData: PageData): PageFrontmatter =>
pageData.frontmatter
26 changes: 0 additions & 26 deletions packages/@vuepress/client/src/composables/pageHead.ts
@@ -1,10 +1,6 @@
import { dedupeHead, isArray, isString } from '@vuepress/shared'
import type { HeadConfig } from '@vuepress/shared'
import { inject } from 'vue'
import type { ComputedRef, InjectionKey } from 'vue'
import type { PageFrontmatter } from './pageFrontmatter'
import type { PageHeadTitle } from './pageHeadTitle'
import type { SiteLocaleData } from './siteLocaleData'

/**
* Page head config, which would be used for generate html tags in `<head>`
Expand Down Expand Up @@ -33,25 +29,3 @@ export const usePageHead = (): PageHeadRef => {
}
return pageHead
}

/**
* Merge the head config in frontmatter and site locale
*
* Frontmatter should take priority over site locale
*/
export const resolvePageHead = (
headTitle: PageHeadTitle,
frontmatter: PageFrontmatter,
siteLocale: SiteLocaleData
): HeadConfig[] => {
const description = isString(frontmatter.description)
? frontmatter.description
: siteLocale.description
const head: HeadConfig[] = [
...(isArray(frontmatter.head) ? frontmatter.head : []),
...siteLocale.head,
['title', {}, headTitle],
['meta', { name: 'description', content: description }],
]
return dedupeHead(head)
}
12 changes: 0 additions & 12 deletions packages/@vuepress/client/src/composables/pageHeadTitle.ts
@@ -1,7 +1,5 @@
import { inject } from 'vue'
import type { ComputedRef, InjectionKey } from 'vue'
import type { PageData } from './pageData'
import type { SiteLocaleData } from './siteLocaleData'

/**
* Page head title, which would be used as the content of `head > title` tag
Expand Down Expand Up @@ -30,13 +28,3 @@ export const usePageHeadTitle = (): PageHeadTitleRef => {
}
return pageHeadTitle
}

/**
* Resolve the content of page head title
*
* It would be used as the content of the `<title>` tag
*/
export const resolvePageHeadTitle = (
page: PageData,
siteLocale: SiteLocaleData
): PageHeadTitle => `${page.title ? `${page.title} | ` : ``}${siteLocale.title}`
9 changes: 0 additions & 9 deletions packages/@vuepress/client/src/composables/pageLang.ts
@@ -1,4 +1,3 @@
import type { PageData } from '@vuepress/shared'
import { inject } from 'vue'
import type { ComputedRef, InjectionKey } from 'vue'

Expand Down Expand Up @@ -29,11 +28,3 @@ export const usePageLang = (): PageLangRef => {
}
return pageLang
}

/**
* Resolve page language from page data
*
* It would be used as the `lang` attribute of `<html>` tag
*/
export const resolvePageLang = (pageData: PageData): PageLang =>
pageData.lang || 'en'
2 changes: 1 addition & 1 deletion packages/@vuepress/client/src/composables/pagesData.ts
Expand Up @@ -9,7 +9,7 @@ import type { Ref } from 'vue'
* The key is page key, and the value is an async function that
* returns the page data
*/
export type PagesData = Record<string, () => Promise<PageData>>
export type PagesData = Record<string, (() => Promise<PageData>) | undefined>

/**
* Ref wrapper of `PagesData`
Expand Down

0 comments on commit 405fc8d

Please sign in to comment.