作为道具传递与提取缓存 Apollo 客户端 Nextjs

R. *_*lie 6 typescript server-side-rendering next.js apollo-client

我在apollo-client repo 中发布了这个,但我想我也会问 stackoverflow

你好!我是阿波罗(以及整个 graphql)的新手,并且对 SSR / SSG 有一些疑问。我从概念上知道 SSR/SSG 是什么以及它是如何工作的,但对 apollo-client 不太了解。

我已经尝试在网上搜索和搜索正确执行此操作的方法,并且看到了两个版本而几乎没有解释原因,所以我这篇文章的目标是有一个指向和去的地方“这就是为什么你做一个其他”。

做之间有什么好处/坏处

这是 TypeScript 和伪代码的混合体,请不要批评语法 kthx

// apolloClient.ts
const client = new ApolloClient({
  link: new HttpLink({ uri: '/graphql' }),
  cache: new InMemoryCache(),
});
Run Code Online (Sandbox Code Playgroud)
// component.tsx
import client from '../apolloClient';

const Component: FunctionalComponent = ({ data }) => {
   ...
}

export const getServerSideProps: GetServerSideProps = async () => {
  const { data } = client.query(...);

  return {
    props: { data }
  }
}
Run Code Online (Sandbox Code Playgroud)
// app.tsx
import client from './client';

const MyApp: NextComponentType<AppContext, AppInitialProps, AppProps> = ({ Component, pageProps }) => (
  <ApolloProvider client={client}>
    <Component {...pageProps} />
  </ApolloProvider>
);
Run Code Online (Sandbox Code Playgroud)

VS

// apolloClient.ts
const createClient = () => new ApolloClient({
  link: new HttpLink({ uri: '/graphql' }),
  cache: new InMemoryCache(),
  ssrMode: typeof window === 'undefined',
});

let client: ApolloClient<NormalizedCacheObject>;
const initalizeApollo = (initalState?: NormalizedCacheObject) => {
  const apolloClient = client ?? createClient();

  if (initalState) {
    apolloClient.cache.restore({
      ...apolloClient.cache.extract(),
      ...initalState,
    });
  }

  if (typeof window === 'undefined') return apolloClient;

  client ??= apolloClient;

  return client;
}

const useApollo = (initalState?: NormalizedCacheObject) => useMemo(() => initalizeApollo(initalState), [initalState]);
Run Code Online (Sandbox Code Playgroud)
// component.tsx
import { useQuery } from 'apollo-client';
import useApollo from '../apolloClient';

const Component = () => {
   const { data } = useQuery(...);
}

export const getServerSideProps: GetServerSideProps = async () => {
  const client = useApollo();

  await client.query(...);

  return {
    props: { initalApolloState: client.cache.extract() }
  }
}
Run Code Online (Sandbox Code Playgroud)
// app.tsx
const MyApp: NextComponentType<AppContext, AppInitialProps, AppProps> = ({ Component, pageProps }) => {
  const client = useApollo(pageProps.initalApolloState);

  return (
    <ApolloProvider client={client}>
      <Component {...pageProps} />
    </ApolloProvider>
  )
};
Run Code Online (Sandbox Code Playgroud)

这是本次讨论中我自己的“我不明白”部分

对我来说,似乎是第二种方式(使用 SSR?),您必须运行查询两次并编写大量额外代码才能获得相同的效果?这两种方法的性能/安全性/任何好处是什么。

谢谢!

ser*_*ays 1

您的第一种方法更符合 Next 尝试做的事情。getStaticProps它希望在构建时通过(SSG) 或getServerSideProps(SSR)提取页面数据。这个想法是,只要有可能,它就会在服务器上静态生成页面。

Apollo Client 不需要在getStaticProps/getServerSideProps调用中引用。我认为你可以不用使用 Context,甚至不需要 Apollo Client 按照你的代码编写方式。

我在 Next 应用程序中使用 Apollo 的地方是当我需要将数据拉入组件时。Next 并没有真正解决这个问题,但这正是 Apollo 做得很好的地方。一个好的用例是每个页面上都出现的菜单。如果您更改菜单项,您不希望触发站点范围的静态重新生成。Apollo 将使所有页面保持新鲜,并且不会触发 Next 再次渲染。

概述了我在这个答案中所做的事情。