Как веб-архитекторы оптимизируют производительность?

оптимизация производительности React.js

предисловие

Эта статьяРендеринг в Интернете: влияние архитектуры приложений на производительность (Google I/O ’19)Это краткое изложение выступления инженеров Google, посвященного оптимизации современной системы архитектуры приложений. В докладе представлены следующие методы оптимизации:

  • предварительный рендеринг
  • изоморфный рендеринг
  • Потоковая визуализация
  • прогрессивная закачка воды(очень волнующе)

Система архитектуры приложений

Когда мы обсуждаем «архитектуру приложения», ее можно понимать как создание веб-сайта путем объединения следующих частей.

  1. Component modelкомпонентная модель.
  2. Rendering and loadingвизуализировать и загрузить.
  3. Routing and transitionsМаршрутизация и переход.
  4. Data/state managementУправление данными и состоянием.

image

Представление

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

  1. FP: First Paint, часть Paint Timing API, является промежуточным звеном между навигацией по страницам и браузером, который отображает первый пиксель этой веб-страницы на экране. Рендеринг — это все, что отличается от того, что было на экране до входа в навигацию по веб-странице.

  2. FCP: First Contentful Paint, первый рендеринг с контентом — это когда браузер рендерит первую часть контента в DOM и в первый раз сообщает пользователю, что страница загружается.

  3. TTI: Время до интерактивности Время первого взаимодействия, когда пользователь может фактически инициировать событие элемента DOM и взаимодействовать со страницей.

  4. FID: задержка первого ввода. Задержка первого ввода измеряет время с момента, когда пользователь впервые взаимодействует с вашим сайтом (т. е. когда он щелкает ссылку, кнопку или использует настраиваемый элемент управления на основе JavaScript) до момента, когда браузер фактически может ответить на этот интерактив.

  5. TTFB: Время до первого байта Время первого байта, как следует из названия, относится ко времени с момента, когда клиент начинает взаимодействовать с сервером, до момента, когда сервер начинает передавать данные в браузер клиента (включая DNS, подключение к сокету и запросы). время отклика), что может отражать важный показатель скорости отклика сервера.

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

Стоимость рендеринга

Рендеринг на стороне клиента

Получение HTML, CSS и JavaScript с сервера требует затрат. Взяв в качестве примера веб-сайт CSR (рендеринг на стороне клиента), веб-сайт для рендеринга на стороне клиента использует библиотеку фреймворка (пакет) и приложение (приложение) для начального рендеринга. Предполагая, что у него есть 1 МБ кода JavaScript Bundle, пользователь не сможет увидеть страницу, пока этот фрагмент кода не будет загружен и выполнен.

Его структура в целом выглядит следующим образом:

Проанализируйте его процесс:

  1. Пользователь вводит URL-адрес для входа на веб-сайт и извлекает ресурс HTML.

  1. Пакет, загруженный тегом script, находится в ресурсе HTML и еще раз инициирует запрос на получение пакета. В это время он также находится в показателях статистики производительности.FPЗаканчивать.

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

  1. После того, как пакет JavaScript загружен и выполнен, страница фактически отображает значимый контент. вести перепискуFCPЗаканчивать.

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

этонедостатокДело в том, что пользователь не видит ничего значимого, пока не будет выполнена вся зависимость JavaScript.

Изоморфный рендеринг на стороне сервера SSR с Hydration

Основываясь на вышеупомянутых недостатках рендеринга на стороне клиента и потребностях пользователей в расширенном взаимодействии приложений CSR, родилась комбинация SSR и CSR.Производительность, SEO, сбор данныхПреимущества и "изюминка"изоморфный рендеринг", проще говоря:

  1. Для первого запроса сервер использует возможности рендеринга на стороне сервера, предоставляемые фреймворком, для прямого запроса данных на месте и создания html-страницы с полным содержимым.Пользователь может видеть контент, не дожидаясь загрузки js фреймворка. .

  2. После рендеринга страницы используйте возможности Hydration, предоставляемые фреймворком, чтобы вернуть «сухое» событие регистрации HTML, возвращенное сервером, и т. Д., И иметь множество инцидентов, он имеет то же самое, что и традиционный CSR.Взаимодействует красочный клиент.

В однородном приложении, пока возвращается HTML-страница, пользователь может видеть красочную страницу:

После загрузки JavaScript пользователь может взаимодействовать с содержимым (например, щелкнуть, чтобы увеличить, перейти на страницу и т. д.).

сравнение кода

Код типичного приложения CSR React выглядит так:

Код SSR требует сотрудничества сервера,

сначала через серверReactDOMServer.renderToStringСериализуйте компонент в html-строку на стороне сервера и верните его во внешний интерфейс:

проход переднего концаhydrateЗакачка воды делает функциональное взаимодействие полным:

То же самое верно и для SSR Vue:

Изоморфные дефекты

Насколько идеально изоморфное приложение? Конечно нет На самом деле обычные изоморфные приложения только улучшают FCP, то есть скорость, с которой пользователи могут видеть контент, но при этом еще нужно дождаться завершения загрузки кода фреймворка.hydrateПосле того, как закачка воды завершена и серия процессов завершена, реальнаяИнтерактивный.

и дляFIDДругими словами, для индикатора «Задержка первого ввода», поскольку SSR отображает контент быстро, пользователям легче ошибочно полагать, что страница уже находится в интерактивном состоянии. Вместо этого «пользователь щелкнет в первый раз». - браузер реагирует на событие". время становится больше.

Поэтому изоморфные приложения, скорее всего, станут «мечом о двух концах».

Ниже мы обсудим некоторые варианты.

Предварительный рендеринг Предварительный рендеринг.

Для контента, который меняется нечасто, использование предварительного рендеринга — хороший способ генерировать статические HTML-страницы с помощью возможностей фреймворка во время сборки кода, а не повторно генерировать страницы, когда пользователи запрашивают страницы, такие как изоморфные приложения. немедленно.

Конечно, у него также есть большие ограничения:

  1. Применяется только к статическим страницам.
  2. URL-адреса, которые необходимо предварительно отобразить, должны быть перечислены заранее.

Потоковая передача

Потоковая визуализация позволяет серверу отправлять большие фрагменты контента, так что клиенту не нужно получать полный HTML-код, а он начинает визуализацию, когда получает первую часть, что значительно улучшаетTTFBВремя первого байта.

В React это можно сделать с помощьюrenderToNodeStreamИспользовать потоковую рендеринг:

Прогрессивная гидратация

мы знаемhydrateПроцесс обхода всего дерева узлов React для добавления событий, который должен занимать много времени, когда страница очень большая, можем ли мы выполнить «впрыск воды» только в ключевые части, такие как видимые части в представлении». сначала сделать эту часть интерактивной?

Представьте его особенности:

  1. Постепенное заполнение водой на уровне компонентов.
  2. Сервер по-прежнему отображает всю страницу.
  3. Страницы могут «запускать» компоненты путем сегментирования в зависимости от приоритета.

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

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

Давайте просто поговорим об этом и не будем этого делать. Давайте посмотрим на код, который использует 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Расширенный интерфейсный расширенный маршрут,Алгоритм внешнего интерфейса, основанный на нуле, для решения выбранных проблем, Я также буду часто делиться некоторыми интересными вещами, с которыми я сталкиваюсь в работе и жизни, а также заводить друзей и общаться со всеми.