预渲染

如果服务端渲染的数据是完全静态的,即不依赖于不同用户访问看到的内容不一样,那么可以采用预渲染(Server Side General,SSG)来实现服务端渲染的优势,即直接通过前端构建来生成首屏的静态页面资源,不依赖于后端 Node.js 服务,用户通过浏览器访问时,直接打开预先生成的 HTML 页面即可,这也有利于 SEO、首屏提速等优化,并且不需要 Node.js 服务,减少后端运维成本。

server.js 同级目录新增 prerender.js,其内容如下:

// Pre-render the app into static HTML.
// 预渲染出首屏的页面并生成HTML文件

const fs = require('fs')
const path = require('path')

const toAbsolute = (p) => path.resolve(__dirname, p)
// 资源映射文件
const manifest = require('./dist/static/ssr-manifest.json')
// 模板文件
const template = fs.readFileSync(toAbsolute('dist/static/index.html'), 'utf-8')
// 调用生成模式下的entry-server.js,可以利用这里的逻辑添加preload资源
const { render } = require('./dist/server/entry-server.js')

;(async () => {
  // 预渲染指定路由的首屏页面
  // 这里首屏的路由是 /
  let url = '/'

  const [appHtml, preloadLinks] = await render(url, manifest)

  const html = template
      .replace(`<!--preload-links-->`, preloadLinks)
      .replace(`<!--app-html-->`, appHtml)

  const filePath = `dist/static${url === '/' ? '/index' : url}.html`
  fs.writeFileSync(toAbsolute(filePath), html)

  // HTML文件生成后,删除无用文件
  fs.unlinkSync(toAbsolute('dist/static/ssr-manifest.json'))
})()

执行 node prerender.js 即可在 dist/static 目录下生成预渲染的首屏 index.html 文件,这个文件不是单独的空壳子,而是含有首屏内容的静态 HTML 页面,代码如下:

...
<body>
    <div id="app"><h1 data-v-485b7ba6>首屏prerender内容</h1></div>
</body>
...

修改 package.json,完善命令:

"scripts": {
    // 提前将预渲染需要的资源准备好
    "prerender": "vite build --ssrManifest --outDir dist/static && npm run build:ssr && node prerender"
},

预渲染不适用于经常变化的数据,比如股票代码网站、天气预报网站。因为此时的数据是动态的,而预渲染要求事先生成好页面内容,这就无法保证这些数据的实时性。