По мере увеличения размера пакета интерфейсного приложения разработчики начали искать более эффективные способы более быстрой загрузки пакетов клиенту. Разделение кода и отложенная загрузка - это способ значительно сократить время начальной загрузки для клиентов.
Есть несколько стратегий для разделения ваших кодов 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
- React.lazy с одним неизвестным: https://codesandbox.io/s/reactlazy-one-suspense-example-giy9c
- React.lazy с множественной неизвестностью, https://codesandbox.io/s/reactlazy-multiple-suspense-example-hgz4u
- React.loadable, https://codesandbox.io/s/react-loadable-example-7s5mu
- @ loadable / component, https://codesandbox.io/s/loadablecomponent-example-th1xt
Если вам нравятся мои статьи, вы можете поддержать меня, хлопая в ладоши и подписываясь на меня.
Я тоже на linkedin, все приглашения приветствуются.