Vite: [Doc/Tuto] Example of prerendering for vue.

Created on 11 Oct 2020  路  9Comments  路  Source: vitejs/vite

Hi,

I made a simple landing page with vue 3 and vuejs, and would like to prerender it for seo and performance (and usage with netlify form).

I was looking for an existing solution, but didn't find anything, so I've dug in the builder of vite and made my own script. I tried to make it as simple as possible, and at the end it's pretty straightforward, but I've spent few hours to understand vite and rollup build, and I think it can be of interest to some people (you can start from there for SSR too).

I share it in the issue to keep a record, but I think it can be good to add some documentation about prerendering and ssr with vite and vue3.

Please feel free to give me your feedbacks, so we can improve it.

Prerendering with vite and vue3

Update you main.js file

// main.js
import { createApp as createClientApp, createSSRApp } from 'vue'

import './assets/main.css'
import App from './App.vue'

const inBrowser = typeof window !== 'undefined'

// We export a function that will create a standard app in dev, but a ssr app for production.
// To enable hydration on the client, use createSSRApp instead of createApp to create your app instance (separate imports allows hydration logic to be tree-shakeable for apps that do not require hydration).

export const createApp = () => (
  process.env.NODE_ENV === 'production'
    ? createSSRApp(App)
    : createClientApp(App)
)

// When in browser, we create and mount the app like before.
if (inBrowser) {
  createApp().mount('#app')
}

Add a build.js file

const { ssrBuild, build } = require('vite')
const { resolve } = require('path')
const { writeFileSync, rmdirSync } = require('fs')
const renderer = require('@vue/server-renderer')

const build = async () => {
  const outDir = resolve(process.cwd(), 'dist')
  const tmpDir = resolve(process.cwd(), 'dist/tmp')

  // Classic client build. Output assets, and index.html. Returns chunks and generated html.
  const clientResult = await build({ outDir })

  // Build for server side (imports are replaced by requires, and no assets)
  await ssrBuild({
    outDir: tmpDir,

    // Important to keep the export createApp in the main.js. 
    rollupInputOptions: {
      input: { index: 'src/main.js' },
      preserveEntrySignatures: 'allow-extension',
    },
  })

  // We import our main.js createApp from the new server flavored built file.
  const { createApp } = require(tmpDir)

  // Render the html of the app, and insert it in the generated index.html built for client side.
  const content = await renderer.renderToString(createApp()) 
  const indexPath = resolve(outDir, 'index.html')
  const indexOutput = clientResult.html.replace(
    '<div id="app">',
    `<div id="app" data-server-rendered="true">${content}`,
  )

  // Write the new file and remove the server build, we don't need it anymore.
  writeFileSync(indexPath, indexOutput)

  rmdirSync(tmpDir, { recursive: true })

  console.log('馃帀 Page generated!')
  process.exit()
}

build().catch((e) => {
  console.error(e)
  process.exit(1)
})

Update you build script.

-    "build": "cross-env NODE_ENV=production vite build"
+    "build": "cross-env NODE_ENV=production node build.js"

Have fun! 馃帀 (and please share me your thoughts!)

Moreover, I've only one file here, but next step could be to generate several pages depending of routes.

documentation

Most helpful comment

It's for a next-gen framework, Factor 7

All 9 comments

@mathieutu thanks for posting this. Was helpful.

vite-ssr might be helpful in some cases.

@frandiox also took a look at your repo as well, also good. I've been struggling with some fringe issues like teleports in SSR (metatags), will report back on where things end up.

Actually, I've just found there where some breaking changes in rc7 and rc9 which break the build process. You need to adapt it to the new versions.

@mathieutu @frandiox you SSR pros have thoughts on this issue? https://github.com/vitejs/vite/issues/972

@arpowers I'm far from pro 馃槀
That would be a nice feature but I personally don't need it right now. My approach is running SPA locally during development so there's no need to watch files and build for SSR until deployment. You can see the approach in Vitedge, perhaps you can try something similar for your app.

You don't think that it's fundamentally important to match your development and production environments?

Hydration and pre-fetching are error-prone enough where I don't feel comfortable developing without SSR.

@arpowers It is indeed important and I hope they support build watch at some point. However, Vite is still under development and I don't think we can do anything but wait (there are PRs open but not enough people to manage them). In the meantime, you can go with SPA approach at least if this issue is stopping you from building your app 馃

It's for a next-gen framework, Factor 7

Was this page helpful?
0 / 5 - 0 ratings

Related issues

duanxianze picture duanxianze  路  3Comments

cmwhited picture cmwhited  路  3Comments

stefnotch picture stefnotch  路  3Comments

robrich picture robrich  路  4Comments

pd4d10 picture pd4d10  路  3Comments