diff --git a/renderer/_default.page.client.ts b/renderer/_default.page.client.ts index 53796fc..269dfd9 100644 --- a/renderer/_default.page.client.ts +++ b/renderer/_default.page.client.ts @@ -1,17 +1,36 @@ import { createApp } from './app' -import type { PageContextClient } from '#types/PageContext' +import type { PageContext, VikePageContext } from '#types/PageContext' -// This render() hook only supports SSR, see https://vike.dev/render-modes for how to modify render() to support SPA -async function render(pageContext: PageContextClient) { - const { Page, pageProps } = pageContext - if (!Page) throw new Error('Client-side render() hook expects pageContext.Page to be defined') - const app = createApp(Page, pageProps, pageContext) - app.mount('#app') +let app: ReturnType +async function render(pageContext: VikePageContext & PageContext) { + if (!app) { + app = createApp(pageContext) + app.mount('#app') + } else { + // console.log(pageContext) + app.changePage(pageContext) + } } -/* To enable Client-side Routing: -export const clientRouting = true -// !! WARNING !! Before doing so, read https://vike.dev/clientRouting */ +function onHydrationEnd() { + // eslint-disable-next-line no-console + console.log('Hydration finished; page is now interactive.') +} +function onPageTransitionStart() { + // eslint-disable-next-line no-console + console.log('Page transition start') + // document.querySelector('.content')!.classList.add('page-transition') +} +function onPageTransitionEnd() { + // eslint-disable-next-line no-console + console.log('Page transition end') + // document.querySelector('.content')!.classList.remove('page-transition') +} +export const clientRouting = true +export const prefetchStaticAssets = 'viewport' export { render } +export { onHydrationEnd } +export { onPageTransitionStart } +export { onPageTransitionEnd } diff --git a/renderer/_default.page.server.ts b/renderer/_default.page.server.ts index 041aa80..f6d55fe 100644 --- a/renderer/_default.page.server.ts +++ b/renderer/_default.page.server.ts @@ -6,20 +6,14 @@ import { META } from '#src/env' import { createApp } from './app' -import type { PageContextServer } from '#types/PageContext' +import type { PageContextServer, PageContext } from '#types/PageContext' import type { App } from 'vue' // See https://vike.dev/data-fetching -export const passToClient = ['pageProps', 'urlPathname', 'routeParams'] +export const passToClient = ['pageProps', /* 'urlPathname', */ 'routeParams'] -async function render(pageContext: PageContextServer) { - const { Page, pageProps } = pageContext - // This render() hook only supports SSR, see https://vike.dev/render-modes for how to modify render() to support SPA - if (!Page) { - throw new Error('My render() hook expects pageContext.Page to be defined') - } - - const app = createApp(Page, pageProps, pageContext, false) +async function render(pageContext: PageContextServer & PageContext) { + const app = createApp(pageContext, false) const appHtml = await renderToString(app) diff --git a/renderer/app.ts b/renderer/app.ts index 0e31264..0680983 100644 --- a/renderer/app.ts +++ b/renderer/app.ts @@ -1,32 +1,36 @@ import piniaPluginPersistedstate from 'pinia-plugin-persistedstate' -import { createSSRApp, defineComponent, h } from 'vue' +import { createSSRApp, defineComponent, h, markRaw, reactive } from 'vue' import PageShell from '#components/PageShell.vue' import { setPageContext } from '#context/usePageContext' import i18n from '#plugins/i18n' import pinia from '#plugins/pinia' import CreateVuetify from '#plugins/vuetify' -import { Page } from '#types/Page' -import { PageProps } from '#types/PageProps' -import type { PageContext } from '#types/PageContext' +import type { Component } from '#types/Component' +import type { PageContext, VikePageContext } from '#types/PageContext' const vuetify = CreateVuetify(i18n) -function createApp( - Page: Page, - pageProps: PageProps | undefined, - pageContext: PageContext, - isClient = true, -) { - const PageWithLayout = defineComponent({ +function createApp(pageContext: VikePageContext & PageContext, isClient = true) { + let rootComponent: Component + const PageWithWrapper = defineComponent({ + data: () => ({ + Page: markRaw(pageContext.Page), + pageProps: markRaw(pageContext.pageProps || {}), + isClient, + }), + created() { + // eslint-disable-next-line @typescript-eslint/no-this-alias + rootComponent = this + }, render() { return h( PageShell, {}, { - default() { - return h(Page, pageProps || {}) + default: () => { + return h(this.Page, this.pageProps) }, }, ) @@ -37,15 +41,35 @@ function createApp( pinia.use(piniaPluginPersistedstate) } - const app = createSSRApp(PageWithLayout) + const app = createSSRApp(PageWithWrapper) app.use(pinia) app.use(i18n) app.use(vuetify) + objectAssign(app, { + changePage: (pageContext: VikePageContext & PageContext) => { + Object.assign(pageContextReactive, reactive(pageContext)) + rootComponent.Page = markRaw(pageContext.Page) + rootComponent.pageProps = markRaw(pageContext.pageProps || {}) + }, + }) + + // When doing Client Routing, we mutate pageContext (see usage of `app.changePage()` in `_default.page.client.js`). + // We therefore use a reactive pageContext. + const pageContextReactive = reactive(pageContext) + // Make pageContext available from any Vue component - setPageContext(app, pageContext) + setPageContext(app, pageContextReactive) return app } +// Same as `Object.assign()` but with type inference +function objectAssign( + obj: Obj, + objAddendum: ObjAddendum, +): asserts obj is Obj & ObjAddendum { + Object.assign(obj, objAddendum) +} + export { createApp } diff --git a/renderer/context/usePageContext.ts b/renderer/context/usePageContext.ts index e7e7163..9f8c479 100644 --- a/renderer/context/usePageContext.ts +++ b/renderer/context/usePageContext.ts @@ -3,11 +3,11 @@ import { inject } from 'vue' -import { PageContext } from '#types/PageContext' +import { PageContext, VikePageContext } from '#types/PageContext' import type { App, InjectionKey } from 'vue' -const key: InjectionKey = Symbol(undefined) +const key: InjectionKey = Symbol(undefined) function usePageContext() { const pageContext = inject(key) @@ -15,7 +15,7 @@ function usePageContext() { return pageContext } -function setPageContext(app: App, pageContext: PageContext) { +function setPageContext(app: App, pageContext: VikePageContext & PageContext) { app.provide(key, pageContext) } diff --git a/src/components/VikeBtn.vue b/src/components/VikeBtn.vue index 3d86407..cf760ba 100644 --- a/src/components/VikeBtn.vue +++ b/src/components/VikeBtn.vue @@ -1,5 +1,5 @@ @@ -8,7 +8,7 @@ import { usePageContext } from '#context/usePageContext' const pageContext = usePageContext() -const isRoutSelected = (href: string) => { +const isRouteSelected = (href: string) => { if (href === '/app') { return pageContext.urlPathname.indexOf(href) === 0 } diff --git a/src/pages/app/index.page.vue b/src/pages/app/index.page.vue index c6f25f5..9de6e38 100644 --- a/src/pages/app/index.page.vue +++ b/src/pages/app/index.page.vue @@ -37,7 +37,12 @@ import { useCounterStore } from '#stores/counter' const counter = useCounterStore() +/* const { routeParams: { id: page }, } = usePageContext() +*/ +const { routeParams } = usePageContext() +const page = routeParams?.id +// console.log(page) diff --git a/types/Component.ts b/types/Component.ts new file mode 100644 index 0000000..b996d61 --- /dev/null +++ b/types/Component.ts @@ -0,0 +1,2 @@ +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export type Component = any diff --git a/types/PageContext.ts b/types/PageContext.ts index 24a52a1..86a8da4 100644 --- a/types/PageContext.ts +++ b/types/PageContext.ts @@ -3,31 +3,31 @@ import { PageProps } from '#types/PageProps' export type { PageContextServer, - /* - // When using Client Routing https://vike.dev/clientRouting - PageContextClient, - PageContext, - / */ + + // When using Client Routing https://vike.dev/clientRouting + PageContextClient, + PageContext as VikePageContext, + PageContextBuiltInClientWithClientRouting as PageContextBuiltInClient, // When using Server Routing + /* PageContextClientWithServerRouting as PageContextClient, PageContextWithServerRouting as PageContext, + */ //* / } from 'vike/types' -// https://vike.dev/pageContext#typescript -declare global { - // eslint-disable-next-line @typescript-eslint/no-namespace - namespace Vike { - interface PageContext { - Page: Page - pageProps?: PageProps - urlPathname: string - exports: { - documentProps?: { - title?: string - description?: string - } - } +export type PageContext = { + Page: Page + pageProps?: PageProps + urlPathname: string + exports: { + documentProps?: { + title?: string + description?: string } } + documentProps?: { + title: string + description?: string + } }