跳至内容

服务器端渲染 (SSR)

提示

手动进行服务器端渲染可能会非常复杂,建议使用 Nuxt。阅读 Nuxt 指南,大多数内容已经为您配置好了。

使用 Vitesse 进行 SSR

在进行 SSR(服务器端渲染)时,您需要等待服务器上的数据进行序列化,并在客户端检索数据,然后在客户端显示数据。如果您在组件中使用可组合函数,VueFire 已经为您等待数据了。

vue
<script setup>
import { doc, getDoc } from 'firebase/firestore'
import { useDocument, useFirestore } from 'vuefire'

const db = useFirestore()
// automatically waits for the data to be loaded on the server
const quizResults = useDocument(doc(db, 'quizzes', quizId))
</script>

您只需要将数据转义并序列化到客户端,并处理状态水合。这取决于您使用什么进行 SSR,但应该类似于使用 Vitesse 模板 的示例。

添加一个 src/modules/vuefire.ts(或 .js)文件

ts
// src/modules/vuefire.ts
import { initializeApp } from 'firebase/app'
import { VueFire useSSRInitialState } from 'vuefire'
import type { UserModule } from '~/types'

export const install: UserModule = ({ isClient, initialState, app }) => {
  const firebaseApp = initializeApp({
    // your config
  })

  app.use(VueFire, { firebaseApp })

  if (isClient) {
    // reuse the initial state on the client
    useSSRInitialState(initialState.vuefire, firebaseApp)
  } else {
    // on the server we ensure all the data is retrieved in this object
    initialState.vuefire = useSSRInitialState(
      // let `useSSRInitialState` create the initial object for us
      undefined,
      // this is necessary to work with concurrent requests
      firebaseApp,
    )
  }
}

请注意,默认情况下,vite-ssg(由 Vitesse 使用)使用 JSON.stringify() 来序列化状态,这更快,但不支持一些值,例如 TimeStampGeoPoint 对象,并且如果您的数据来自用户,还会使您的应用程序容易受到一些攻击。您可以使用自定义 transformState 函数来处理这种情况。

ts
// src/main.ts
// https://github.com/Rich-Harris/devalue#usage
import devalue from 'devalue'
import { ViteSSG } from 'vite-ssg'
import App from './App.vue'
import {
  devalueCustomParsers,
  devalueCustomStringifiers,
} from 'vuefire'

export const createApp = ViteSSG(
  App,
  { routes },
  ({ app, router, initialState }) => {
    // ...
  },
  {
    transformState(state) {
      return import.meta.env.SSR
        ? devalue.stringify(state, devalueCustomStringifiers)
        : devalue.parse(state, devalueCustomParsers)
    },
  }
)

提示

这在 Nuxt 项目中的 nuxt-vuefire 插件 中开箱即用。

网络安全是一个广泛的主题,我们无法在这里涵盖。我们建议您阅读这些资源以深入了解。

手动 SSR 密钥

VueFire 会自动根据文档或集合的路径推断 SSR 密钥,只要有可能。这意味着在某些情况下,**您必须提供手动 ssrKey**。

  • 使用 Firestore 查询时
  • 将同一个文档绑定多次时

在这些情况下,将 ssrKey 作为第二个参数提供给 useDocument()useCollection() 等。

ts
useCollection(queryRef, { ssrKey: 'my-quiz' })

在组件之外使用

如果您在组件之外使用 VueFire 可组合函数,例如在 Pinia 商店中使用 useDocument(),则需要手动等待服务器上的数据加载,因为 VueFire 无法为您调用 onServerPrefetch(),您必须手动调用它。VueFire 公开了一个函数来检索由不同可组合函数(useDocument()useDatabaseObject() 等)创建的所有挂起承诺。您需要在**使用数据的任何组件**中使用它。

vue
<script setup>
import { useQuizStore } from '~/stores/quiz'
import { usePendingPromises } from 'vuefire'

// this store internally calls `useDocument()` when created
const quizStore = useQuizStore()

// `useDocument()` has been called within `useQuizStore()` but this component isn't aware of it
onServerPrefetch(() => usePendingPromises())
</script>

虽然推荐的方法是使用 onServerPrefetch(),但另一种可能性是 使用 <Suspense> 来能够在 setup() 中使用 await

vue
<script setup>
import { useQuizStore } from '~/stores/quiz'
import { usePendingPromises } from 'vuefire'

// this store internally calls `useDocument()` when created
const quizStore = useQuizStore()

// since `useDocument()` has been called
await usePendingPromises()
</script>