NextJS — это платформа на основе ReactJS для создания веб-приложений. Это поможет вам создавать пользовательские интерфейсы с использованием компонентов React

Примечание. В этой статье рассматриваются только подходы к рендерингу NextJS. Если вы хотите ознакомиться со стандартными методологиями рендеринга, вы можете обратиться к этой статье: Стратегии рендеринга пользовательского интерфейса.

NextJS использует несколько стратегий рендеринга для создания, доставки и рендеринга приложения ReactJS (SPA).

  1. Генерация статических сайтов (SSG)
  2. Рендеринг на стороне сервера (SSR)
  3. Инкрементальная статическая регенерация (ISR)
  4. Рендеринг на стороне клиента (CSR)

Генерация статических сайтов (SSG)

При создании приложения NextJS создает все HTML-файлы вместе с CSS JS и данными, если на странице используется какой-либо метод выборки данных, например getStaticProps()или getStaticPaths(). на сервере готово. Это делается только один раз во время сборки, что делает этот подход не слишком гибким для сложных маршрутов в приложении.

// posts will be populated at build time by getStaticProps()
export default function Blog({ posts }) {
  return (
    <ul>
      {posts.map((post) => (
        <li>{post.title}</li>
      ))}
    </ul>
  );
}
 
// This function gets called at build time on server-side.
// It won't be called on client-side, so you can even do
// direct database queries.
export async function getStaticProps() {
  // Call an external API endpoint to get posts.
  // You can use any data fetching library
  const res = await fetch('https://.../posts');
  const posts = await res.json();
 
  // By returning { props: { posts } }, the Blog component
  // will receive `posts` as a prop at build time
  return {
    props: {
      posts,
    },
  };
}

Поскольку HTML-код страницы был предварительно обработан на стороне сервера, клиентский браузер должен загрузить и отобразить страницу. И, поскольку страница закодирована таким образом, что позволяет браузеру отображать ее с минимальным кодом JavaScript, время блокировки рендеринга сокращается до минимума, что приводит к хорошим результатам производительности. Таким образом, First Paintful Paint (FP), First Contentful Paint (FCP), Самая большая Contentful Paint (LCP), и Время до первого байта (TTFB) быстрее на этом этапе.

(re)Hydrate: несмотря на то, что страница была предварительно создана, этот дополнительный шаг необходим, чтобы позволить React прикрепить события JS к своей виртуальной DOM, чтобы сделать страницу интерактивной. И в этот момент JS был выполнен, что повлияло на общее время блокировки (TBT) на этот дополнительный шаг. Кроме того, браузер должен дождаться завершения процесса гидратации, увеличивая время до взаимодействия (TTI).

Рендеринг на стороне сервера (SSR)

В этой стратегии NextJS генерирует статический контент, который создается (предварительно обрабатывается) и распространяется только по запросу. Из-за дополнительного времени, необходимого во время сборки для каждого запроса, продолжительность Время до первого байта (TTFB) увеличивается, что приводит к плохим результатам. Таким образом, рекомендуется избегать этого подхода, когда ваша страница более динамична.

// posts will be populated at every http request by getServerSideProps()
export default function Blog({ posts }) {
  return (
    <ul>
      {posts.map((post) => (
        <li>{post.title}</li>
      ))}
    </ul>
  );
}
 
// This gets called on every request on SERVER SIDE
// It won't be called on client-side, so you can even do
// direct database queries.
export async function getServerSideProps() {
  // Call an external API endpoint to get posts.
  // You can use any data fetching library
  const res = await fetch(`https://.../posts`);
  const data = await res.json();


  // By returning { props: { posts } }, the Blog component
  // will receive `posts` as a prop
  return { 
    props: { 
      posts 
    } 
  };
}

Поскольку страницы генерируются на стороне сервера, теперь браузеры получают страницы с минимальным количеством CSS, JS и данных, если страница использует метод выборки данных getServerSideProps(). Таким образом, браузер просто отображает страницу, что ускоряет FP, FCP и LCP при таком подходе.

Инкрементальная статическая регенерация (ISR)

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

// posts will be populated at build time by getStaticProps()
function Blog({ posts }) {
  return (
    <ul>
      {posts.map((post) => (
        <li key={post.id}>{post.title}</li>
      ))}
    </ul>
  );
}
 
// This function gets called at build time on server-side.
// It may be called again, on a serverless function, if
// revalidation is enabled and a new request comes in
export async function getStaticProps() {
  const res = await fetch('https://.../posts');
  const posts = await res.json();
 
  return {
    props: {
      posts,
    },
    // Next.js will attempt to re-generate the page:
    // - When a request comes in
    // - At most once every 10 seconds
    revalidate: 10, // In seconds
  };
}
 
// This function gets called at build time on server-side.
// It may be called again, on a serverless function, if
// the path has not been generated.
export async function getStaticPaths() {
  const res = await fetch('https://.../posts');
  const posts = await res.json();
 
  // Get the paths we want to pre-render based on posts
  const paths = posts.map((post) => ({
    params: { id: post.id },
  }));
 
  // We'll pre-render only these paths at build time.
  return { paths };
}
 
export default Blog;
  • Любые запросы к странице после первоначального запроса и до 10 секунд также кэшируются и выполняются мгновенно.
  • После 10-секундного окна следующий запрос по-прежнему будет показывать кешированную (устаревшую) страницу.
  • Next.js запускает регенерацию страницы в фоновом режиме.
  • После успешного создания страницы NextJS аннулирует кеш и покажет обновленную страницу. Если фоновая регенерация не удалась, старая страница останется неизменной.
  • Чтобы решить проблему кэширования страниц, они ввели: Повторная проверка по требованию.

Повторная проверка по требованию с проверкой пользователя

// handler function for serving HTTP requests for revalidation of a route
export default async function handler(req, res) {
  // Check for secret to confirm this is a valid request
  // to make sure valid user ca trigger regeneration of page
  if (req.query.secret !== process.env.MY_SECRET_TOKEN) {
    return res.status(401).json({ message: 'Invalid token' });
  }
 
  try {
    // this should be the actual path not a rewritten path
    // e.g. for "/blog/[slug]" this should be "/blog/post-1"
    await res.revalidate('/path-to-revalidate');
    return res.json({ revalidated: true });
  } catch (err) {
    // If there was an error, Next.js will continue
    // to show the last successfully generated page
    return res.status(500).send('Error revalidating');
  }
}

Рендеринг на стороне клиента

Рендеринг на стороне клиента (CSR) аналогичен созданию статического сайта, за исключением того, что части содержимого могут быть созданы на стороне клиента. Мы использовали этот подход в основном с фреймворками FE (например, Angular, ReactJS, Vue и т. д.).

// Basic ReactJS component loads in client side(browser)
// afte JS initialization and hydration
import { useState, useEffect } from 'react';

function Profile() {
  const [data, setData] = useState(null);
  const [isLoading, setLoading] = useState(false);

  useEffect(() => {
    setLoading(true);
    // This API call will haapen at client side
    // after loading on the reactJS
    fetch('/api/profile-data')
      .then((res) => res.json())
      .then((data) => {
        setData(data);
        setLoading(false);
      });
  }, []);

  if (isLoading) return <p>Loading...</p>;
  if (!data) return <p>No profile data</p>;

  return (
    <div>
      <h1>{data.name}</h1>
      <p>{data.bio}</p>
    </div>
  );
}

CSR требуется, когда содержимое ваших страниц необходимо часто обновлять. В отличие от SSR API, вы можете использовать выборку данных на стороне клиента на уровне компонентов.

Если CSR выполняется на уровне страницы, данные извлекаются во время выполнения, и страница обновляется по мере изменения данных. При использовании на уровне компонента данные извлекаются во время монтирования компонента, а содержимое компонента обновляется по мере изменения данных.

Важно отметить, что использование выборки данных на стороне клиента может повлиять на производительность вашего приложения и скорость загрузки ваших страниц. Это связано с тем, что выборка данных выполняется во время монтирования компонента или страниц, и это делает TBT и TTI более длительными, чем другие подходы, из-за которых страницы не отвечают или отображаются индикаторы загрузки в течение значительного времени.

Использованная литература: