diff --git a/packages/@vuepress/core/lib/client/app.js b/packages/@vuepress/core/lib/client/app.js index bcccec1797..a98c4afb71 100644 --- a/packages/@vuepress/core/lib/client/app.js +++ b/packages/@vuepress/core/lib/client/app.js @@ -9,14 +9,12 @@ import globalUIComponents from '@internal/global-ui' import ClientComputedMixin from '@transform/ClientComputedMixin' import VuePress from './plugins/VuePress' import { handleRedirectForCleanUrls } from './redirect.js' -import { getLayoutAsyncComponent } from './util' // built-in components import Content from './components/Content.js' import ContentSlotsDistributor from './components/ContentSlotsDistributor' import OutboundLink from './components/OutboundLink.vue' import ClientOnly from './components/ClientOnly' -import TOC from './components/TOC.vue' // suggest dev server restart on base change if (module.hot) { @@ -44,11 +42,8 @@ Vue.component('ContentSlotsDistributor', ContentSlotsDistributor) Vue.component('OutboundLink', OutboundLink) // component for client-only content Vue.component('ClientOnly', ClientOnly) -// core components -Vue.component('Layout', getLayoutAsyncComponent('Layout')) -Vue.component('NotFound', getLayoutAsyncComponent('NotFound')) // markdown components -Vue.component('TOC', TOC) +Vue.component('TOC', () => import('./components/TOC.vue')) // global helper for adding base path to absolute urls Vue.prototype.$withBase = function (path) { diff --git a/packages/@vuepress/core/lib/client/components/Content.js b/packages/@vuepress/core/lib/client/components/Content.js index c2ed88d487..5e8bad5561 100644 --- a/packages/@vuepress/core/lib/client/components/Content.js +++ b/packages/@vuepress/core/lib/client/components/Content.js @@ -1,5 +1,3 @@ -import { isPageExists } from '../util' - export default { props: { pageKey: String, @@ -10,7 +8,7 @@ export default { }, render (h) { const pageKey = this.pageKey || this.$parent.$page.key - if (isPageExists(pageKey)) { + if (this.$hasComponent(pageKey)) { return h(pageKey) } return h('') diff --git a/packages/@vuepress/core/lib/client/components/GlobalLayout.vue b/packages/@vuepress/core/lib/client/components/GlobalLayout.vue index e137bb3c0f..bfcf5511d4 100644 --- a/packages/@vuepress/core/lib/client/components/GlobalLayout.vue +++ b/packages/@vuepress/core/lib/client/components/GlobalLayout.vue @@ -7,7 +7,7 @@ export default { computed: { layout () { if (this.$page.path) { - if (this.$vuepress.isLayoutExists(this.$page.frontmatter.layout)) { + if (this.$hasComponent(this.$page.frontmatter.layout)) { return this.$page.frontmatter.layout } return 'Layout' diff --git a/packages/@vuepress/core/lib/client/plugins/Store.d.ts b/packages/@vuepress/core/lib/client/plugins/Store.d.ts index d290e852a7..007d0b6887 100644 --- a/packages/@vuepress/core/lib/client/plugins/Store.d.ts +++ b/packages/@vuepress/core/lib/client/plugins/Store.d.ts @@ -1,15 +1,13 @@ import Vue from 'Vue' export declare class Store { - store: Vue; + store: Vue - $get(key: string): any; + $get(key: string): any - $set(key: string, value: any): void; + $set(key: string, value: any): void - $emit: typeof Vue.prototype.$emit; + $emit: typeof Vue.prototype.$emit - $on: typeof Vue.prototype.$on; + $on: typeof Vue.prototype.$on } - - diff --git a/packages/@vuepress/core/lib/client/plugins/VuePress.d.ts b/packages/@vuepress/core/lib/client/plugins/VuePress.d.ts index ef4cf3782f..3d6eba736c 100644 --- a/packages/@vuepress/core/lib/client/plugins/VuePress.d.ts +++ b/packages/@vuepress/core/lib/client/plugins/VuePress.d.ts @@ -1,20 +1,8 @@ -import { Store } from './Store'; -import { AsyncComponent } from 'vue' +import { Store } from './Store' -declare class VuePress extends Store { - isPageExists (pageKey: string): boolean; - - isPageLoaded (pageKey: string): boolean; - - getPageAsyncComponent (pageKey: string): () => Promise; - - loadPageAsyncComponent (pageKey: string): Promise; - - registerPageAsyncComponent (pageKey: string): void; -} - -declare module "vue/types/vue" { +declare module 'vue/types/vue' { export interface Vue { - $vuepress: VuePress; + $vuepress: Store + $hasComponent (key: string): boolean } } diff --git a/packages/@vuepress/core/lib/client/plugins/VuePress.js b/packages/@vuepress/core/lib/client/plugins/VuePress.js index bdb93e1b54..afa90fa9be 100644 --- a/packages/@vuepress/core/lib/client/plugins/VuePress.js +++ b/packages/@vuepress/core/lib/client/plugins/VuePress.js @@ -1,28 +1,23 @@ import Store from './Store' -import { - isPageExists, - isPageLoaded, - getPageAsyncComponent, - isLayoutExists, - isLayoutLoaded, - getLayoutAsyncComponent -} from '../util' -class VuePress extends Store {} +// TODO: reuse this function in shared-utils +function cached (fn) { + const cache = Object.create(null) + return str => { + if (typeof cache[str] === 'undefined') { + cache[str] = fn(str) + } + return cache[str] + } +} -Object.assign(VuePress.prototype, { - isPageExists, - isPageLoaded, - getPageAsyncComponent, - isLayoutExists, - isLayoutLoaded, - getLayoutAsyncComponent -}) +const pascalize = cached((str = '') => str.replace(/(^|-)\w/g, s => s.slice(-1).toUpperCase())) export default { install (Vue) { - const ins = new VuePress() - Vue.$vuepress = ins - Vue.prototype.$vuepress = ins + const vuepress = new Store() + Vue.$vuepress = vuepress + Vue.prototype.$vuepress = vuepress + Vue.prototype.$hasComponent = key => Boolean(Vue.component(pascalize(key))) } } diff --git a/packages/@vuepress/core/lib/client/util.js b/packages/@vuepress/core/lib/client/util.js index e4e483f47b..5094b3dc9e 100644 --- a/packages/@vuepress/core/lib/client/util.js +++ b/packages/@vuepress/core/lib/client/util.js @@ -1,42 +1,3 @@ -import Vue from 'vue' -import layoutComponents from '@internal/layout-components' -import pageComponents from '@internal/page-components' - -const asyncComponents = Object.assign({}, layoutComponents, pageComponents) - -export function isPageExists (pageKey) { - return Boolean(pageComponents[pageKey]) -} - -export function isPageLoaded (pageKey) { - return Boolean(Vue.component(pageKey)) -} - -export function getPageAsyncComponent (pageKey) { - return pageComponents[pageKey] -} - -export function isLayoutExists (layout) { - return Boolean(layoutComponents[layout]) -} - -export function isLayoutLoaded (layout) { - return Boolean(Vue.component(layout)) -} - -export function getLayoutAsyncComponent (pageKey) { - return layoutComponents[pageKey] -} - -export function ensureAsyncComponentsLoaded (...names) { - return Promise.all(names.filter(v => v).map(async (name) => { - if (!Vue.component(name) && asyncComponents[name]) { - const comp = await asyncComponents[name]() - Vue.component(name, comp.default) - } - })) -} - /** * Inject option to Vue SFC * @param {object} options @@ -96,7 +57,7 @@ export function findPageByKey (pages, key) { * * * Usage: - * + * ```js * import { normalizeConfig } from '@app/util' * export default { * data () { @@ -108,7 +69,7 @@ export function findPageByKey (pages, key) { * } * } * } - * + * ``` * * e.g. * diff --git a/packages/@vuepress/core/lib/node/internal-plugins/layoutComponents.js b/packages/@vuepress/core/lib/node/internal-plugins/layoutComponents.js index d8b71a2417..60ad57bcd2 100644 --- a/packages/@vuepress/core/lib/node/internal-plugins/layoutComponents.js +++ b/packages/@vuepress/core/lib/node/internal-plugins/layoutComponents.js @@ -1,13 +1,18 @@ +const { pascalize } = require('@vuepress/shared-utils') + module.exports = (options, ctx) => { return { name: '@vuepress/internal-layout-components', - async clientDynamicModules () { + async enhanceAppFiles () { const componentNames = Object.keys(ctx.themeAPI.layoutComponentMap) - const code = `export default {\n${componentNames - .map(name => ` ${JSON.stringify(name)}: () => import(${JSON.stringify(ctx.themeAPI.layoutComponentMap[name].path)})`) - .join(',\n')} \n}` - return { name: 'layout-components.js', content: code, dirname: 'internal' } + const code = `import Vue from 'vue'\n${componentNames + .map(name => `Vue.component(${JSON.stringify(pascalize(name))}, () => import(${JSON.stringify(ctx.themeAPI.layoutComponentMap[name].path)}))`) + .join(',\n')} \n` + return { + name: 'layout-components.js', + content: code + } } } } diff --git a/packages/@vuepress/core/lib/node/internal-plugins/pageComponents.js b/packages/@vuepress/core/lib/node/internal-plugins/pageComponents.js index e4f1377888..022fc2fdc5 100644 --- a/packages/@vuepress/core/lib/node/internal-plugins/pageComponents.js +++ b/packages/@vuepress/core/lib/node/internal-plugins/pageComponents.js @@ -1,3 +1,5 @@ +const { pascalize } = require('@vuepress/shared-utils') + module.exports = (options, ctx) => { const { pages } = ctx // const componentNames = Object.keys(layoutComponentMap) @@ -5,12 +7,15 @@ module.exports = (options, ctx) => { return { name: '@vuepress/internal-page-components', - async clientDynamicModules () { - const code = `export default {\n${pages + async enhanceAppFiles () { + const code = `import Vue from 'vue'\n${pages .filter(({ _filePath }) => _filePath) - .map(({ key, _filePath }) => ` ${JSON.stringify(key)}: () => import(${JSON.stringify(_filePath)})`) - .join(',\n')} \n}` - return { name: 'page-components.js', content: code, dirname: 'internal' } + .map(({ key, _filePath }) => `Vue.component(${JSON.stringify(pascalize(key))}, () => import(${JSON.stringify(_filePath)}))`) + .join(',\n')} \n` + return { + name: 'page-components.js', + content: code + } } } } diff --git a/packages/@vuepress/core/lib/node/internal-plugins/routes.js b/packages/@vuepress/core/lib/node/internal-plugins/routes.js index d6cf90b9b1..8c91601c00 100644 --- a/packages/@vuepress/core/lib/node/internal-plugins/routes.js +++ b/packages/@vuepress/core/lib/node/internal-plugins/routes.js @@ -42,10 +42,7 @@ function routesCode (pages) { { name: ${JSON.stringify(componentName)}, path: ${JSON.stringify(pagePath)}, - component: GlobalLayout, - beforeEnter: (to, from, next) => { - ensureAsyncComponentsLoaded(${JSON.stringify(layout || 'Layout')}, ${JSON.stringify(componentName)}).then(next) - },${_meta ? `\n meta: ${JSON.stringify(_meta)}` : ''} + component: GlobalLayout,${_meta ? `\n meta: ${JSON.stringify(_meta)}` : ''} }` const dncodedPath = decodeURIComponent(pagePath) diff --git a/packages/@vuepress/shared-utils/src/index.ts b/packages/@vuepress/shared-utils/src/index.ts index 689300274e..df7c1a4df6 100644 --- a/packages/@vuepress/shared-utils/src/index.ts +++ b/packages/@vuepress/shared-utils/src/index.ts @@ -18,6 +18,7 @@ import * as parseEmojis from './parseEmojis' import parseFrontmatter from './parseFrontmatter' import parseHeaders from './parseHeaders' import * as parseVueFrontmatter from './parseVueFrontmatter' +import pascalize from './pascalize' import performance from './performance' import slugify from './slugify' import sort from './sort' @@ -54,6 +55,7 @@ export { parseFrontmatter, parseHeaders, parseVueFrontmatter, + pascalize, performance, slugify, sort, diff --git a/packages/@vuepress/shared-utils/src/pascalize.ts b/packages/@vuepress/shared-utils/src/pascalize.ts new file mode 100644 index 0000000000..21aa2caf5f --- /dev/null +++ b/packages/@vuepress/shared-utils/src/pascalize.ts @@ -0,0 +1,3 @@ +export default function pascalize (source: string) { + return source.replace(/(^|-)\w/g, s => s.slice(-1).toUpperCase()) +} diff --git a/packages/docs/docs/theme/option-api.md b/packages/docs/docs/theme/option-api.md index 57ac02cd9c..0f8d7b0a25 100755 --- a/packages/docs/docs/theme/option-api.md +++ b/packages/docs/docs/theme/option-api.md @@ -111,7 +111,7 @@ export default { computed: { layout () { if (this.$page.path) { - if (this.$vuepress.isLayoutExists(this.$frontmatter.layout)) { + if (this.$hasComponent(this.$frontmatter.layout)) { return this.$frontmatter.layout } return 'Layout' diff --git a/packages/docs/docs/zh/theme/option-api.md b/packages/docs/docs/zh/theme/option-api.md index 3fb8ccd359..3a665eff34 100755 --- a/packages/docs/docs/zh/theme/option-api.md +++ b/packages/docs/docs/zh/theme/option-api.md @@ -111,7 +111,7 @@ export default { computed: { layout () { if (this.$page.path) { - if (this.$vuepress.isLayoutExists(this.$frontmatter.layout)) { + if (this.$hasComponent(this.$frontmatter.layout)) { return this.$frontmatter.layout } return 'Layout'