mirror of
https://git.hmsn.ink/kospo/svcm/oa.git
synced 2026-03-20 08:03:34 +09:00
first
This commit is contained in:
144
src/entry-server.ts
Normal file
144
src/entry-server.ts
Normal file
@@ -0,0 +1,144 @@
|
||||
import devalue from '@nuxt/devalue'
|
||||
import { getRequestURL, setResponseHeader } from 'h3'
|
||||
import { renderToWebStream } from 'vue/server-renderer'
|
||||
import { renderSSRHead } from '@unhead/ssr'
|
||||
import { minify } from 'html-minifier-terser'
|
||||
|
||||
import type { VueroServerRender, VueroSSRContext } from '/@server/types'
|
||||
import { createApp } from '/@src/app'
|
||||
|
||||
const placeholderAppRe = /<div id="app"([\s\w\-"'=[\]]*)><\/div>/
|
||||
const placeholderStream = '<!--vueroplaceholder-->'
|
||||
|
||||
export const render: VueroServerRender = async ({
|
||||
event,
|
||||
manifest,
|
||||
template,
|
||||
}) => {
|
||||
const url = getRequestURL(event)
|
||||
|
||||
event.context.initialState = {}
|
||||
|
||||
const { app, router, pinia, head } = await createApp(event)
|
||||
|
||||
// set the router to the desired URL before rendering
|
||||
router.push(url.pathname)
|
||||
await router.isReady()
|
||||
|
||||
// passing SSR context object which will be available via useSSRContext()
|
||||
// @vitejs/plugin-vue injects code into a component's setup() that registers
|
||||
// itself on ctx.modules. After the render, ctx.modules would contain all the
|
||||
// components that have been instantiated during this render call.
|
||||
const ctx: VueroSSRContext = {
|
||||
event,
|
||||
}
|
||||
|
||||
const stream = renderToWebStream(app, ctx)
|
||||
const {
|
||||
headTags,
|
||||
htmlAttrs,
|
||||
bodyAttrs,
|
||||
bodyTags,
|
||||
bodyTagsOpen,
|
||||
} = await renderSSRHead(head)
|
||||
|
||||
event.context.initialState.pinia = pinia?.state.value
|
||||
|
||||
// the SSR manifest generated by Vite contains module -> chunk/asset mapping
|
||||
// which we can then use to determine what files need to be preloaded for this
|
||||
// request.
|
||||
const preloadLinks = renderPreloadLinks(ctx.modules, manifest)
|
||||
|
||||
let wrapper = template
|
||||
.replace(`<html>`, `<html${htmlAttrs}>`)
|
||||
.replace(`<head>`, `<head>${headTags}`)
|
||||
.replace(`</head>`, `${preloadLinks}</head>`)
|
||||
.replace(`<body>`, `<body${bodyAttrs}>${bodyTagsOpen}`)
|
||||
.replace(`</body>`, `${bodyTags}</body>`)
|
||||
|
||||
// minify app wrapper
|
||||
wrapper = await minify(wrapper, {
|
||||
collapseWhitespace: true,
|
||||
collapseInlineTagWhitespace: true,
|
||||
removeComments: true,
|
||||
minifyJS: true,
|
||||
})
|
||||
|
||||
wrapper = wrapper.replace(
|
||||
placeholderAppRe,
|
||||
`<div id="app" data-server-rendered="true"$1>${placeholderStream}</div><script>window.__vuero__=${devalue(
|
||||
event.context.initialState,
|
||||
)}</script>`,
|
||||
)
|
||||
|
||||
setResponseHeader(event, 'Content-Type', 'text/html')
|
||||
|
||||
return stream.pipeThrough(wrapTemplate(wrapper))
|
||||
}
|
||||
|
||||
function wrapTemplate(html: string) {
|
||||
const [pre, post] = html.split(placeholderStream) as [string, string]
|
||||
|
||||
return new TransformStream<Uint8Array, Uint8Array>({
|
||||
start(controller) {
|
||||
controller.enqueue(new TextEncoder().encode(pre))
|
||||
},
|
||||
flush(controller) {
|
||||
controller.enqueue(new TextEncoder().encode(post))
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
function renderPreloadLinks(modules?: any, manifest?: any) {
|
||||
let links = ''
|
||||
if (!modules) return links
|
||||
if (!manifest) return links
|
||||
|
||||
const seen = new Set()
|
||||
modules?.forEach((id: string) => {
|
||||
const files = manifest[id]
|
||||
if (files) {
|
||||
files.forEach((file: any) => {
|
||||
if (!seen.has(file)) {
|
||||
seen.add(file)
|
||||
links += renderPreloadLink(file)
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
return links
|
||||
}
|
||||
|
||||
function renderPreloadLink(file: string) {
|
||||
if (file.endsWith('.js') || file.endsWith('.mjs')) {
|
||||
return `<link rel="modulepreload" crossorigin href="${file}">`
|
||||
}
|
||||
else if (file.endsWith('.css')) {
|
||||
return `<link rel="stylesheet" href="${file}">`
|
||||
}
|
||||
else if (file.endsWith('.woff')) {
|
||||
return `<link rel="preload" href="${file}" as="font" type="font/woff" crossorigin>`
|
||||
}
|
||||
else if (file.endsWith('.woff2')) {
|
||||
return `<link rel="preload" href="${file}" as="font" type="font/woff2" crossorigin>`
|
||||
}
|
||||
else if (file.endsWith('.gif')) {
|
||||
return `<link rel="preload" href="${file}" as="image" type="image/gif">`
|
||||
}
|
||||
else if (file.endsWith('.jpg') || file.endsWith('.jpeg')) {
|
||||
return `<link rel="preload" href="${file}" as="image" type="image/jpeg">`
|
||||
}
|
||||
else if (file.endsWith('.png')) {
|
||||
return `<link rel="preload" href="${file}" as="image" type="image/png">`
|
||||
}
|
||||
else if (file.endsWith('.webp')) {
|
||||
return `<link rel="preload" href="${file}" as="image" type="image/webp">`
|
||||
}
|
||||
else if (file.endsWith('.svg')) {
|
||||
return `<link rel="prefetch" href="${file}" as="image" type="image/svg+xml"/>`
|
||||
}
|
||||
else {
|
||||
console.log('missing preload link for', file)
|
||||
return ''
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user