@samveys/vue
From packages/vue/README.md in the framework repository.
Server-side rendering of Vue pages for @samveys/view.
Peer dependencies: vue and @vue/server-renderer (both ^3.5.44). Nothing else.
.vue files are not compiled here. Compiling single-file components needs @vue/compiler-sfc. Build your SFCs with Vite
(@vitejs/plugin-vue) and pass the compiled components to the engine. Components written as plain objects with render functions
(h()) work without any build step, which is what the examples below use.
Set up
import { http } from '@samveys/http'
import { view } from '@samveys/view'
import { vite } from '@samveys/vite'
import { vue } from '@samveys/vue'
import { defineComponent, h } from 'vue'
const Home = defineComponent({
props: { name: { type: String, required: true } },
setup: (props) => () => h('main', [h('h1', `Hello ${props.name}`)]),
})
export const plugins = [http(), view(), vue({ components: { Home }, entry: 'resources/app.ts', title: 'My app' }), vite()]| Option | Meaning |
|---|---|
components | Page components by name |
entry | Vite entry of the browser bundle; its tags are added to the page (needs @samveys/vite) |
id | Id of the app element. Default app |
title, head | <title> (escaped) and extra <head> markup (trusted) |
Render
import type { Router } from '@samveys/http'
export function routes(router: Router): void {
router.get('/', (ctx) => ctx.view.render('Home', { name: 'Ann' }))
}The page is rendered with createSSRApp and renderToString, then wrapped in a shell:
<div id="app"><main><h1>Hello Ann</h1></main></div>
<script type="application/json" id="__SVS_STATE__">{"component":"Home","props":{"name":"Ann"}}</script>
<script type="module" src="/build/assets/app-abc123.js"></script>import { parseState, serializeState } from '@samveys/vue'
const text = serializeState({ component: 'Home', props: { name: '</script>' } })
export const roundTrips = text.includes('<') === false && parseState<{ props: { name: string } }>(text).props.name === '</script>'The state is JSON with <, >, & and line separators written as \uXXXX, so </script> inside a value cannot end the element,
and JSON.parse gives back exactly the props. serializeState and parseState do the two directions.
Hydrate in the browser
The browser entry (built by Vite) reads the state and mounts the same component on the server-rendered markup:
import { createSSRApp } from 'vue'
import { parseState } from '@samveys/vue'
import Home from './pages/Home.vue'
const pages = { Home }
const state = parseState<{ component: keyof typeof pages; props: Record<string, unknown> }>(document.getElementById('__SVS_STATE__')!.textContent!)
createSSRApp(pages[state.component], state.props).mount('#app')