DevGang
Авторизоваться

Поднимите настройку Next js + GraphQL + TypeScript на новый уровень

В этом посте мы покажем вам, как воспользоваться Next.js возможности в сочетании с Apollo и GraphQL. Наше внимание будет сосредоточено на интеграции Apollo Client с некоторыми Next.js наиболее важные функции, включая рендеринг на стороне сервера (SSR), статическую генерацию сайта (SSG) и инкрементную статическую регенерацию (ISR). Однако, прежде чем мы углубимся в специфику, давайте кратко рассмотрим, что включает в себя каждая из этих функций.

Метод SSR

Рендеринг на стороне сервера (SSR) - это метод, который позволяет вам предварительно отрисовывать страницы вашего веб-сайта на сервере и предоставлять их клиенту в виде HTML-файлов. С SSR начальная загрузка страницы происходит быстрее, потому что сервер отправляет HTML-файл с отображаемым содержимым, а не отправляет пустую страницу, которую необходимо заполнить содержимым после загрузки JavaScript.

В дополнение к увеличению времени начальной загрузки страницы SSR также может улучшить SEO, поскольку поисковые системы могут легко сканировать и индексировать ваши предварительно отрисованные страницы. SSR также может помочь оптимизировать производительность для пользователей с медленным подключением к Интернету или менее мощными устройствами.

SSR может быть реализован многими способами, но Next.js предоставляет встроенный API, который упрощает процесс. С помощью Next.js , вы можете легко включить SSR для ваших компонентов React, что позволит вам воспользоваться преимуществами SSR без необходимости писать много серверного кода.

Метод SSG и функция ISR

Статическая генерация сайтов (SSG) - это метод создания веб-сайтов, который предварительно отображает все страницы веб-сайта во время сборки и предоставляет эти предварительно отрисованные страницы пользователям, в отличие от генерации страниц по запросу. Хотя SSG - это быстрый и масштабируемый подход к созданию веб-сайтов, у него есть ограничение, заключающееся в том, что каждый раз, когда вы хотите обновить какой-либо контент, вам необходимо перестраивать весь сайт. Инкрементная статическая регенерация (ISR) является особенностью Next.js это устраняет это ограничение, позволяя вам обновлять определенные страницы вашего веб-сайта без перестройки всего сайта.

Важно отметить, что SSG и ISR предлагают преимущества SSR, а также являются более производительными.

Кэш Apollo и SSR/SSG/ISR

Apollo Client предоставляет мощную систему кэширования, которая позволяет вам управлять состоянием вашего приложения и быстро получать доступ к данным без необходимости делать сетевой запрос. Когда вы используете Apollo Client для выполнения запроса GraphQL, результаты автоматически сохраняются в кэше, что значительно ускоряет последующие запросы к тем же данным.

Для достижения SSR/SSG/IS в Next.js приложение, вы можете сделать сетевой запрос на сервере с помощью Apollo Client и предварительно заполнить кэш данными. Это позволяет данным быть немедленно доступными на стороне клиента, без необходимости дополнительных сетевых запросов.

Используя кэш в сочетании с SSR/SSG/ISR, вы можете значительно повысить производительность и удобство использования вашего приложения, гарантируя, что данные всегда доступны быстро и надежно.

Вот небольшая диаграмма.

Настройка приложения

Для начала мы собираемся использовать этот шаблон, который уже имеет настройки TypeScript и Apollo в Next js.

Давай приступим к работе.

1. Установите следующие зависимости.

npm install lodash-es deepmerge

Позже эти библиотеки понадобятся нам для изменения кэша Apollo.

2. Установите типы для lodash-es

npm install --save-dev @types/lodash-es

3. Создайте папку под названием apollo с файлом index.ts внутри.

4. Внутри этого файла создайте функцию, которая инициализирует клиент apollo.

import { ApolloClient, InMemoryCache } from '@apollo/client';

const COUNTRIES_API = 'https://countries.trevorblades.com';

function createApolloClient() {
  return new ApolloClient({
    ssrMode: typeof window === 'undefined',
    uri: COUNTRIES_API,
    cache: new InMemoryCache(),
  });
}

Это должно выглядеть знакомо, теперь это просто абстрагировано в функцию. Здесь мы создаем экземпляр клиента Apollo, свойство ssr здесь очень важно, оно определяет, используется ли клиент в режиме рендеринга на стороне сервера (true) или в режиме рендеринга на стороне клиента (false). Все это основано на том, определено window или нет.

5. Внутри того же файла создайте функцию с именем initializeApollo со следующим содержимым.

import merge from 'deepmerge';
import isEqual from 'lodash-es/isEqual';
import { ApolloClient, InMemoryCache } from '@apollo/client';

let apolloClient: ApolloClient<NormalizedCacheObject> | null;

function createApolloClient() {
//...
}

export function initializeApollo(initialState?: any) {
  const _apolloClient = apolloClient ?? createApolloClient();

  if (initialState) {
    const existingCache = _apolloClient.cache.extract();

    const data = merge(initialState, existingCache, {
      arrayMerge: (destinationArray, sourceArray) => [
        ...sourceArray,
        ...destinationArray.filter((d) =>
          sourceArray.every((s) => !isEqual(d, s))
        ),
      ],
    });
    _apolloClient.cache.restore(data);
  }

  if (typeof window === 'undefined') {
    return _apolloClient;
  }

  if (!apolloClient) {
    apolloClient = _apolloClient;
  }

  return _apolloClient;
}

Эта функция инициализирует клиент Apollo. Он создает новую переменную с именем _apolloClient с существующим apolloClient, если он существует, или с новым экземпляром createApolloClient(), если он не существует. Это гарантирует наличие только одного экземпляра клиента во всем приложении. Если есть начальное состояние, он извлекает существующий кеш из _apolloClient, объединяет его с initialState с помощью функции merge(), а затем восстанавливает объединенные данные в кеш. Это позволяет использовать начальное состояние в любых последующих запросах.

6. Последней функцией в этом файле будет addApolloState.

import merge from 'deepmerge';
import isEqual from 'lodash/isEqual';
import { ApolloClient, InMemoryCache } from '@apollo/client';

let apolloClient: ApolloClient<NormalizedCacheObject> | null;

export const APOLLO_STATE_PROP_NAME = '__APOLLO_STATE__';

function createApolloClient() {
  // ...
}

export function initializeApollo(initialState?: any) {
  // ...
}

export function addApolloState(
  client: ApolloClient<NormalizedCacheObject>,
  pageProps: any
) {
  if (pageProps?.props) {
    pageProps.props[APOLLO_STATE_PROP_NAME] = client.cache.extract();
  }

  return pageProps;
}

Эта служебная функция, называемая addApolloState, принимает два параметра: экземпляр клиента Apollo и объект, содержащий реквизиты, передаваемые на страницу Next.js с именем pageProps.

Затем функция проверяет, есть ли у объекта pageProps свойство, называемое props. Если это так, он добавляет состояние кэша клиента Apollo в объект pageProps.props, используя идентификатор APOLLO_STATE_PROP_NAME.

Наконец, функция возвращает объект pageProps с добавленным к нему состоянием кэша клиента Apollo. Эта функция будет использоваться в функции getStaticProps/getServerSideProps страницы Next.js для внедрения состояния кэша клиента Apollo.

7. Теперь давайте создадим хук, который поможет нам получить клиент Apollo и передать его нашему приложению. Создайте папку с именем hooks с файлом с именем useApollo.ts.

import { useMemo } from 'react';

import { APOLLO_STATE_PROP_NAME, initializeApollo } from '../apollo';

function useApollo(pageProps: any) {
  const state = pageProps[APOLLO_STATE_PROP_NAME];
  const client = useMemo(() => initializeApollo(state), [state]);

  return client;
}

export default useApollo;

Хук сначала извлекает состояние кэша клиента Apollo из pageProps, используя идентификатор APOLLO_STATE_PROP_NAME. Затем он вызывает функцию initializeApollo, которую мы создали ранее, чтобы получить либо существующий, либо новый экземпляр клиента Apollo.

8. Теперь мы, наконец, готовы подключить полностью предварительно заполненный клиент к нашему приложению. Внутри _app.tsx добавьте следующий код.

import type { AppProps } from 'next/app';
import { ApolloProvider } from '@apollo/client';

import useApollo from '../hooks/useApollo';

import '../styles/globals.css';

function MyApp({ Component, pageProps }: AppProps) {
  const client = useApollo(pageProps);
  return (
    <ApolloProvider client={client}>
      <Component {...pageProps} />
    </ApolloProvider>
  );
}

export default MyApp;

Теперь вместо того, чтобы создавать здесь экземпляр клиента, мы используем хуки useApollo.ts, чтобы получить клиента и передать его в ApolloProvider.

С этим изменением наш Next.js приложение теперь полностью подключено к нашему экземпляру клиента Apollo и может использовать запросы GraphQL для извлечения и отображения данных.

Тестирование

Теперь давайте заполним запрос стран через getStaticProps.

import Head from 'next/head';
import { useQuery } from '@apollo/client';
import type { GetStaticProps } from 'next';

import QUERY_COUNTRIES from './queryCountries.graphql';

import { addApolloState, initializeApollo } from '../apollo';

import styles from '../styles/Home.module.css';

export default function Home() {
  // ...
}

export const getStaticProps: GetStaticProps = async (ctx) => {

    const client = initializeApollo();

    await client.query({
      query: QUERY_COUNTRIES
    });

    return addApolloState(client, {
      props: {},
    });
};

Итак, здесь мы инициализируем клиента, делаем запрос и передаем клиент с заполненным кешем функции addApolloState вместе с реквизитами страницы. Мы знаем, что эта функция добавит кеш apollo в pageProps. Это гарантирует, что данные предварительно заполняются в кэше при отображении страницы на стороне клиента.

Теперь, если вы проверите вкладку сети, вы не увидите никакой загрузки и запросов, сделанных хуком useQuery на клиенте, так как данные уже доступны в кеше.

В этом примере мы используем функцию getStaticProps для создания статической страницы, которая включает функции SSG/ISR. Этот подход также можно использовать в функции getServerSideProps, чтобы предоставить нам возможности SSR.

Теперь мы интегрировали возможности Next.js с Apollo Client, чтобы вывести наше приложение на новый уровень.

Вы можете найти полный код здесь

#React #GraphQL #Apollo
Комментарии
Чтобы оставить комментарий, необходимо авторизоваться

Присоединяйся в тусовку

В этом месте могла бы быть ваша реклама

Разместить рекламу