服务器端渲染
已过时
本指南已过时,需要针对 Vue 3 和 vue-apollo 4 进行重写。欢迎贡献!
警告
需要 Vue 2.6+,支持 serverPrefetch
Vue CLI 插件
我为 vue-cli 创建了一个插件,因此您可以将您的 vue-apollo
应用程序在两分钟内转换为一个同构 SSR 应用程序!✨🚀
在您的 vue-cli 3 项目中
vue add @akryum/ssr
组件预取
使用以下命令安装 SSR 工具
npm install --save @vue/apollo-ssr
或者
yarn add @vue/apollo-ssr
提示
请遵循 官方 SSR 指南,以了解有关使用 Vue 进行服务器端渲染的更多信息。
默认情况下,使用 vue-server-renderer
,您在服务器端渲染的组件中的所有 GraphQL 查询将自动预取。
提示
您可以在 variables
等选项中访问 this
,即使在服务器上也是如此!
示例
export default {
apollo: {
allPosts: {
query: gql`query AllPosts {
allPosts {
id
imageUrl
description
}
}`,
}
}
}
示例 2
export default {
apollo: {
post: {
query: gql`query Post($id: ID!) {
post (id: $id) {
id
imageUrl
description
}
}`,
variables () {
return {
id: this.id,
}
},
}
}
}
跳过预取
您可以使用将 prefetch
选项设置为 false
来跳过对查询的服务器端预取。
不预取查询的示例
export default {
apollo: {
allPosts: {
query: gql`query AllPosts {
allPosts {
id
imageUrl
description
}
}`,
// Don't prefetch
prefetch: false,
}
}
}
如果您想跳过对特定组件的所有查询的预取,请使用 $prefetch
选项
export default {
apollo: {
// Don't prefetch any query
$prefetch: false,
allPosts: {
query: gql`query AllPosts {
allPosts {
id
imageUrl
description
}
}`,
}
}
}
创建 Apollo 客户端
建议在具有 ssr
参数的函数中创建 apollo 客户端,该参数在服务器上为 true
,在客户端上为 false
。
如果 ssr
为 false,我们将尝试使用 cache.restore
通过获取我们在 SSR 期间在服务器上注入到 HTML 页面中的 window.__APOLLO_STATE__
变量来恢复 Apollo 缓存的状态。
以下是一个示例
// apollo.js
import { ApolloClient, HttpLink, InMemoryCache } from '@apollo/client/core'
// Create the apollo client
export function createApolloClient (ssr = false) {
const httpLink = new HttpLink({
// You should use an absolute URL here
uri: ENDPOINT + '/graphql',
})
const cache = new InMemoryCache()
// If on the client, recover the injected state
if (!ssr) {
if (typeof window !== 'undefined') {
const state = window.__APOLLO_STATE__
if (state) {
// If you have multiple clients, use `state.<client_id>`
cache.restore(state.defaultClient)
}
}
}
const apolloClient = new ApolloClient({
link: httpLink,
cache,
...(ssr ? {
// Set this on the server to optimize queries when SSR
ssrMode: true,
} : {
// This will temporary disable query force-fetching
ssrForceFetchDelay: 100,
}),
})
return apolloClient
}
创建应用程序
我们不立即创建根 Vue 实例,而是使用一个接受 context
参数的 createApp
函数。
此函数将在客户端和服务器条目中使用,在上下文中具有不同的 ssr
值。我们在之前编写的 createApolloClient
方法中使用此值。
常见 createApp
方法的示例
// app.js
import { createApp } from 'vue'
import { createRouter, createWebHistory } from 'vue-router'
import { createStore } from 'vuex'
import VueApollo from '@vue/apollo-option'
import { createApolloClient } from './apollo'
import App from './ui/App.vue'
import routes from './routes'
import storeOptions from './store'
function createMyApp (context) {
const router = createRouter({
history: createWebHistory(),
routes,
})
const store = createStore(storeOptions)
// Vuex state restoration
if (!context.ssr && window.__INITIAL_STATE__) {
// We initialize the store state with the data injected from the server
store.replaceState(window.__INITIAL_STATE__)
}
// Apollo
const apolloClient = createApolloClient(context.ssr)
const apolloProvider = createApolloProvider({
defaultClient: apolloClient,
})
const app = createApp(App)
app.use(router)
app.use(store)
app.use(apolloProvider)
return {
app,
router,
store,
apolloProvider,
}
}
export default createMyApp
客户端条目
客户端条目非常简单 - 我们只需使用 ssr
为 false
来调用 createApp
// client-entry.js
import createApp from './app'
createApp({
ssr: false,
}).mount('#app')
服务器条目
除了将 Apollo 缓存存储起来以将其注入到客户端 HTML 中之外,不需要任何特殊操作。了解有关 使用路由的服务器条目 和 数据预取 的更多信息,请参阅官方 SSR 指南。
以下是一个使用 vue-router 和 Vuex 存储的示例
// server-entry.js
import * as ApolloSSR from '@vue/apollo-ssr'
import createApp from './app'
export default () => new Promise((resolve, reject) => {
const { app, router, store, apolloProvider } = createApp({
ssr: true,
})
// set router's location
router.push(context.url)
// wait until router has resolved possible async hooks
router.onReady(() => {
// This `rendered` hook is called when the app has finished rendering
context.rendered = () => {
// After the app is rendered, our store is now
// filled with the state from our components.
// When we attach the state to the context, and the `template` option
// is used for the renderer, the state will automatically be
// serialized and injected into the HTML as `window.__INITIAL_STATE__`.
context.state = store.state
// ALso inject the apollo cache state
context.apolloState = ApolloSSR.getStates(apolloProvider.clients)
}
resolve(app)
})
})
使用 ApolloSSR.getStates 方法获取您需要注入到生成的页面中的 JavaScript 代码,以将 apollo 缓存数据传递给客户端。
在 页面模板 中,使用 renderState
助手
{{{ renderState({ contextKey: 'apolloState', windowKey: '__APOLLO_STATE__' }) }}}
以下是一个完整的示例
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
<title>{{ title }}</title>
{{{ renderResourceHints() }}}
{{{ renderStyles() }}}
</head>
<body>
<!--vue-ssr-outlet-->
{{{ renderState() }}}
{{{ renderState({ contextKey: 'apolloState', windowKey: '__APOLLO_STATE__' }) }}}
{{{ renderScripts() }}}
</body>
</html>