Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How to use NaiveUI with vite-ssr? #168

Open
yuriifabirovskyi opened this issue Jun 15, 2022 · 1 comment
Open

How to use NaiveUI with vite-ssr? #168

yuriifabirovskyi opened this issue Jun 15, 2022 · 1 comment

Comments

@yuriifabirovskyi
Copy link

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.

@SkyleLai
Copy link

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()] })],
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants