Сделайте ваши медленные приложения лучше

К тому же пахнет React 17 на React Conf в октябре 🤞

Пришло время узнать, что такое новые функции React Suspense и Time Slicing!

Вы можете прослушать это письмо 🎧

Предполагается, что React Suspense и Time Slicing предназначены для оптимизации тяжелых рабочих нагрузок для медленных устройств. Медленные сети, плохие процессоры, все работает гладко и хорошо.

Что может быть лучше, чем визуализация 199 222 велосипедных поездок в Сан-Франциско за июль 2018 года? Это общедоступный набор данных, в нем много данных, рендеринг всех этих узлов DOM заставит плакать даже ваш MacBook Pro за 3500 долларов.

👌

PS: более углубленной версией этого будет новая глава в React + D3 2018, на которую вы все еще можете оформить предзаказ

ПРИМЕЧАНИЕ. Это кросс-пост из моего информационного бюллетеня. Я публикую каждое электронное письмо через две недели после его отправки. Подпишитесь, чтобы получать больше подобного контента раньше прямо в свой почтовый ящик! 💌

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

Старшие гонщики предпочитают более короткие поездки?

Эх, не сильная корреляция. Но вы можете видеть, что молодые гонщики ездят больше. Может быть, потому, что Сан-Франциско перекос молод? Согласно Google, средний возраст составляет 36 лет.

Тяжело сказать. В любом случае, это не тот вопрос, который нас волнует.

Наш вопрос заключается в следующем: Может ли ваш компьютер и браузер обработать эту диаграмму рассеяния?

Да! Вот как 👇

Не верите мне? Попробуйте: https://dist-exhowcijhf.now.sh/

Визуализация использует несколько приемов:

  1. Используя React Suspense, мы показываем экран загрузки, пока в браузер загружается 40 МБ данных CSV.
  2. Разделите набор данных на 4 сегмента и отобразите с перекрывающимися диаграммами рассеяния, чтобы избежать больших компонентов
  3. Используйте асинхронный рендеринг, чтобы постепенно отображать все больше и больше данных

Почему и как React Suspense?

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

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

<React.Placeholder delayMs={500} fallback={<div>🌀 Loading like 40 megs of CSV</div>} > <LazyViz /> </React.Placeholder>

React.Placeholder - это компонент-заполнитель, который вы всегда хотели. Теперь встроен прямо в React.

Больше не нужно возиться с componentDidMount и запутанными процедурами загрузки или сложными махинациями Redux.

Окончательный API все еще может измениться. Посмотрим. Вы можете прочитать это как «Уважаемый заполнитель, если LazyViz не разрешится в течение 500 миллисекунд, выполните откат.

delayMs указывает, как долго вы хотите ждать, fallback - это свойство рендеринга, которое показывает состояние загрузки, а <LazyViz> - компонент React, который возвращает обещание.

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

На данный момент лучший (единственный?) Способ попробовать это - использовать экспериментальный пакет simple-cache-provider Дэна Абрамова. Он работает как кеш загружаемых ресурсов, поэтому последующие запросы обрабатываются быстрее, чем первоначальные. Думаю.

Я уверен, что скоро из этого появятся новые реализации и крутые идеи.

А пока вот как вы можете использовать его при загрузке данных для визуализации 👇

const getData = createResource( () => d3 .csv( "https://s3-us-west-1.amazonaws.com/swizec-datasets/201807-fordgobike-tripdata.csv", d => ({ duration_sec: Number(d.duration_sec), member_birth_year: Number(d.member_birth_year) }) ) .then(data => props => <Dataviz data={data} {...props} />), key => key ); const LazyViz = props => { return getData.read(cache)(props); };

Мы создаем новый ресурс под названием getData. Функция createResource принимает метод загрузки, который в нашем случае является вызовом d3.csv, и хеш-карту. Наша хеш-карта возвращает ключ для ключа, что кажется бессмысленным, но в противном случае simple-cache-provider не работает. ¯_ (ツ) _ / ¯

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

Итак, мы берем загруженный data и возвращаем функциональный компонент, который принимает props и возвращает наш основной Dataviz компонент. Вот эта часть 👇

data => props => <Dataviz data={data} {...props} />

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

Теперь, чтобы использовать все это в React.Placeholder, мы должны обернуть это в другой функциональный компонент, называемый LazyViz.

const LazyViz = props => { return getData.read(cache)(props); };

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

Все это немного запутано, и мне потребовалось время, чтобы грокнуть, и я все еще не уверен, что полностью понимаю.

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

Почему и как Time Slicing?

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

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

В моих экспериментах отрисовка всего набора данных могла занять до нескольких минут. Это если бы вкладка браузера не просто аварийно завершилась.

Наша проблема двоякая:

  1. Рендеринг многих узлов одновременно выполняется медленно
  2. Мы блокируем поток пользовательского интерфейса во время рендеринга, из-за чего вся страница кажется медленной.

Мы можем решить обе эти проблемы с помощью Time Slicing! 🤘

Time Slicing позволяет нам перенести работу по рендерингу React на задний план. Асинхронный рендеринг.

Не волнуйтесь, вы не берете на себя работу планировщика React. Вы просто даете ему понять, что, эй, это не так важно, вы можете делать это в любое время. Планировщик разберется с остальным.

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

Но вот временная часть 👇

class Dataviz extends React.PureComponent { componentDidMount() { this.showMoreData(); } componentDidUpdate() { this.showMoreData(); } showMoreData = () => { const { N, chunkSize } = this.state; if (N < chunkSize) { requestAnimationFrame(() => this.setState({ N: N + 1000 })); } };

showMoreData использует this.setState для увеличения количества данных, которые мы показываем с шагом в 1000. Бит, который создает квантование времени, заключается в том, что this.setState заключен в requestAnimationFrame вызов, что делает его асинхронным.

Я также экспериментировал с requestIdleCallback, что рекомендует команда React, но это замедляет визуализацию. Не знаю почему.

Вызов showMoreData в componentDidUpdate создает цикл, который продолжается до тех пор, пока showMoreData запускает обновления. Вызов его в componentDidMount запускает процесс.

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

Вы также можете попробовать вживую.

На всякий случай я также обернул все приложение в <React.unstable_AsyncMode>, но у меня нет свидетельств, которые бы что-то меняли. Асинхронные обновления с временным разделением, казалось, работали и без него.

Попробуй сам

Лучший способ поиграть с React Suspense и Time Slicing прямо сейчас - это использовать настройку react-Suspense-starter @ palmerhq. Предоставляет вам базовую настройку Parcel и будущую версию React.

Начни возиться с кодом и посмотри, что получится.

Вы также можете посмотреть полный код моего эксперимента на GitHub.

В других новостях…

Прогресс в новой редакции React + D3 2018 был… хорошим? Сложно сказать. Время часто скользит между пальцами.

Как много. Как уже сентябрь? Разве это не был июль? О.о

Но исследовательская часть идет хорошо. Я обещал, что мы расскажем о новых функциях React, и вот они. Виртуальная реальность еще не закончена, и мне нужно подправить некоторые примеры React Native.

Также необходимо обновить холст и пример Konva, потому что этот API изменился, и я думаю, что хочу добавить раздел об использовании Gatsby для рендеринга на стороне сервера. 🤔

Впереди много работы, но как только исследование будет сделано, написание будет быстрым. Скоро вы начнете получать новый контент :)

А пока вот небольшая забавная история об отладке макетов электронной почты, когда ни один из ваших обычных инструментов не справится с этой задачей 👇

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

Ваше здоровье,

~ Swizec

P.S. Если вам это нравится, обязательно подпишитесь, подпишитесь на меня в Twitter, купите мне обед и поделитесь этим со своими друзьями 😀