client side routing works except parameters

This commit is contained in:
Ulf Gebhardt 2023-11-23 16:27:41 +01:00
parent aac1b93cc8
commit fedeacad19
Signed by: ulfgebhardt
GPG Key ID: DA6B843E748679C9
8 changed files with 103 additions and 59 deletions

View File

@ -1,17 +1,36 @@
import { createApp } from './app' 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 let app: ReturnType<typeof createApp>
async function render(pageContext: PageContextClient) { async function render(pageContext: VikePageContext & PageContext) {
const { Page, pageProps } = pageContext if (!app) {
if (!Page) throw new Error('Client-side render() hook expects pageContext.Page to be defined') app = createApp(pageContext)
const app = createApp(Page, pageProps, pageContext)
app.mount('#app') app.mount('#app')
} else {
// console.log(pageContext)
app.changePage(pageContext)
}
} }
/* To enable Client-side Routing: function onHydrationEnd() {
export const clientRouting = true // eslint-disable-next-line no-console
// !! WARNING !! Before doing so, read https://vike.dev/clientRouting */ 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 { render }
export { onHydrationEnd }
export { onPageTransitionStart }
export { onPageTransitionEnd }

View File

@ -6,20 +6,14 @@ import { META } from '#src/env'
import { createApp } from './app' import { createApp } from './app'
import type { PageContextServer } from '#types/PageContext' import type { PageContextServer, PageContext } from '#types/PageContext'
import type { App } from 'vue' import type { App } from 'vue'
// See https://vike.dev/data-fetching // See https://vike.dev/data-fetching
export const passToClient = ['pageProps', 'urlPathname', 'routeParams'] export const passToClient = ['pageProps', /* 'urlPathname', */ 'routeParams']
async function render(pageContext: PageContextServer) { async function render(pageContext: PageContextServer & PageContext) {
const { Page, pageProps } = pageContext const app = createApp(pageContext, false)
// 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)
const appHtml = await renderToString(app) const appHtml = await renderToString(app)

View File

@ -1,32 +1,36 @@
import piniaPluginPersistedstate from 'pinia-plugin-persistedstate' 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 PageShell from '#components/PageShell.vue'
import { setPageContext } from '#context/usePageContext' import { setPageContext } from '#context/usePageContext'
import i18n from '#plugins/i18n' import i18n from '#plugins/i18n'
import pinia from '#plugins/pinia' import pinia from '#plugins/pinia'
import CreateVuetify from '#plugins/vuetify' 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) const vuetify = CreateVuetify(i18n)
function createApp( function createApp(pageContext: VikePageContext & PageContext, isClient = true) {
Page: Page, let rootComponent: Component
pageProps: PageProps | undefined, const PageWithWrapper = defineComponent({
pageContext: PageContext, data: () => ({
isClient = true, Page: markRaw(pageContext.Page),
) { pageProps: markRaw(pageContext.pageProps || {}),
const PageWithLayout = defineComponent({ isClient,
}),
created() {
// eslint-disable-next-line @typescript-eslint/no-this-alias
rootComponent = this
},
render() { render() {
return h( return h(
PageShell, PageShell,
{}, {},
{ {
default() { default: () => {
return h(Page, pageProps || {}) return h(this.Page, this.pageProps)
}, },
}, },
) )
@ -37,15 +41,35 @@ function createApp(
pinia.use(piniaPluginPersistedstate) pinia.use(piniaPluginPersistedstate)
} }
const app = createSSRApp(PageWithLayout) const app = createSSRApp(PageWithWrapper)
app.use(pinia) app.use(pinia)
app.use(i18n) app.use(i18n)
app.use(vuetify) 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 // Make pageContext available from any Vue component
setPageContext(app, pageContext) setPageContext(app, pageContextReactive)
return app return app
} }
// Same as `Object.assign()` but with type inference
function objectAssign<Obj extends object, ObjAddendum>(
obj: Obj,
objAddendum: ObjAddendum,
): asserts obj is Obj & ObjAddendum {
Object.assign(obj, objAddendum)
}
export { createApp } export { createApp }

View File

@ -3,11 +3,11 @@
import { inject } from 'vue' import { inject } from 'vue'
import { PageContext } from '#types/PageContext' import { PageContext, VikePageContext } from '#types/PageContext'
import type { App, InjectionKey } from 'vue' import type { App, InjectionKey } from 'vue'
const key: InjectionKey<PageContext> = Symbol(undefined) const key: InjectionKey<VikePageContext & PageContext> = Symbol(undefined)
function usePageContext() { function usePageContext() {
const pageContext = inject(key) const pageContext = inject(key)
@ -15,7 +15,7 @@ function usePageContext() {
return pageContext return pageContext
} }
function setPageContext(app: App, pageContext: PageContext) { function setPageContext(app: App, pageContext: VikePageContext & PageContext) {
app.provide(key, pageContext) app.provide(key, pageContext)
} }

View File

@ -1,5 +1,5 @@
<template> <template>
<v-btn :variant="isRoutSelected($attrs.href as string) ? 'tonal' : 'flat'"> <v-btn :variant="isRouteSelected($attrs.href as string) ? 'tonal' : 'flat'">
<slot /> <slot />
</v-btn> </v-btn>
</template> </template>
@ -8,7 +8,7 @@ import { usePageContext } from '#context/usePageContext'
const pageContext = usePageContext() const pageContext = usePageContext()
const isRoutSelected = (href: string) => { const isRouteSelected = (href: string) => {
if (href === '/app') { if (href === '/app') {
return pageContext.urlPathname.indexOf(href) === 0 return pageContext.urlPathname.indexOf(href) === 0
} }

View File

@ -37,7 +37,12 @@ import { useCounterStore } from '#stores/counter'
const counter = useCounterStore() const counter = useCounterStore()
/*
const { const {
routeParams: { id: page }, routeParams: { id: page },
} = usePageContext() } = usePageContext()
*/
const { routeParams } = usePageContext()
const page = routeParams?.id
// console.log(page)
</script> </script>

2
types/Component.ts Normal file
View File

@ -0,0 +1,2 @@
// eslint-disable-next-line @typescript-eslint/no-explicit-any
export type Component = any

View File

@ -3,22 +3,20 @@ import { PageProps } from '#types/PageProps'
export type { export type {
PageContextServer, PageContextServer,
/*
// When using Client Routing https://vike.dev/clientRouting // When using Client Routing https://vike.dev/clientRouting
PageContextClient, PageContextClient,
PageContext, PageContext as VikePageContext,
/ */ PageContextBuiltInClientWithClientRouting as PageContextBuiltInClient,
// When using Server Routing // When using Server Routing
/*
PageContextClientWithServerRouting as PageContextClient, PageContextClientWithServerRouting as PageContextClient,
PageContextWithServerRouting as PageContext, PageContextWithServerRouting as PageContext,
*/
//* / //* /
} from 'vike/types' } from 'vike/types'
// https://vike.dev/pageContext#typescript export type PageContext = {
declare global {
// eslint-disable-next-line @typescript-eslint/no-namespace
namespace Vike {
interface PageContext {
Page: Page Page: Page
pageProps?: PageProps pageProps?: PageProps
urlPathname: string urlPathname: string
@ -28,6 +26,8 @@ declare global {
description?: string description?: string
} }
} }
} documentProps?: {
title: string
description?: string
} }
} }