vite-ssr
vite-ssr copied to clipboard
How to use NaiveUI with vite-ssr?
NaiveUI provide a good example of how to use it with ssr: https://github.com/07akioni/naive-ui-vite-ssr . The algorithm is as follows: first you need to collect all css from project and then inject its in the index.html file on the server. But I can't figure out how to do it with vite-ssr.
This is my workaround:
I modify entry-server.js and html.js in vite-ssr (commented // changed), and use postinstall script to replace it after yarn/npm install
package.json
"scripts": {
"postinstall": "cp ./src/utils/entry-server.js ./node_modules/vite-ssr/vue/entry-server.js && cp ./src/utils/html.js ./node_modules/vite-ssr/utils/html.js"
}
./src/utils/entry-server.js
import { createApp } from 'vue'
import { renderToString } from '@vue/server-renderer'
import { setup } from '@css-render/vue3-ssr' // changed
import { createRouter, createMemoryHistory } from 'vue-router'
import { getFullPath, withoutSuffix } from '../utils/route'
import { addPagePropsGetterToRoutes } from './utils'
import { renderHeadToString } from '@vueuse/head'
import coreViteSSR from '../core/entry-server.js'
import { provideContext } from './components.js'
export { ClientOnly, useContext } from './components.js'
export const viteSSR = function viteSSR(
App,
{
routes,
base,
routerOptions = {},
pageProps = { passToPage: true },
...options
},
hook
) {
if (pageProps && pageProps.passToPage) {
addPagePropsGetterToRoutes(routes)
}
return coreViteSSR(options, async (context, { isRedirect, ...extra }) => {
const app = createApp(App)
const { collect } = setup(app) // changed
const routeBase = base && withoutSuffix(base(context), '/')
const router = createRouter({
...routerOptions,
history: createMemoryHistory(routeBase),
routes: routes,
})
router.beforeEach((to) => {
to.meta.state = extra.initialState || null
})
provideContext(app, context)
const fullPath = getFullPath(context.url, routeBase)
const { head } =
(hook &&
(await hook({
app,
router,
initialRoute: router.resolve(fullPath),
...context,
}))) ||
{}
app.use(router)
router.push(fullPath)
await router.isReady()
if (isRedirect()) return {}
Object.assign(
context.initialState || {},
(router.currentRoute.value.meta || {}).state || {}
)
const body = await renderToString(app, context)
const cssHtml = collect() // changed
if (isRedirect()) return {}
const {
headTags = '',
htmlAttrs = '',
bodyAttrs = '',
} = head ? renderHeadToString(head) : {}
return { body, cssHtml, headTags, htmlAttrs, bodyAttrs } // changed
})
}
export default viteSSR
./src/utils/html.js
export function findDependencies(modules, manifest) {
const files = new Set()
for (const id of modules || []) {
for (const file of manifest[id] || []) {
files.add(file)
}
}
return [...files]
}
export function renderPreloadLinks(files) {
let link = ''
for (const file of files || []) {
if (file.endsWith('.js')) {
link += `<link rel="modulepreload" crossorigin href="${file}">`
} else if (file.endsWith('.css')) {
link += `<link rel="stylesheet" href="${file}">`
}
}
return link
}
// @ts-ignore
const containerId = __CONTAINER_ID__
const containerRE = new RegExp(
`<div id="${containerId}"([\\s\\w\\-"'=[\\]]*)><\\/div>`
)
export function buildHtmlDocument(
template,
{ cssHtml, htmlAttrs, bodyAttrs, headTags, body, initialState } // changed
) {
// @ts-ignore
if (__DEV__) {
if (template.indexOf(`id="${containerId}"`) === -1) {
console.warn(
`[SSR] Container with id "${containerId}" was not found in index.html`
)
}
}
if (htmlAttrs) {
template = template.replace('<html', `<html ${htmlAttrs} `)
}
// changed
if (cssHtml) {
template = template.replace('<meta name="naive-ui-style-ssr" />', cssHtml)
}
if (bodyAttrs) {
template = template.replace('<body', `<body ${bodyAttrs} `)
}
if (headTags) {
template = template.replace('</head>', `\n${headTags}\n</head>`)
}
return template.replace(
containerRE,
// Use function parameter here to avoid replacing `$1` in body or initialState.
// https://github.com/frandiox/vite-ssr/issues/123
(_, d1) =>
`<div id="${containerId}" data-server-rendered="true"${d1 || ''}>${
body || ''
}</div>\n\n <script>window.__INITIAL_STATE__=${
initialState || "'{}'"
}</script>`
)
}
./index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta name="naive-ui-style-ssr" />
<meta name="naive-ui-style" />
<meta charset="UTF-8" />
<link rel="icon" href="/favicon.ico" />
<meta
name="viewport"
content="width=device-width, initial-scale=1.0, maximum-scale=1.0"
/>
</head>
<body>
<div id="app"></div>
<script type="module" src="/src/main.js"></script>
</body>
</html>
./src/main.js
import { setup } from '@css-render/vue3-ssr' // unused but needs to import (I don't know why either)
./vite.config.js
import Components from 'unplugin-vue-components/vite'
import { NaiveUiResolver } from 'unplugin-vue-components/resolvers'
export default {
plugins: [Components({ resolvers: [NaiveUiResolver()] })],
}