От Create-React-App к Next

0 0

Есть много причин, по которым вы можете захотеть перейти на Next с приложения CRA. Next обеспечивает рендеринг на стороне сервера (SSR) и даже инкрементную статическую регенерацию (ISR) при размещении на Vercel. Это всеобъемлющая структура со встроенной маршрутизацией, оптимизацией изображений, средой разработки и многим другим.

Этот пост представляет собой подробное руководство по переходу с CRA на Next. Вот что мы рассмотрим:

Шаблон HTML

Содержание заголовка

От Create-React-App к Next

React JS. Основы

Изучите основы ReactJS на практическом примере по созданию учебного веб-приложения

Получить курс сейчас!

Маршрутизация

Разделение кода

Стилизация

CSR / SSR

Проверка кода на ошибки (линтинг)

Запуск обеих систем

Подведение итогов

Шаблон HTML

CRA использует файл index.html в папке public для настройки приложения. Next обрабатывает все в React через файл _document.js, поэтому его нужно перемещать вручную. К счастью, это относительно легко сделать, и документация Next содержит подробные указания.

Содержание заголовка

Для индивидуального управления заголовками на постраничной основе Next предлагает собственное решение next / head, а CRA — нет. Обычно используются react-helmet (или его чистая версия, react-helmet-async ) или более свежая hoofd. В любом случае я бы рекомендовал абстрагировать использование библиотеки на определенные компоненты или хуки, потому что в таком случае при переключении на Next нужно будет обновить код только в одном месте.

Например, вместо импорта Head из react-helmet на каждой странице импортируйте свой собственный компонент Head, который является оболочкой для react-helmet. Таким образом, вы можете обновить детали реализации, чтобы они работали с Next, не изменяя других компонентов.

Маршрутизация

CRA не имеет встроенной возможности маршрутизации, поэтому часто используется вместе с react-router-dom. Обычно у вас есть компонент Router, который объявляет все ваши маршруты и какой компонент визуализировать для каждого маршрута. Например:

JavaScript import { BrowserRouter, Route, Switch } from ‘react-router-dom’ import PostPage from ‘../PostPage’ import HomePage from ‘../HomePage’ const Router = () => ( <BrowserRouter> <Switch> <Route path=’/’> <HomePage /> </Route> <Route path=’/post/:slug’> <PostPage /> </Route> </Switch> </BrowserRouter> ) export default Router

123456789101112131415161718import { BrowserRouter, Route, Switch } from ‘react-router-dom’import PostPage from ‘../PostPage’import HomePage from ‘../HomePage’ const Router = () => (  <BrowserRouter>    <Switch>      <Route path=’/’>        <HomePage />      </Route>      <Route path=’/post/:slug’>        <PostPage />      </Route>    </Switch>  </BrowserRouter>) export default Router

Далее идет собственный router. Более того: маршрутизация определяется структурой папок, поэтому декларация router/routes как таковая отсутствует. Чтобы переместить это в Next, вам нужно будет создать pages/index.js и pages/post/[slug]/index.js, которые будут выглядеть так:

JavaScript // pages/index.js import HomePage from ‘../components/HomePage’ export default HomePage

1234// pages/index.jsimport HomePage from ‘../components/HomePage’ export default HomePage

JavaScript // pages/post/[slug]/index.js import PostPage from ‘../../../components/PostPage’ export async function getStaticPaths() { // If you can compute possible paths ahead of time, feel free to, but you // shouldn’t need to do it to complete the migration to Next. return { paths: [], fallback: true } } export async function getStaticProps(context) { // If you want to resolve the whole post data from the slug at build time // instead of runtime, feel free to, but you shouldn’t need to do it to // complete the migration to Next. return { props: { slug: context.params.slug } } } export default PostPage

1234567891011121314151617// pages/post/[slug]/index.jsimport PostPage from ‘../../../components/PostPage’ export async function getStaticPaths() {  // If you can compute possible paths ahead of time, feel free to, but you  // shouldn’t need to do it to complete the migration to Next.  return { paths: [], fallback: true }} export async function getStaticProps(context) {  // If you want to resolve the whole post data from the slug at build time  // instead of runtime, feel free to, but you shouldn’t need to do it to  // complete the migration to Next.  return { props: { slug: context.params.slug } }} export default PostPage

Тогда в компоненте PostPage, вместо того, чтобы читать данные из маршрутизатора с помощью useRouteMatch из react-router-dom, вы ожидаете, что они будут поступать из props. Вы можете сделать так:

JavaScript const match = useRouteMatch() const slug = props.slug || match.params.slug

12const match = useRouteMatch()const slug = props.slug || match.params.slug

Помимо самого определения маршрута, я думаю, что хороший способ перенести эту часть — это абстрагироваться от всего, связанного с маршрутизатором, на компоненты и перехватчики, чтоб при переключении на Next необходимо было провести только их обновления. Например, у вас есть компонент ссылки, который является оболочкой Link из react-router-dom, поэтому просто нужно обновить этот компонент с помощью next/link. То же самое и с useRouter и тому подобное.

От Create-React-App к Next

React JS. Основы

Изучите основы ReactJS на практическом примере по созданию учебного веб-приложения

Получить курс сейчас!

Обратите внимание, на указания Next по миграции с react-router.

Разделение кода

И снова у Next есть решение для ручного разделения кода, оно называется next/dynamic, а у CRA — нет. Насколько я могу судить, отраслевым стандартом является @loadable /component (смотрите документацию Next). Обе библиотеки работают в основном одинаково, поэтому миграция должна быть выполнена с помощью нескольких операций поиска и замены:

JavaScript – import loadable from ‘@loadable/component’ + import dynamic from ‘next/dynamic’ – const MyComponent = loadable(() => import(‘./MyComponent’)) + const MyComponent = dynamic(() => import(‘./MyComponent’))

12345– import loadable from ‘@loadable/component’+ import dynamic from ‘next/dynamic’ – const MyComponent = loadable(() => import(‘./MyComponent’))+ const MyComponent = dynamic(() => import(‘./MyComponent’))

Стилизация

CRA имеет встроенную поддержку CSS . Это означает, что вы можете импортировать файл CSS внутри компонента React, и CRA без проблем объединит CSS. К сожалению, Next не выходит за рамки глобальных таблиц стилей. Единственное место, где Next позволяет импортировать таблицы стилей, — это файл _app.js. Так что, если ваша кодовая база повсюду использует файлы CSS, вас ждет болезненная миграция (о чем, по сути, и говорится в документации).

Простой, но грязный выход — импортировать все ваши файлы CSS внутри _app.js, но такой вид нарушает разделение задач, поскольку ваши компоненты больше не отвечают за свои собственные стили. Если вы в конечном итоге удаляете компонент, не забудьте удалить его импортированные стили в файлах _app.js. В целом – это не очень хорошо.

Лучшим подходом будет миграция. К счастью, обе системы поддерживают модули CSS, поэтому можно было бы вручную преобразовать каждый файл CSS в модуль CSS. Другой подход — переместить слой стилей в решение CSS-in-JS, такое как styled-components, Fela или что-то еще. В любом случае, это будет ручная миграция, и это будет обременительно. Безусловно, самая сложная часть.

CSR / SSR

Поскольку CRA не имеет рендеринга на стороне сервера (SSR) и использует только рендеринг на стороне клиента (CSR), легко создать код, который в Next работать не будет (во время предварительного рендеринга). Например, доступ к API-интерфейсам браузера в процессе рендеринга (например window, localStorageи т.п.) или инициализация состояний с использованием информации, специфичной для клиента, вместо того, чтобы делать это при монтировании.

В этой части глубокое знание кодовой базы поможет сделать вещи дружественными к SSR. Это должно быть относительно легко сделать, и хороший набор тестов поможет выявить случаи, когда Next не удается выполнить предварительный рендеринг страницы. Более жесткий подход — запустить next build и смотреть, где он потерпит неудачу.

Проверка кода на ошибки (линтинг)

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

Возможно, вы захотите отказаться от правила @next/next/no-img-element, потому что оно предполагает, что каждое изображение будет написано с использованием next/image, что а) кажется ужасно догматичным и б) нереалистичным для миграции.

JavaScript { “extends”: “next”, “rules”: { “@next/next/no-img-element”: “off” } }

123456{  “extends”: “next”,  “rules”: {    “@next/next/no-img-element”: “off”  }}

Запуск обеих систем

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

CRA использует единственную точку входа (обычно src/index.js), тогда как Next полагается на каталог pages, поэтому у них нету конфликта. CRA проигнорирует pages, а Next проигнорирует входной файл.

Если вы абстрагировали на хуки и компоненты все, что касается маршрутизации и управления заголовками, вы можете использовать переменную среды в указанных компонентах, чтобы использовать правильные библиотеки. Небольшое подтверждение концепции (не проверено, будьте осторожны):

JavaScript import NextLink from ‘next/link’ import { RRLink } from ‘react-router-dom’ // See: https://nextjs.org/docs/basic-features/environment-variables // See: https://create-react-app.dev/docs/adding-custom-environment-variables const FRAMEWORK = process.env.NEXT_PUBLIC_FRAMEWORK || process.env.REACT_APP_FRAMEWORK const Link = props => { return FRAMEWORK === ‘next’ ? ( <NextLink href={props.to} passHref> <a>{props.children}</a> </NextLink> ) : ( <RRLink to={props.to}>{props.children}</RRLink> ) } export default Link

12345678910111213141516171819import NextLink from ‘next/link’import { RRLink } from ‘react-router-dom’ // See: https://nextjs.org/docs/basic-features/environment-variables// See: https://create-react-app.dev/docs/adding-custom-environment-variablesconst FRAMEWORK =  process.env.NEXT_PUBLIC_FRAMEWORK || process.env.REACT_APP_FRAMEWORK const Link = props => {  return FRAMEWORK === ‘next’ ? (    <NextLink href={props.to} passHref>      <a>{props.children}</a>    </NextLink>  ) : (    <RRLink to={props.to}>{props.children}</RRLink>  )} export default Link

Таким образом, вы можете запустить REACT_APP_FRAMEWORK=cra react-scripts build и развернуть его в производственной среде, пока вы медленно переносите свою кодовую базу на Next. Вы можете делать промежуточные / бета-сборки, NEXT_PUBLIC_FRAMEWORK=next next build пока не завершите миграцию.

Если у вас была конфигурация ESLint для CRA, вам может потребоваться сделать файл JavaScript вместо JSON, а также передать и использовать в нем переменную среды, чтобы вы могли выбрать правильную конфигурацию.

Подведение итогов

Я не собираюсь врать: это потребует времени и усилий и не будет безболезненным. Хотя обе структуры имеют много общего, они также принципиально различаются подходом к рендерингу (что является своего рода преимуществом Next), поэтому многое придется обновить. Самая раздражающая часть определенно — это миграция CSS, если ваше приложение CRA использует CSS.

Обязательно прочтите руководство Миграция на Next с CRA, поскольку оно содержит много полезной информации о том, как перейти с одной системы на другую, которую я не рассматривал в этой статье.

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

Автор: Kitty Giraudel

Источник: webformyself.com

Оставьте ответ