По мере увеличения размера пакета интерфейсного приложения разработчики начали искать более эффективные способы более быстрой загрузки пакетов клиенту. Разделение кода и отложенная загрузка - это способ значительно сократить время начальной загрузки для клиентов.

Есть несколько стратегий для разделения ваших кодов javascript.

  • Разделение базы маршрута
  • Компонентное базовое разделение
  • Разделение базы библиотеки

Для достижения этих стратегий разделения кода и отложенной загрузки исследуются 4 разные библиотеки. Давайте изучим библиотеки одну за другой.

custom-component.js будет использоваться в следующих примерах.

/* custom-compnent.js */
import React, { useEffect } from "react";
const CustomComponent = ({ label }) => {
  useEffect(() => {
    console.log(`${label} created`);
    return () => console.log(`${label} destroyed`);
  }, []);
return <div>{label}</div>;
};
export default CustomComponent;

React.lazy

В версии 16.6 React имеет встроенную поддержку отложенной загрузки компонентов. Функция React.lazy принимает функцию, основанную на обещании, и возвращает ее.

  • экспортируйте свои компоненты по умолчанию (здесь наш CustomComponent). Эта библиотека еще не поддерживает именованный экспорт.
  • Вызов const LazyLoadedComponent = React.lazy (() = ›import (‘ ./ custom-component.js ’)
  • Используйте ‹LazyLoadedComponent /›

в приведенном ниже примере я использовал обещание и тайм-аут, чтобы показать эффект загрузки

‹Suspense /› - это реагирующий компонент, у которого есть резервная опора, которая принимает любой реагирующий компонент. Используя этот компонент, вы можете показать индикатор загрузки.

import React, { Suspense } from "react";
import ReactDOM from "react-dom";
/* wait 100 ms to render component */
const CustomComponent = React.lazy(
  () =>
    new Promise((resolve, reject) =>
      setTimeout(() => resolve(import("./custom-component")), 100)
    )
);
/* wait 500 ms to render component */
const CustomComponent2 = React.lazy(
  () =>
    new Promise((resolve, reject) =>
      setTimeout(() => resolve(import("./custom-component")), 5000)
    )
);
function App() {
  return (
    <>
      <Suspense fallback={<div>Loading</div>}>
        <CustomComponent label="Component 1" />
        <CustomComponent2 label="Component 2" />
      </Suspense>
    </>
  );
}

Объединение всех ваших пользовательских компонентов в один фрагмент, как в приведенном выше примере, приведет к тому, что ваш пользовательский интерфейс будет отображать индикатор загрузки до тех пор, пока не будет загружен загруженный компонент. В приведенном выше примере Компонент 1 будет создан, но не будет отображаться, пока Компонент 2 не будет загружен.

Рабочий пример на codeandbox можно найти здесь

Чтобы решить эту проблему, вы можете заключить свои отложенные компоненты в несколько компонентов ‹Suspense /›.

<>
  <Suspense fallback={<div>Loading</div>}>
    <CustomComponent label="Component 1" />
  </Suspense>
  <Suspense fallback={<div>Loading</div>}>
    <CustomComponent2 label="Component 2" />
  </Suspense>
</>

Рабочий пример на codeandbox можно найти здесь

/* route base splitting */
const DashboardPage = React.lazy(() => import('../pages/dashboard'));
const SettingsPage = React.lazy(() => import('../pages/settings'));
<Route>
  <DashboardPage />
  <SettingsPage />
</Route>

Недостаток: необходимо вручную импортировать все компоненты страницы по одному.

@ загружаемые / компоненты

Если у вас есть SSR, то React.lazy вам не подойдет, потому что он не поддерживает SSR. Но сила этой библиотеки заключается не только в наличии SSR, но и в некоторых приятных функциях, таких как предварительная выборка, подсказки веб-пакетов и разделение библиотеки. Итак, что предлагает вам эта библиотека,

  • ССР
  • Использование подсказок веб-пакетов для разделения
  • Возможность разделить ваши куски в соответствии с библиотеками
  • Позволяет вам написать общую функцию для динамического импорта, такую ​​как import (`$ {component}`)
  • Использование ‹Suspence /› (Не требуется, необязательно)
  • предварительная выборка

Использование похоже на React.lazy.

import React, { Suspense } from "react";
import ReactDOM from "react-dom";
import loadable, { lazy } from "@loadable/component";
const CustomComponent = lazy(
  () =>
    new Promise((resolve, reject) =>
      setTimeout(() => resolve(import("./custom-component")), 100)
    )
);
const CustomComponent2 = lazy(
  () =>
    new Promise((resolve, reject) =>
      setTimeout(() => resolve(import("./custom-component")), 5000)
    )
);
function App() {
  return (
    <>
      <Suspense fallback={<div>Loading</div>}>
        <CustomComponent label="Component 1" />
        <CustomComponent2 label="Component 2" />
      </Suspense>
    </>
  );
}

В приведенном выше примере будет достаточно просто заменить React.lazy на ленивую функцию, исходящую из импорта @ loadable / component.

Давайте удалим компонент ‹Suspense /› и воспользуемся резервным параметром в @ loadable / component. Единственная разница между использованием ‹Suspense /› и резервным использованием заключается в том, что мы не используем ленивую функцию из @ loadable / component, но загружаемую.

import React from "react";
import ReactDOM from "react-dom";
import loadable from "@loadable/component";
const CustomComponent1 = loadable(
  () =>
    new Promise((resolve, reject) =>
      setTimeout(() => resolve(import("./custom-component")), 100)
    ),
  {
    fallback: <div>Loading...</div>
  }
);
const CustomComponent2 = loadable(
  () =>
    new Promise((resolve, reject) =>
      setTimeout(() => resolve(import("./custom-component")), 5000)
    ),
  {
    fallback: <div>Loading...</div>
  }
);
function App() {
  return (
    <>
      <CustomComponent1 label="Compnent 1" />
      <CustomComponent2 label="Component 2" />
    </>
  );
}

Функция предварительной загрузки имеет два типа использования.

Чтобы загрузить компонент, когда браузер бездействует, можно использовать подсказки webpackPrefetch и webpackPreload.

const CustomComponent = loadable(() =>
  import(/* webpackPrefetch: true */ './custom-component.js'),
)

Другое использование - запуск предварительной выборки вручную. Есть функция preload (), которая вызывается, когда нужно показать компонент.

import React from "react";
import ReactDOM from "react-dom";
import loadable from '@loadable/component'
const CustomComponent = loadable(() => import('./custom-component.js'))
function App() {
  const [show, setShow] = useState(false)
  return (
    <div>
      <a 
        onMouseOver={() => CustomComponent.preload()}
        onClick={() => setShow(true)}>
        Show me
      </a>
      {show && <Infos />}
    </div>
  )
}

Чтобы использовать preLoad в SSR, вы должны использовать подсказки веб-пакетов. Эта библиотека пока не поддерживает функцию preLoad в SSR.

Эта библиотека изначально не поддерживает тайм-аут или задержку реквизита. Чтобы использовать эти функции, рекомендуется использовать некоторые сторонние библиотеки, которые можно найти в его репозитории на github.

import loadable from '@loadable/component'
const LazyLoadPage = loadable({page} => import(`./${page}`))
<Route>
  <LazyLoadPage page="Dashboard" />
  <LazyLoadPage page="Setting" />
</Route>

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

реагирующий

response-loadable имеет огромное количество функций от SSR до пользовательского рендеринга. Его использование похоже на @ loadable / components с дополнительными функциями.

Что он предлагает вам,

  • встроенные функции задержки, тайм-аута
  • пользовательский компонент рендеринга вместо импортированного
  • ССР
  • предварительная выборка
import Loadable from "react-loadable";
const CustomComponent = Loadable({
  loader: () =>
    new Promise((resolve, reject) => {
      setTimeout(() => resolve(import("./custom-component")), 2000);
    }),
  loading: ({ pastDelay }) => (pastDelay ? <div>Loading...</div> : null),
  delay: 50
});
const CustomComponent2 = Loadable({
  loader: () =>
    new Promise((resolve, reject) => {
      setTimeout(() => resolve(import("./custom-component")), 5000);
    }),
  loading: () => <div>Loading...</div>
});
const ErrorCustomComponent = Loadable({
  loader: () =>
    new Promise((resolve, reject) => {
      setTimeout(() => reject(import("./custom-component")), 200);
    }),
  loading: ({ error }) =>
    !error ? <div>Loading...</div> : <div>Component could not be loaded!</div>
});
const TimeoutComponent = Loadable({
  loader: () =>
    new Promise((resolve, reject) => {
      setTimeout(() => resolve(import("./custom-component")), 2000);
    }),
  loading: ({ timedOut }) =>
    timedOut ? <div>Taking too long...</div> : <div>Loading...</div>,
  timeout: 50
});
function App() {
  return (
    <>
      <CustomComponent label="Component 1" />
      <CustomComponent2 label="Component 2" />
      <ErrorCustomComponent label="Component 3" />
      <TimeoutComponent label="Component 4" />
    </>
  );
}

Несмотря на то, что эта библиотека имеет множество функций и звезд, последние замечательные коммиты были сделаны более года назад. Хотя есть какие-либо предупреждения о репозитории github, он может больше не поддерживаться. Так что будьте осторожны при его использовании.

реагирующая загрузка-видимость

react-loadable-visibility - это компонент-оболочка, построенный на основе библиотек react-loadable и @ loadable / component для загрузки компонентов, когда они находятся в области просмотра. Он делает это с помощью InterSectionObserver API. В ней есть полифилл, но автор этой библиотеки сомневается в ее работоспособности.

Что предлагает вам эта библиотека,

  • Ленивая загрузка компонентов, если они находятся в области просмотра на экране.
  • прост в использовании с использованием только функции loadableVisibility.
  • Вы по-прежнему можете использовать конфигурацию react-loadable & @ loadable / component

Имеет смысл, если ваша страница слишком длинная

import loadableVisibility from "react-loadable-visibility/loadable-components";
const LoadableComponent = loadableVisibility(() => import("./custom-component"), {
  fallback: () => <div>Loading...</div>
});
export default function App() {
  return <LoadableComponent />;
}

Вы можете найти рабочие примеры на codeandbox

Если вам нравятся мои статьи, вы можете поддержать меня, хлопая в ладоши и подписываясь на меня.
Я тоже на linkedin, все приглашения приветствуются.