Как использовать пакеты только на стороне клиента с SSR в Gatsby и Next.js

Итак, вы начали проект в Gatsby или Next.js, чтобы воспользоваться преимуществами статической генерации сайта или рендеринга на стороне сервера и его преимуществами в производительности, потому что так вам посоветовал Youtube/StackOverflow/какой-то курс. Вы счастливо кодите, пока не появляется эта ошибка:

ERROR #95312 
"window" is not available during server side rendering.
Войдите в полноэкранный режим Выйти из полноэкранного режима

Возможно, вы — героический старший разработчик, который просто понимает эту ошибку, исправляет ее за 2 минуты и продолжает свою работу. (Зачем вы вообще это читаете?) Если это не так, то вот приземленное объяснение и несколько идей, как решить эту проблему.

Если вам просто нужно решение: вот репозиторий с решением для Gatsby, а вот то, что вам нужно для Next.js.

Сначала основы: Понимание SSR в сравнении с CSR

У меня было много проблем с пониманием того, в чем именно заключается разница между этими двумя понятиями, поэтому я надеюсь просветить вас с помощью аналогии:
Вспомните, когда вы в последний раз ходили в IKEA: вы выбрали красивую книжную полку для своего дома, поссорились из-за нее со своим партнером, получили тяжелую коробку со склада IKEA, помирились со своим партнером за хот-догом, а затем пошли домой и собрали свою покупку (не оскорбляя инструкцию, конечно).

Вот как работает рендеринг на стороне клиента: Ваш браузер запрашивает страницу, получает хороший блок кода, достает свои инструменты (Javascript, в данном случае) и собирает страницу. В этой аналогии Ikea — это сервер, а ваш дом — клиент. Поскольку вы собираете свою книжную полку на стороне клиента, вам потребуется немного больше времени и, возможно, звонок в службу поддержки, пока вы не сможете насладиться своей новой мебелью, но для Ikea это было удобно — предоставить вам один из своих продуктов.

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

Вот как работает рендеринг на стороне сервера: сервер, с которого мы запросили страницу, выполняет весь код на сервере node: вашему браузеру остается только раскрасить его, никакой javascript не нужен. Именно поэтому вы можете отключить javascript в инструментах разработчика вашего браузера, и страницы с серверным рендерингом все равно будут отображаться безупречно. Это более дорогой вариант для IKEA (и владельца веб-страницы), поскольку им нужны ресурсы для сборки и доставки вашего запроса, но пользовательский опыт лучше, если только вам действительно нравятся эти хот-доги.

 

Можно ли смешивать рендеринг на стороне сервера и на стороне клиента?

Может быть, IKEA доставит вам книжную полку в собранном виде, но вы поймете, что расстояние между полками не подходит. Вам придется достать отвертку и внести некоторые коррективы, ничего страшного. Если нечто подобное происходит на веб-странице, например, браузер условно отображает модальное окно на основе предпочтений, установленных в локальном хранилище: это все еще серверный рендеринг? Или теперь это считается рендерингом на стороне клиента?

Отчасти и то, и другое, верно?

Вы можете добавлять клиентские части к страницам, отрисованным на стороне сервера. Может быть, вы нажали «Не показывать мне этот диалог каждый раз», и страница корректируется на стороне клиента на основе данных, хранящихся в браузере. Или страница реализует вызов API для поиска целевых продуктов на основе ваших локально сохраненных предпочтений.

 

Понимание ошибки

Ошибка, которую мы видим здесь, вызвана следующим: Часть нашего кода считается рендерингом на стороне клиента и предполагает наличие определенных глобальных значений, таких как window или document, поскольку код предназначен для выполнения в браузере. Это может быть вызвано нашим кодом или сторонними библиотеками.

Но если код рендерится на стороне сервера, он не выполняется браузером, а выполняется сервером узла, и такие глобальные значения, как window и document, недоступны: отсюда и ошибка, которую мы видим.

Решение

1. Используйте хук useEffect или просто запросите window.

Это самое простое решение: если вам просто нужно получить доступ к window для прокрутки или рендеринга какого-то специального компонента только на стороне клиента, вы можете сделать это следующим образом:

import React, { useEffect } from "react";

export default function TestComponent(props) {
  console.log("I get executed in the browser and the client");

  const isSSR = typeof window === "undefined";
  console.log(isSSR);

  useEffect(() => {
    console.log("I am only being executed in the browser");
  }, []);

  return (
    <div>
      I am a test component!
      {!isSSR && <p>This is only rendered on the client side!</p>}
    </div>
  );
}
Войти в полноэкранный режим Выйти из полноэкранного режима

2. @loadable/component в Gatsby

Библиотека @loadable/component позволяет вам динамически импортировать компоненты в ваш проект, чтобы они не отрисовывались на сервере. Следующий компонент использует leaflet, библиотеку, подобную google maps, которая поддерживает рендеринг только на стороне клиента:

import React from 'react';
import { MapContainer, Marker, Popup, TileLayer } from 'react-leaflet';
import L from 'leaflet';
import 'leaflet/dist/leaflet.css';

export default function Map(props) {
    const defaultPosition = [-31.41528, -64.18156];
    const myIcon = new L.Icon({
      iconUrl: '/marker-icon.png',
    });
    return (
      <div className="map__container">
        <MapContainer
          center={defaultPosition}
          zoom={16}
          style={{ height: 400, width: '100%' }}
        >
          <TileLayer
            attribution='&copy; <a href="http://osm.org/copyright">OpenStreetMap</a> contributors'
            url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
          />
          <Marker position={defaultPosition} icon={myIcon}>
            <Popup>
              Entrá en la galería! <br /> Estamos en frente de Nunatak. <br /> Te
              esperamos!
            </Popup>
          </Marker>
        </MapContainer>
      </div>
    );
  }
Вход в полноэкранный режим Выход из полноэкранного режима

Чтобы иметь возможность использовать этот компонент в Gatsby, я использовал @loadable/component следующим образом:

import loadable from '@loadable/component';
import React from 'react';
// import Map from './Map'; // uncomment this line to see the Gatsby build error

export default function MapWrapper() {
    const Map = loadable(() => import("./Map")) // comment this line to see the Gatsby build error
    return <div>
        <Map />
    </div>
}
Войти в полноэкранный режим Выйти из полноэкранного режима

Продолжайте, клонируйте проект и поиграйте с различными типами импорта. Затем попробуйте запустить: npm run build, чтобы вы могли увидеть результат в своем терминале.

Gatsby также упоминает некоторые альтернативные решения в документации.

3. Динамическая загрузка в Next.js

Next.js имеет свой собственный механизм динамического импорта компонентов только на стороне клиента: ознакомьтесь с документацией. Вот как я добавил карту листания в Next.js:

import React, { useState } from "react";
import dynamic from "next/dynamic";
import styles from "../styles/Contacto.module.css";

export function FindMe(props) {
  const Map = dynamic(
    () => import("../components/Map"), // replace '@components/map' with your component's location
    { ssr: false } // This line is important. It's what prevents server-side render
  );

  return (
        <div className={styles.map}>
          <Map />
        </div>
  );
}
Вход в полноэкранный режим Выход из полноэкранного режима

Компонент <Map /> точно такой же, как и в проекте Gatsby. Чтобы попробовать это, просто запустите проект Next.js и импортируйте компонент Map вместе с его оберткой в сгенерированную на стороне сервера страницу.

Как всегда, я надеюсь, что это помогло! Дайте мне знать, что вы думаете в комментариях, и удачного дня!

Оцените статью
Procodings.ru
Добавить комментарий