🔥

Тред (@thekashey)


Итак его величество #codesplitting, или "техасская резня бензопилой", потому что я разрежу тебя на части. На самом деле codesplitting более про собирание из частей, чем про разрезание. В общем поехали!

Codesplitting вообще очень старая тема. В те времена когда мы отдельно подключали jQuery, отдельно Google Analytics, и отдельно чего там нужно нашей странице - это был тот самый code splitting. Счастливые времена RequireJS! Но потом пришел Browserify/Webpack и все испортил.

Первым делом бандлер "обьяснил" что загружать один большой бандл - БЫСТРЕЕ, даже с http2 и компанией 1 - engineering.khanacademy.org/posts/js-packa… 2 - medium.com/webpack/webpac… 3 - github.com/theKashey/requ…

Проблема в том, что у всего есть пределы и если бандл уже под 100 мегабайт (Jira), ну или хотя бы 10 - это СЛИШКОМ много. Так что: - мельчить плохо - оно работает медленно - крупные куски переварить или сложно, или невозможно ЧТО ДЕЛАТЬ!!

Первым делом - сохраняем спокойствие и пытаемся вспомнить зачем нам этот codesplitting нужен. 💡чтобы работало быстрее!!! Так что давайте разберем почему codesplitting может что-то сделать быстрее 🚀, и почему он может что-то сделать медленнее 🐢

🚀 Почему быстрее? 🚀 - потому что можно загружать меньше кода, что не так чтобы большая проблема, особенно в России (а Австралии со скоростью все СИЛЬНО хуже) - потому что можно исполнять меньше кода, и это самое главное, именно это делает "быстрее" 😉

The “inline requires” optimization has been enabled at FB for so long that we take it for granted now. Open source bundlers (beyond Metro) need to catch up! If you care about bundle size you should start caring about how much code executes eagerly. instagram-engineering.com/making-instagr…
"Исполнять меньше" можно разными средствами: - у бабеля есть lazy режим, который импортирует файлы по фактическому использованию - у ReactNative есть примерно тоже самое - inline requires В обоих случаях работает прям очень круто. Никто не использует. twitter.com/dan_abramov/st…

На самом деле это просто - можно банально начать использовать require когда нужно. Например был у меня код в redux приложении, который требовал различные нормализации и другой jsonapi - 11kb gzip. Я заменил import в шапке на require в самом action, и тем отложил исполнение js.

Откладывая исполнения js можно добиться замечательных результатов, особенно в тестах и SSR, которые многие куски кода вообще никогда запускать не будут, но на деле это не так чтобы прям просто. Иногда нам нужны side-effects, иногда разделить что-то на две части просто сложно.

В прошлом году меня зашеймили за react-focus-lock, что немного жирноват - 5kb min gz, и я придумал как это проблему решить. Теперь есть react-focus-lock/UI - 2kb, и "sideCar", который 3kb. Две части - UI и логика которая для SSR или неактивного lock не нужна.

Вроде как фигня, но react-focus-on, который focus+scroll lock - все те же 2kb на UI, и уже 5kb на sidecar Плям идеальный кейс для size-limit: github.com/theKashey/reac… Как всегда есть статья с теоретическим обоснованием концепта: dev.to/thekashey/side…

🐢 Почему медленнее 🐢 Потому что "fetch-then-render". Если нарезать приложение на части, то для того чтобы только понять какую часть надо загрузить надо в начале загрузить все части "до" нее. Это формирует "волны", или "смерть через 1000 спинеров"

React.lazy и другие Suspense проблему не решают от слова совсем. По определению. Ну Suspense быть может(в concurrent mode) задержит показ нового экрана, но 🚀быстрее🚀 его не сделает. А нужно то именно "быстрее"!

Быстрее возможно только в одном случае - если мы начинаем загружать код как можно "раньше", чтобы побороть latency. Опять же - "render-as-you-fetch". Таким образом когда наступит время показа элемента - он уже будет загружен. Получится "быстрее"

Варианта тут 2 - первый это SSR. Просто отрендерите страницу на сервере, где все скрипты уже загружены, да и данные "близко", и можно сходить за ними пару раз. Это создаст как бы "инструкцию" того, что нужно сделать на клиенте, чтобы получить аналогичную картинку.

- второй это "умный" CSR. Например с использованием "плоского" раутинга, где можно заранее понять что будет отрендерено на странице, и сделать что-то типа prefetch. Почти никто так не делает 🤷‍♂️, хотя иногда встречаются прям супер навороченные решения github.com/Shopify/quilt/…

На "работе" у нас чистый SPA без SSR, так что там сидит "плоское" CSR решение, а после работы я мейтейню loadable-components и react-imported-components... да, одновременно. Вообще React code splitting немного хитрая чтука - работает просто на соплях. dev.to/thekashey/insi…

Основная проблема в SSR + Code Splitting хорошо показана в @stereobooster's react-snap - с сервера пришла красивая картинка - стартовал реакт и все похерил - загрузились lazy компоненты, опять все хорошо
notion image

Разные библиотеки решают это по разному: - async-components ручками рендерил приложение чтобы найти "себя" и создать "инструкции". Не пережил React 16. - react-loadable/loadable-components патчат код и лезут в webpack - imported-components добавляет сахар в imports.

Далее библиотеки или сами вставляют нужные скрипты в ответ (получается что они "знают" какие правильные), или повторяют те же imports на клиенте до старта приложения. В итоге почти все работает только с @webpack, и для @parceljs я знаю только одно решение - imported-components.

Проблема в том, что разрабочики "фреймворков" не так чтобы думают как их поделки будут использовать, или никто кроме них их использовать не может. Тот же React.lazy - не работает на сервере, от слова совсем. И на клиенте особо не помогает(нет prefetch)...

When discussing Concurrent Mode, we haven’t focused on server rendering as much. However, it’s an essential part of the picture! For example, it enables Progressive Hydration — making individual React components interactive before all JS loads. Demo: codesandbox.io/s/floral-worke… pic.twitter.com/7QfC4g3JIK
Решение? Partial hydration, который у Фейсбука то есть, а у нас еще нет. Вот у @dan_abramov это работает хорошо из коробки twitter.com/dan_abramov/st…

У @_developit тоже работает - github.com/GoogleChromeLa…, но не хорошо И у меня работает, тоже не так чтобы, но уж как пару лет - medium.com/@antonkorzunov… (никто не пользуется)

Самое главное, на что я до сих пор, ни смотря на весь мой опыт, напарываюсь... CODESPLITTING МОЖЕТ СДЕЛАТЬ ТОЛЬКО ХУЖЕ! Или вы можете добавить кучу imports в свой код, разбить бандл на кучу файлов, и для показа любой страницы все еще потребуются почти! все! файлы!

Codesplitting это не про imports, и не про Lazy - это про контроль зависимостей. Про то как ваше приложение живет и дышит. Не только про то "где" резать, но и про "когда". В том числе как и когда собирать обратно.