предисловие
Эта статьяРендеринг в Интернете: влияние архитектуры приложений на производительность (Google I/O ’19)Это краткое изложение выступления инженеров Google, посвященного оптимизации современной системы архитектуры приложений. В докладе представлены следующие методы оптимизации:
- предварительный рендеринг
- изоморфный рендеринг
- Потоковая визуализация
- прогрессивная закачка воды(очень волнующе)
Система архитектуры приложений
Когда мы обсуждаем «архитектуру приложения», ее можно понимать как создание веб-сайта путем объединения следующих частей.
-
Component model
компонентная модель. -
Rendering and loading
визуализировать и загрузить. -
Routing and transitions
Маршрутизация и переход. -
Data/state management
Управление данными и состоянием.
Представление
Прежде чем анализировать производительность рендеринга страницы, давайте взглянем на некоторые из наиболее важных показателей, чтобы облегчить следующее понимание:
-
FP
: First Paint, часть Paint Timing API, является промежуточным звеном между навигацией по страницам и браузером, который отображает первый пиксель этой веб-страницы на экране. Рендеринг — это все, что отличается от того, что было на экране до входа в навигацию по веб-странице. -
FCP
: First Contentful Paint, первый рендеринг с контентом — это когда браузер рендерит первую часть контента в DOM и в первый раз сообщает пользователю, что страница загружается. -
TTI
: Время до интерактивности Время первого взаимодействия, когда пользователь может фактически инициировать событие элемента DOM и взаимодействовать со страницей. -
FID
: задержка первого ввода. Задержка первого ввода измеряет время с момента, когда пользователь впервые взаимодействует с вашим сайтом (т. е. когда он щелкает ссылку, кнопку или использует настраиваемый элемент управления на основе JavaScript) до момента, когда браузер фактически может ответить на этот интерактив. -
TTFB
: Время до первого байта Время первого байта, как следует из названия, относится ко времени с момента, когда клиент начинает взаимодействовать с сервером, до момента, когда сервер начинает передавать данные в браузер клиента (включая DNS, подключение к сокету и запросы). время отклика), что может отражать важный показатель скорости отклика сервера.
Ничего страшного, если вы еще не знакомы с этими метриками.В следующих разделах мы проанализируем эти метрики на реальных примерах использования.
Стоимость рендеринга
Рендеринг на стороне клиента
Получение HTML, CSS и JavaScript с сервера требует затрат. Взяв в качестве примера веб-сайт CSR (рендеринг на стороне клиента), веб-сайт для рендеринга на стороне клиента использует библиотеку фреймворка (пакет) и приложение (приложение) для начального рендеринга. Предполагая, что у него есть 1 МБ кода JavaScript Bundle, пользователь не сможет увидеть страницу, пока этот фрагмент кода не будет загружен и выполнен.
Его структура в целом выглядит следующим образом:
Проанализируйте его процесс:
- Пользователь вводит URL-адрес для входа на веб-сайт и извлекает ресурс HTML.
- Пакет, загруженный тегом script, находится в ресурсе HTML и еще раз инициирует запрос на получение пакета. В это время он также находится в показателях статистики производительности.
FP
Заканчивать.
На этом этапе страница в основном бессмысленна.Конечно, вы также можете разместить несколько статических скелетных экранов или подсказок при загрузке, чтобы напомнить пользователям.
- После того, как пакет JavaScript загружен и выполнен, страница фактически отображает значимый контент. вести переписку
FCP
Заканчивать.
Когда фреймворк добавляет различные привязки событий к узлам DOM, пользователь действительно может взаимодействовать со страницей, а соответствующиеTTI
Заканчивать.
этонедостатокДело в том, что пользователь не видит ничего значимого, пока не будет выполнена вся зависимость JavaScript.
Изоморфный рендеринг на стороне сервера SSR с Hydration
Основываясь на вышеупомянутых недостатках рендеринга на стороне клиента и потребностях пользователей в расширенном взаимодействии приложений CSR, родилась комбинация SSR и CSR.Производительность, SEO, сбор данныхПреимущества и "изюминка"изоморфный рендеринг", проще говоря:
-
Для первого запроса сервер использует возможности рендеринга на стороне сервера, предоставляемые фреймворком, для прямого запроса данных на месте и создания html-страницы с полным содержимым.Пользователь может видеть контент, не дожидаясь загрузки js фреймворка. .
-
После рендеринга страницы используйте возможности Hydration, предоставляемые фреймворком, чтобы вернуть «сухое» событие регистрации HTML, возвращенное сервером, и т. Д., И иметь множество инцидентов, он имеет то же самое, что и традиционный CSR.Взаимодействует красочный клиент.
В однородном приложении, пока возвращается HTML-страница, пользователь может видеть красочную страницу:
После загрузки JavaScript пользователь может взаимодействовать с содержимым (например, щелкнуть, чтобы увеличить, перейти на страницу и т. д.).
сравнение кода
Код типичного приложения CSR React выглядит так:
Код SSR требует сотрудничества сервера,
сначала через серверReactDOMServer.renderToString
Сериализуйте компонент в html-строку на стороне сервера и верните его во внешний интерфейс:
проход переднего концаhydrate
Закачка воды делает функциональное взаимодействие полным:
То же самое верно и для SSR Vue:
Изоморфные дефекты
Насколько идеально изоморфное приложение? Конечно нет На самом деле обычные изоморфные приложения только улучшают FCP, то есть скорость, с которой пользователи могут видеть контент, но при этом еще нужно дождаться завершения загрузки кода фреймворка.hydrate
После того, как закачка воды завершена и серия процессов завершена, реальнаяИнтерактивный.
и дляFID
Другими словами, для индикатора «Задержка первого ввода», поскольку SSR отображает контент быстро, пользователям легче ошибочно полагать, что страница уже находится в интерактивном состоянии. Вместо этого «пользователь щелкнет в первый раз». - браузер реагирует на событие". время становится больше.
Поэтому изоморфные приложения, скорее всего, станут «мечом о двух концах».
Ниже мы обсудим некоторые варианты.
Предварительный рендеринг Предварительный рендеринг.
Для контента, который меняется нечасто, использование предварительного рендеринга — хороший способ генерировать статические HTML-страницы с помощью возможностей фреймворка во время сборки кода, а не повторно генерировать страницы, когда пользователи запрашивают страницы, такие как изоморфные приложения. немедленно.
Конечно, у него также есть большие ограничения:
- Применяется только к статическим страницам.
- URL-адреса, которые необходимо предварительно отобразить, должны быть перечислены заранее.
Потоковая передача
Потоковая визуализация позволяет серверу отправлять большие фрагменты контента, так что клиенту не нужно получать полный HTML-код, а он начинает визуализацию, когда получает первую часть, что значительно улучшаетTTFB
Время первого байта.
В React это можно сделать с помощьюrenderToNodeStream
Использовать потоковую рендеринг:
Прогрессивная гидратация
мы знаемhydrate
Процесс обхода всего дерева узлов React для добавления событий, который должен занимать много времени, когда страница очень большая, можем ли мы выполнить «впрыск воды» только в ключевые части, такие как видимые части в представлении». сначала сделать эту часть интерактивной?
Представьте его особенности:
- Постепенное заполнение водой на уровне компонентов.
- Сервер по-прежнему отображает всю страницу.
- Страницы могут «запускать» компоненты путем сегментирования в зависимости от приоритета.
Через движущееся изображение, чтобы интуитивно почувствовать разницу между обычным впрыском воды (слева) и прогрессивным впрыском воды (справа):
Видно, что время, когда пользователь может взаимодействовать в первый раз, значительно увеличилось.
Давайте просто поговорим об этом и не будем этого делать. Давайте посмотрим на код, который использует React для выполнения этой функции. Во-первых, нам нужно подготовить компонентHydrator
используется для реализации, когда компонентПосле попадания в поле зренияСнова вода.
Во-первых, давайте взглянем на общую структуру приложения:
let load = () => import('./stream');
let Hydrator = ClientHydrator;
if (typeof window === 'undefined') {
Hydrator = ServerHydrator;
load = () => require('./stream');
}
export default function App() {
return (
<div id="app">
<Header />
<Intro />
<Hydrator load={load} />
</div>
);
}
В зависимости от среды клиента и сервера используйте разныеHydrator
, на стороне сервера возвращается обычный HTML-текст:
function interopDefault(mod) {
return (mod && mod.default) || mod;
}
export function ServerHydrator({ load, ...props }) {
const Child = interopDefault(load());
return (
<section>
<Child {...props} />
</section>
);
}
Клиент, с другой стороны, должен реализовать ключевые части прогрессивной закачки воды:
export class Hydrator extends React.Component {
render() {
return (
<section
ref={c => (this.root = c)}
dangerouslySetInnerHTML={{ __html: '' }}
suppressHydrationWarning
/>
);
}
}
Первая часть рендера, используйтеdangerouslySetInnerHTML
Чтобы сделать эту часть инициализированной как пустой html-текст, и поскольку серверная сторона должна по-прежнему отображать содержимое полностью, как обычно, а клиентской стороне не нужно ничего делать из-за инициализации, это приведет к тому, что React внутренне «контент на стороне сервера и клиент- побочный контент «Проверка непротиворечивости» не удался.
при использованииdangerouslySetInnerHTML
функции, которые больше не будут удерживать Reacthydrate
траверсchildren
Вместо этого HTML-код, возвращаемый рендерингом на стороне сервера, используется напрямую, чтобы гарантировать, что стиль, отображаемый до впрыска воды, также в порядке.
Повторное использованиеsuppressHydrationWarning
Подавить предупреждение React о неудачных проверках согласованности содержимого.
export class Hydrator extends React.Component {
componentDidMount() {
new IntersectionObserver(async ([entry], obs) => {
if (!entry.isIntersecting) return;
obs.unobserve(this.root);
const { load, ...props } = this.props;
const Child = interopDefault(await load());
ReactDOM.hydrate(<Child {...props} />, this.root);
}).observe(this.root);
}
render() {
return (
<section
ref={c => (this.root = c)}
dangerouslySetInnerHTML={{ __html: '' }}
suppressHydrationWarning
/>
);
}
}
Далее, когда компонент инициализируется на стороне клиента, он используетIntersectionObserver
Следите за тем, входит ли компонентный элемент в вид.После того, как он входит в вид, он будет динамически переходить в вид.import
компоненты и использоватьReactDOM.hydrate
Прийти к реальной впрыску воды.
В настоящее время не только впрыск воды является динамическим, но также происходит загрузка кода компонента, когда компонент входит в представление, что действительно обеспечивает «загрузку по требованию».
Когда в анимации появляется фиолетовая анимация, это означает прогрессивныйhydrate
законченный.
Сравнивая производительность полной закачки воды и дополнительной закачки воды, мы обнаружим, что время первого взаимодействия значительно увеличивается:
Конечно, мы понимаем принцип и обнаруживаем, что можно не только войти в представление, слушая компонентhydrate
, даже поhover
,click
Подождите, пока время не сработает, и гибко настройте его в соответствии с различными потребностями бизнеса.
Вы можете перейти по URL-адресу на изображении, чтобы получить свои любимые статьи, связанные с фреймами в этой области:
Суммировать
В этой статье обобщаютсяРендеринг в Интернете: влияние архитектуры приложений на производительность (Google I/O ’19)Это замечательное выступление команды Google знакомит с методами оптимизации в современной системе архитектуры приложений, в том числе:
- предварительный рендеринг
- изоморфный рендеринг
- Потоковая визуализация
- прогрессивная закачка воды
Выбор соответствующего метода оптимизации в различных бизнес-сценариях — необходимый навык для отличного фронтенд-инженера, я думаю, вы кое-что приобретете, прочитав эту статью.
Демонстрационный адрес этой статьи:GitHub.com/Google CHROM…
Обратите внимание на общественный номер»Передняя часть от продвинутого до допуска», получить 4DРасширенный интерфейсный расширенный маршрут,Алгоритм внешнего интерфейса, основанный на нуле, для решения выбранных проблем, Я также буду часто делиться некоторыми интересными вещами, с которыми я сталкиваюсь в работе и жизни, а также заводить друзей и общаться со всеми.