Next.js: Уменьшите выборку данных и обменивайтесь данными между страницами

Я ищу решения для улучшения получения данных в приложении Next.js. В этом вопросе я не просто ищу в решение, я ищу несколько вариантов, чтобы мы могли посмотреть на плюсах и минусах.

Проблема у меня

Прямо сейчас у меня есть несколько страниц, которые включают компонент, отображающий статический контент, и некоторые страницы с динамическим контентом, получаемым из API. Каждая страница делаетfetch() в их getInitialProps()чтобы получить данные своей страницы, а также данные нижнего колонтитула, которые одинаковы для всех страниц.

Это, конечно, работает, но есть много дублированных данных. Данные нижнего колонтитула всегда будут отображаться для всех страниц и всегда будут одинаковыми. Кроме того, это будет редко изменяться в API, поэтому нет необходимости повторно проверять данные.

Ответы, которые я ищу

Я не просто пытаюсь решить эту проблему, я ищу обзор, чтобы научиться чему-то новому для будущих проектов. Мне нравится писать "очевидный" код, поэтому я не ищу слишком хакерских решений, например, писать вwindowобъект и т. д. Предпочтительны простые решения с меньшей зависимостью. Цель - быстрый сайт. Не так важно уменьшить использование сети / количество вызовов API.

Что я думал до сих пор

Это возможные решения, которые я придумал, в некоторой степени отсортированные от простых / очевидных до более сложных.

  1. Сделайте выборку внутри компонента нижнего колонтитула (на стороне клиента)
  2. Сделайте выборку в getInitialProps (на стороне сервера и на стороне клиента) на всех / страницах
  3. Сделайте выборку в _app.js с помощью HOC и подключитесь к нему getInitialProps() и добавьте его в props, чтобы данные были доступны для всех страниц
  4. Используйте zeit/swr и предварительную выборку данных для кэширования данных
  5. Используйте redux для хранения глобального состояния

Все это "работает", но большинство из них будет повторно извлекать данные без необходимости и / или немного усложняет их. Вот плюсы и минусы, как я вижу (цифры такие же, как указано выше):

  1. Просто! Код получения находится только в одном месте, он находится там, где он используется. Данные извлекаются после загрузки страницы, поэтому содержимое "перескакивает" на просмотр. Данные постоянно обновляются.
  2. Просто! Данные извлекаются на сервере, поэтому контент доступен до того, как страница будет отображена. Данные обновляются для каждой страницы. Мы должны не забывать получать одни и те же данные нижнего колонтитула для каждой страницы в ихgetInitialProps().
  3. Мы можем выполнить выборку в одном месте и добавить ее ко всем реквизитам страниц, поэтому данные нижнего колонтитула будут автоматически доступны для всех реквизитов страниц. Некоторым может быть немного сложнее легко понять, что происходит, поскольку для этого требуется немного больше понимания того, как работает Next.js/React. По-прежнему обновляет данные для всех страниц. Теперь делаем дваfetch() вызовы друг за другом (сначала в _app.js для загрузки содержимого нижнего колонтитула, затем на каждой странице для получения настраиваемого содержимого), так что это еще медленнее.
  4. Довольно просто. Мы можем использовать предварительную выборку для загрузки данных в кеш даже до загрузки JS. После загрузки первой страницы у нас будет быстрая выборка данных. Можно получить код прямо в компоненте нижнего колонтитула. Вrel="preload"Техника предварительной выборки не будет работать со всеми типами выборки (например, клиент Sanity, использующий groq). Чтобы не было "скачкообразного" контента, в котором данные загружаются после начальной загрузки страницы, мы должны предоставитьuseSWR() с initialData что по-прежнему потребует от нас получения данных в getInitialProps(), но достаточно просто сделать это на стороне сервера. Мог бы использовать новыйgetServerSideProps().
  5. Мы можем загрузить данные один раз (?) И сделать их доступными для всего приложения. Быстро и меньше / без повторной загрузки. Добавляет внешнюю зависимость. Более сложный, поскольку вам придется изучить redux, даже чтобы просто загрузить один общий объект данных.

Текущее решение, использующее решение, описанное в пункте № 2.

const HomePage = (props) => {
  return (
    <Layout data={props.footer}>
       <Home data={props.page} />
    </Layout>
  )
}

// Not actual query, just sample
const query = `{
  "page": *[_type == "page"][0], 
  "footer": *[_type == "footer"][0]
}`

HomePage.getInitialProps = async () => {
  const data = await client.fetch(query)

  return {
    page: data.page
    footer: data.footer
  }
}

export default HomePage

Хотелось бы получить больше информации об этом. Я упускаю что-то очевидное?

3 ответа

Хорошо! Я нашел эту ветку, когда искал что-то еще. Но поскольку мне приходилось работать над аналогичными проблемами, я могу дать вам несколько указаний, и я сделаю все возможное, чтобы прояснить их для вас.

Итак, есть некоторые данные, которыми вы хотите поделиться в своем приложении (страницы / компоненты).

  1. Next.js использует компонент App для инициализации страниц. Вы можете переопределить его и контролировать инициализацию страницы. для этого просто создайте_app.js файл в корне pagesкаталог. Для получения дополнительной информации перейдите по этой ссылке: https://nextjs.org/docs/advanced-features/custom-app.
  2. Так же, как вы можете использовать getInitialProps на своих страницах для получения данных из вашего API, вы также можете использовать тот же метод в _app.js. Итак, я бы получил те данные, которые мне нужно передать в моем приложении, и исключил бы свои вызовы API.

Что ж, теперь я могу придумать два способа поделиться данными в моем приложении.

  1. Использование createContext крючки.

1.1. Создайте DataContext с помощью хуков createContext. и завернуть<Component {...pageProps} /> с твоим <DataContext.Provider>. Вот фрагмент кода, который поможет вам лучше понять:

<DataContext.Provider value={{ userData, footerData, etc... }}>
  <Component {...pageProps} />
</DataContext.Provider>

1.2. Теперь на других страницах / компонентах вы можете получить доступ к своему DataContext следующим образом:

const { footerData } = useContext(DataContext); 

И тогда вы можете выполнять манипуляции в своем интерфейсе.

  1. населяет props с помощью getInitialProps

2.1. getInitialProps используется для асинхронной выборки данных, которые затем заполняют props. то же самое было бы в_app.js.

Код в вашем _app.js будет примерно так:

function MyApp({ Component, pageProps, footerData }) {
  //do other stuffs
  return (
   <Component {...pageProps} footerData={footerData} />
  ;
}

MyApp.getInitialProps = async ({ Component, ctx }) => {
  const footerRes = await fetch('http://API_URL');
  const footerData = await footerRes.json(); 
  let pageProps = {};
  if (Component.getInitialProps) {
    pageProps = await Component.getInitialProps(ctx);
  }
  return { pageProps, footerData };
};

2.2. Теперь на ваших страницах (не в ваших компонентах) вы можете получить доступ к реквизитам, включая те, которыми вы поделились с_app.jsи можно начинать манипулировать вами.

Надеюсь, я смогу дать вам подсказку и направление. Получайте удовольствие от изучения.

Я тоже искал решение для этого. У нас есть миллионы страниц, и для каждой страницы главное меню должно быть получено из некоторого api, то же самое касается других более простых меню и информации верхнего и нижнего колонтитула.

Я думал об использовании Varnish с помощью Esi's. Это просто концепция в моей голове, еще не реализованная и не протестированная. Но при использовании лака отдельные компоненты могут быть «частичными» страницами. Как и главное меню, это часть страницы.

Я не уверен, что это сработает, потому что NextJs обертывает стандартные теги html, head, body, вокруг страницы вашего приложения. Но попробовать все же стоит.

Я искал то же самое несколько недель назад. Если у вас есть какой-то компонент, который оборачивает все страницы, или есть компонент, который отображается на всех страницах, то вот видео, которое описывает то же самое.

Вот пример из моего собственного проекта.

_app.ts

      import "../styles/global.css";
import "@fontsource/rubik";

import type { AppProps } from "next/app";
import { ThemeProvider } from "next-themes";

type ComponentWithPageLayout = AppProps & {
  Component: AppProps["Component"] & {
    PageLayout?: React.ComponentType<any>;
  };
};

const MyApp = ({ Component, pageProps }: ComponentWithPageLayout) => {
  return (
    <>
      <ThemeProvider attribute="class">
        {Component.PageLayout ? (
          <Component.PageLayout>
            <Component {...pageProps} />
          </Component.PageLayout>
        ) : (
          <Component {...pageProps} />
        )}
      </ThemeProvider>
    </>
  );
};

export default MyApp;

LayoutWithoutSidebar

      import Navbar from "src/components/Navbar";

const LayoutWithoutSidebar = ({ children }: { children: React.ReactNode }) => {
  return (
    <>
      <header className="sticky top-0 z-50 ">
        <Navbar />
      </header>
      <main className="max-w-7xl m-auto">{children}</main>
    </>
  );
};

export default LayoutWithoutSidebar;

index.ts

      import { GetStaticProps, GetStaticPropsContext, PreviewData } from "next/types";
import { ParsedUrlQuery } from "querystring";

import ApiService_ from "src/services/ApiService";

import PageHead from "src/components/PageHead";
import { Article, Project } from "src/@types/schema";

import LayoutWithoutSidebar from "src/layouts/LayoutWithoutSidebar";
import SectionSeperator from "src/components/SectionSeperator";

import IntroSection from "src/components/section/IntroSection";
import LatestSection from "src/components/section/LatestSection";


const Home = ({ latest, projects }: { latest: Article[]; projects: Project[] }) => {
  return (
    <div className="flex flex-col">
      <PageHead title="NextJS Blog" desc="Generated by create next app" />
      <IntroSection />
      <SectionSeperator />
      <LatestSection latest={latest} />
    </div>
  );
};

Home.PageLayout = LayoutWithoutSidebar; 

export default Home;

export const getStaticProps: GetStaticProps = async (
  context: GetStaticPropsContext<ParsedUrlQuery, PreviewData>
) => {
  const _service = new ApiService_();

  const latest: Article[] = await _service.getPost({
    maximum: 3,
    type: "Update",
  });

  const projects: Project[] = await _service.getProjects({});

  return {
    props: {
      latest,
      projects,
    },
  };
};

** Создайте макет, например, с помощью Navbar, {children} (это содержимое страницы), нижнего колонтитула **

      Home.PageLayout = LayoutWithoutSidebar; 

вот видео откуда я узнал. оформить заказ для получения дополнительной информации узнать подробное объяснение Как добавить вложенные/постоянные макеты в Next.js

Другие вопросы по тегам