Код уже давно взломан для рендеринга на стороне сервера и разделения кода индивидуально. До сих пор - за исключением Next.js - не было простой в использовании общей абстракции для одновременного достижения обоих для большего сообщества NPM. В этой статье представлены несколько пакетов, над которыми я работал долгое время, и которые, я надеюсь, станут окончательным простым + идиоматическим решением этой проблемы.

import { flushModuleIds } from 'react-universal-component/server'
import flushChunks from 'webpack-flush-chunks'

const app = ReactDOMServer.renderToString(<App />)
const { js, styles } = flushChunks(webpackStats, {
  moduleIds: flushModuleIds()
})

res.send(`
  <!doctype html>
  <html>
    <head>
      ${styles}
    </head>
    <body>
      <div id="root">${app}</div>
      ${js}
    </body>
  </html>
`)

TL;DR

Запустите один из шаблонов универсального веб-пакета ниже и изучите код и готово. Есть 4 пакета и 4 шаблона:

ПАКЕТЫ:









ПЛИТЫ:









ОБНОВЛЕНИЕ (26 июля): теперь официальное демонстрационное репо: https://github.com/faceyspacey/flush-chunks-boilerplate-webpack-chunknames… мы продолжит поддерживать очистку идентификаторов модулей, но сосредоточится на очистке имен блоков.

ИСТОРИЯ

Около двух месяцев назад Джеймс Кайл заложил основу для того, что нужно было сделать, в своей теперь известной статье, заканчивающейся словами «используйте это дерьмо», что даже вдохновило Аруноду на его «динамический» компонентный вклад в Next.js 3.0. Так родился React Loadable.

React Loadable в конечном итоге предложил решение, на которое, возможно, многие сделали что-то похожее, так или иначе. И у которого была такая же проблема: он решал только проблему наличия асинхронного клиентского компонента. Наиболее важным было открытие Джеймса Кайла ближе к концу статьи о том, что даже если у вас может быть асинхронный компонент, он нужен для асинхронного и синхронного рендеринга, а также для буферизации / очистки идентификаторов модулей, которые были синхронно загружены в server может привести к выяснению того, какие начальные блоки отправлять клиенту.

Таким образом, оказалось, что асинхронный аспект на самом деле был более простой задачей.

Синхронный рендеринг клиента, как и на сервере, требует много труда.

Вы должны выполнять рендеринг синхронно на сервере (в средах сервера babel или webpack) И снова синхронно на клиенте при начальной загрузке страницы, чтобы контрольные суммы React совпадали с тем, что было получено с сервера, и поэтому был выполнен дополнительный рендеринг. не происходит (и поэтому вы не теряете драгоценные миллисекунды или секунды, загружая модули асинхронно). Даже это оказывается только началом проблемы - потому что как насчет фактического обнаружения, каким фрагментам соответствуют идентификаторы модулей или пути? А как насчет их обслуживания? А как насчет таблиц стилей? Собираетесь ли вы всегда отображать свою таблицу стилей как один большой main.css вместо того, чтобы разбивать ее на куски, как ваш js ИЛИ, вам придется использовать новые захватывающие инструменты, которые имеют свои собственные предостережения: они не генерировать статические кэшируемые листы и постоянно потреблять циклы во время рендеринга как на клиенте, так и на сервере? А как насчет HMR? Этот список можно продолжить. Для разделения кода и правильного SSR требуется подробный список вещей, отмеченных с легкостью, и, что более важно: идиоматически.

И ТАК РОДИЛСЯ WEBPACK-FLUSH-CHUNKS

И поэтому я старательно продолжил свой путь, чтобы решить проблему, с которой я мирился в прошлом, воспользовавшись преимуществами разделения кода без SSR + SEO. Если вы это сделали, то вы знаете, что упражнение: создайте карту возможных компонентов, которые вы хотите загружать асинхронно, где каждый ключ - это имя компонента, а значение - это объект, содержащий асинхронное и синхронное значение ( для сервера). Затем, когда вы закончите, вы можете динамически вызвать require.ensure и выбрать, какой компонент загружать, и, что более важно, переключаться между отрисовкой синхронного на сервере и асинхронного на клиенте. И когда вы закончили, вы / я просто согласились на отказ от рендеринга на стороне сервера и без преимуществ SEO.

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

В любом случае, все идет в прогрессе, и, в конечном счете, это действительно захватывающая вещь во всем, что происходит в Reactlandia. Я даже не собираюсь произносить слова «Javascript F *** gue», чтобы нарисовать картину, но для меня это полная противоположность. Все мы децентрализованно развиваем идеальный мир [программного обеспечения]. Будущее здесь. Вернее, это только начало.

МОТИВАЦИЯ: ПРОБЛЕМЫ В ПРОШЛОМ / РАЗДЕЛЕНИЕ КОДА

Так каковы были точные проблемы? В чем заключались недостатки разделения кода?

Webpack давно представил возможность разделения кода. Однако для большинства (по крайней мере для меня) это оставалось загадкой. Во-первых, просто пробовать оригинальный require.ensure API и то, как создать конфигурацию Webpack для его поддержки, для многих было неестественным. Что еще более важно, если бы вы просто прочитали, как разработчики использовали его, вы бы подумали, что дело сделано. Но любой, кто пытался охватить эту функцию полным кругом и включить рендеринг на стороне сервера, оставался чесать голову (для меня было удивительно, насколько мало об этом говорили).

В чем смысл разделения кода при начальной загрузке страницы - если код из дополнительных фрагментов оценивался на стороне сервера - для их получения требовался дополнительный запрос?

Конечно, когда ваши пользователи перемещаются по вашему одностраничному приложению, это пригодится, но как насчет SEO? Как насчет того, чтобы не отображать счетчики загрузки после начальной загрузки страницы? Если вы похожи на меня, то в итоге вы использовали разделение кода только для нескольких областей вашего приложения, где SEO не было важно - часто менее используемых частей вашего приложения. Не существовало готовых решений для решения этой проблемы, кроме Next.js, что требует от вас приверженности «фреймворку». Я пришел из мира Meteor и покинул его полтора года назад, Я не собирался возвращаться в страну Framework.

Таким образом, React может синхронно отрисовывать себя на сервере за один раз. Однако для этого на клиенте требуются все фрагменты, используемые для выполнения этого рендеринга, который, очевидно, различается для каждого уникального URL-адреса, аутентифицированного пользователя и т. Д. Хотя дополнительные асинхронные запросы, инициируемые при навигации пользователя по вашему приложению, - вот что такое разделение кода. о, неоптимально загружать дополнительные фрагменты при первоначальном рендеринге. Точно так же вы не хотите просто отправлять все фрагменты клиенту для этого начального запроса, поскольку это противоречит цели разделения кода. Кроме того, если ваша стратегия является первой, контрольные суммы не совпадают, и на клиенте произойдет дополнительная ненужная визуализация.

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

РЕШЕНИЕ

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

  • внешний интерфейс
  • бэкэнд

Джеймс Кайл является пионером «внешнего интерфейса»: React Loadable при использовании на сервере пропускает фазу загрузки и синхронно визуализирует содержащийся в нем компонент, одновременно записывая идентификатор соответствующего модуля.

React Loadable может использоваться несколько раз и, следовательно, может записывать несколько точек разделения.

Что делает Webpack Flush Chunks (то есть бэкэнд) - это перекрестные ссылки на эти идентификаторы модулей (или пути, если используется сервер Babel) со статистикой вашего Webpack, чтобы определить минимальный набор фрагментов, необходимых для повторно отобразить эти модули / компоненты на клиенте. Сами чанки содержат ВСЕ файлы, соответствующие чанку (js, css, исходные карты и т. Д.). Таким образом, оттуда Webpack Flush Chunks выводит строки, компоненты React или простые массивы, содержащие точные файлы javascript (и файлы CSS) для вставки в ваш ответ HTML. Хотя вам доступен API нижнего уровня, он также автоматически обрабатывает ваши main, vendor и возможные bootstrap фрагменты, размещая их в правильном порядке. Он даже создает компоненты React, которые вы можете передать renderToStaticMarkup. Возможно, самое важное, что он предоставляет, - это просто точный и подробный список вещей, которые вы должны сделать, чтобы настроить конфигурацию Webpack.

Это не самое сложное (хотя по какой-то причине так долго оставалось тайной). Что нужно, так это внимательно ознакомиться со всей статистикой, выдаваемой Webpack. Также необходимо выяснить, как сопоставить пути Babel с идентификаторами модулей Webpack, если вы используете сервер Babel. Короче говоря, код не слишком сложный и очень удобен в обслуживании, но для его выяснения потребовалось много размышлений, проб и ошибок.

Это конец истории. Используйте блоки очистки Webpack. Используй это дерьмо, как сказал Джеймс Кайл.

ДАЛЕЕ: CSS

Не совсем так. Также есть CSS. У меня много мнений по этому поводу. Но большую часть я оставлю для ридми Извлечь CSS Chunks Webpack Plugin.

У меня тоже есть ответы. Это сводится к:

  • тот факт, что использование модулей CSS уже является «CSS-in-JS»
  • Тратить циклы на рендеринг CSS как на клиенте, так и на сервере - это пустая трата!
  • Кэшируемые таблицы стилей - это просто кешируемые!
  • HMR является обязательным
  • И угадай что? Если вы можете разбить свой CSS на части так же, как JS, вам не нужно создавать CSS «пути рендеринга» для отправки наименьшего количества байтов по сети. Фактически, вы отправляете меньше!

С помощью настоящих решений r CSS-in-JS, таких как StyleTron, Aphrodite и т. Д., Весь ваш CSS в любом случае представлен в коде, также известном как javascript. Таким образом, вы можете отправлять минимально возможное количество CSS, но вы отправляете все это в виде javascript ДОПОЛНИТЕЛЬНО и НЕ ВАЖНО ЧТО.

Также оказывается, что если вы можете статически разбить свой CSS на части, вы достигли правила 80–20: вы достигли оптимального уровня 80% оптимизации того, как мало CSS вы отправляете по сети. Видите ли, настоящая проблема заключается, например, в отправке CSS ваших частных пользовательских панелей на ваш общедоступный сайт или наоборот. Если у вас много разделов / панелей, ваш CSS экспоненциально растет и рассылается повсюду. Однако, если у вас есть простой механизм разбиения CSS на части по разделам, вы решили 80% проблемы, если не больше.

Опять же, вы можете прочитать файл Extract CSS Chunks Webpack Plugin, чтобы узнать больше об этом. Это сводится к статическому определению того, какой CSS отправлять, что является чертовски хорошим решением. Достижение этих последних 20% путем принесения в жертву циклов рендеринга и необходимости использовать специальные узлы - ИМХО - это придирка и приводит к уменьшению отдачи.

Я упоминал, когда вы фактически запрашиваете асинхронные блоки, в этих блоках CSS снова готов для внедрения в JavaScript? Видите ли, он создает два блока js: один без css, который отправляется в начальных запросах вместе с реальными таблицами стилей, И другой для асинхронных запросов, который, как обычно, имеет CSS-инъекцию! Это дает вам минимально возможные исходные пакеты js.

Я уже упоминал - в отличие от исходного Extract Text Webpack Plugin - он поддерживает HMR! ИСПОЛЬЗУЙТЕ ЭТО ДЕРЬМО! »

БОЛЕЕ

Да, у меня есть еще. Вы хотите создать свой собственный асинхронный компонент, потому что ни React Loadable, ни Универсальный компонент React не отвечают вашим потребностям?

Что ж, основной аспект всей этой добродетели «универсального рендеринга» был извлечен / извлечен в отдельный пакет:



И то, что вы делаете с его помощью, будет сбрасывать фрагменты вместе с фрагментами очистки Webpack так же легко, как React Loadable и универсальный компонент React .

На этот раз я больше ничего не скажу. ИСПОЛЬЗУЙТЕ ЭТО ДЕРЬМО!

УНИВЕРСАЛЬНЫЙ КОМПОНЕНТ REACT

Но не будем забегать вперед. Многое было вложено в универсальный компонент React, чтобы сделать его все-все-все - то, что я называю «универсальным» - компонентом. React Loadable надрал задницу, и в конце концов, это его духовный преемник!

В основном все, что находится под солнцем (от PR, выпусков, других пакетов и т. Д.), Было включено в него. Конечно, по своему усмотрению;)

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

import universal from ‘react-universal-component’

const UniversalComponent = universal(() => import(‘./Foo’), {
  loading: Loading,
  error: Error,
  timeout: 15000,
  minDelay: 300,
  chunkName: ‘myChunkName’,
  onLoad: module => replaceReducers({ ...reducers, module.bar })
  key: ‘Foo’ || module => module.Foo,
  path: path.join(__dirname, ‘./Foo'),
  resolve: () => require.resolveWeak('./Foo')
})

export default ({ isLoading, error }) =>
  <div>
    <UniversalComponent isLoading={isLoading} error={error} />
  </div>

и вуаля!

API с двумя аргументами, например Next.js ’dynamic, и аргумент параметров с очень чистой поверхностью как у Vue , который сам по себе также вдохновлен React Loadable.

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

Есть еще намного больше, чем кажется на первый взгляд. Во-первых, вы можете обернуть полученный <Universal Component/> в HoC, который выполняет выборку данных (т.е. некоторую отдельную асинхронную работу) и повторно использует компонент Loading (DRY) через опору isLoading и т. Д. Это идеально подходит для Apollo и подобное, аналогичное, похожее.

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

Вы можете использовать onLoad для использования других экспортов из модуля для выполнения связанной работы: замены редукторов, обновления саг, возможно, что-то с анимацией и т. Д.

Если будет достигнуто 15000milliseconds, отобразится компонент ошибки. Спасибо, Vue.

minDelay отличается от того, что есть в React Loadable. Это приводит к более отзывчивому компоненту. Вместо того, чтобы ждать несколько мс, чтобы увидеть что-нибудь, он всегда показывает счетчик немедленно. И вы можете установить минимальное количество времени, по истечении которого асинхронный компонент может отображаться. Это также помогает с анимацией. Скажем, ваша страница со счетчиком в ней скользит внутрь, а анимация скольжения занимает 500 мс - ну, теперь вы можете избежать рендеринга из-за испорченной анимации скольжения, продлив обновление страницы до тех пор, пока анимация скольжения не будет завершена. Это также лучше решает исходную проблему предотвращения мигания между загрузочным счетчиком и компонентом, поскольку, несмотря ни на что, они могут появиться примерно в одно и то же время без минимальной задержки, которую вы можете контролировать. Readme также содержит информацию о вас. Это просто не в моей голове.

Он поддерживает HMR, которого React Loadable еще не достиг. То же самое со всем вашим асинхронным CSS, если вы используете Плагин Extract CSS Chunks Webpack.

Наконец, вместо того, чтобы ограничиваться обещаниями с import() , вы можете использовать функцию, которая вызывает require.ensure с обратным вызовом, что дает вам дополнительные возможности require.ensure. Фактически вы можете делать все, что делает Async Reactor, включая получение данных. Что еще более важно, реквизиты передаются как аргументы, поэтому вы можете определить, какие данные следует извлекать динамически. Это история для другого дня, но проверяйте Async Reactor, когда будете просматривать этот материал. Даже если вы хорошо знакомы с React Loadable, но не проверили это, скорее всего, это вызовет у вас зацикливание [в очень хорошем смысле].

Интерфейс, предложенный Async Reactor, имеет большой потенциал для того, чтобы стать идиоматическим будущим комбинированного асинхронного / синхронного «универсального» рендеринга.

По сути, он может стать ответом большего сообщества NPM на getInitialProps Next.js, если в нем будет рекурсивная система разрешения обещаний, такая как Apollo. Прочтите конец ридми, чтобы услышать, как я расскажу о том, что я думаю о будущем.

И я уже упоминал: вам не обязательно использовать его в сценарии с разделенным рендером. Вот где он сияет. Если вы прочитаете файл readme (и сравните его с Async Reactor), вы сможете сделать несколько интересных вещей с аргументом async component. Только асинхронный режим - это также основной вариант использования этого пакета.

ЗАКЛЮЧЕНИЕ

ИСПОЛЬЗУЙТЕ ВСЕ ЭТО ДЕРЬМО. ВЫ МОЖЕТЕ СДЕЛАТЬ ГОРАЗДО БОЛЬШЕ, ЧТО ВЫ ПРОЧИТАЕТЕ В ДОБАВЛЕНИЯХ. ЗАКРЫТО ТАКОЕ МНОГО ОСНОВ. ДО СВИДАНИЯ.

PS.

Я упоминал только что появившуюся функцию «волшебных комментариев» Webpack, которая также полностью поддерживается? Просто используйте его, назовите свои блоки и вызовите flushChunkNames вместо flushModuleIds и передайте chunkNames universal(asyncWork, { chunkNames }), чтобы он заработал. Это сэкономит вашему серверу несколько циклов от выполнения всего, что мне нужно было сделать, чтобы перепрыгнуть через обручи, чтобы связать идентификаторы модулей со статистикой.

Программное обеспечение Ричарда Скарротта Webpack-Hot-Server-Middleware (HMR на сервере) является мировым классом. Изучите его использование в шаблонах. Это важно и связано, поскольку предполагается, что это самый современный [неограничивающий] интерфейс разработчиков React / NPM для серьезных приложений. Сами шаблоны - я мог бы добавить - нетронутые. Разработчик повсюду испытывает добро, надеюсь, вы найдете все это идиоматическим. Наслаждайтесь!

›Чтобы узнать о более идиоматическом javascript в Reactlandia, прочтите:











Как использовать новую функцию« волшебного комментария Webpack с React Universal Component + SSR
Webpack 2.4.0, который вышел несколько недель назад, был запущен с очень интересным новым особенность: волшебные комментарии. На… medium.com »





Мы очень ценим твиты и другую любовь. Найдите меня в твиттере @faceyspacey Хотите быть в курсе событий Reactlandia? Нажмите ПОДПИСАТЬСЯ рядом с публикацией FaceySpacey, чтобы получать еженедельные письма Medium по электронной почте 👇 🏽