Вы когда-нибудь задумывались, как Flipkart или Facebook могут очень быстро загружать веб-страницу? Этот вопрос всегда приходит в голову. Как мне улучшить свой сайт, чтобы он загружался так же быстро, как они. Это заставило меня исследовать то, о чем нужно позаботиться при разработке вашего сайта. Здесь, в AR Wealth Team, мы реализовали следующие вещи.

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

Тема, которую нужно осветить.

  1. Модель оболочки приложения
  2. Оптимизируйте свой JavaScript
  3. Оптимизируйте свой CSS

Модель оболочки приложения

Эта модель представляет собой способ создания PWA (прогрессивного веб-приложения), которое мгновенно загружает экран пользователя, используя минимум HTML, CSS и JavaScript, необходимых для работы пользовательского интерфейса, и при кэшировании в автономном режиме может обеспечить мгновенную, надежную и хорошую производительность.

Для одностраничных приложений с архитектурой, насыщенной JavaScript, оболочка приложения является подходящим подходом. Этот подход основан на агрессивном кэшировании оболочки (с использованием сервис-воркера) для запуска приложения. Далее, динамическое содержимое загружается для каждой страницы с использованием JavaScript.

Пример HTML для оболочки приложения

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

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

Ваш index.html файл в вашем рабочем каталоге должен выглядеть примерно так, как показано ниже. Это подмножество фактического содержимого, а не полный индексный файл. Посмотрим, что в нем содержится.

  • HTML и CSS для «скелета» вашего пользовательского интерфейса с заполнителями для навигации и содержимого.
  • Внешний файл JavaScript (app.js) для обработки навигации и логики пользовательского интерфейса, а также код для отображения сообщений, полученных с сервера, и их локального хранения с использованием механизма хранения, такого как IndexedDB.
  • Манифест веб-приложения и загрузчик работника службы для включения автономных возможностей.
<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <title>App Shell</title>
  <link rel="manifest" href="/manifest.json">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <link rel="stylesheet" type="text/css" href="styles/inline.css">
</head>

<body>
  <header class="header">
    <h1 class="header__title">App Shell</h1>
  </header>

  <nav class="nav">
  ...
  </nav>

  <main class="main">
  ...
  </main>

  <div class="dialog-container">
  ...
  </div>

  <div class="loader">
    <!-- Show a spinner or placeholders for content -->
  </div>

  <script src="app.js" async></script>
  <script>
  if ('serviceWorker' in navigator) {
    navigator.serviceWorker.register('/sw.js').then(function(registration) {
      // Registration was successful
      console.log('ServiceWorker registration successful with scope: ', registration.scope);
    }).catch(function(err) {
      // registration failed :(
      console.log('ServiceWorker registration failed: ', err);
    });
  }
  </script>
</body>
</html>

Кеширование оболочки приложения

Оболочку приложения можно кэшировать с помощью написанного вручную сервис-воркера или сгенерированного сервис-воркера с помощью инструмента предварительного кэширования статических ресурсов, такого как sw-precache.

Ниже приведен пример кода сервисного воркера, который кэширует статические ресурсы из оболочки приложения в Cache API с помощью события сервисного воркера install:

var cacheName = 'shell-content';
var filesToCache = [
  '/css/styles.css',
  '/js/scripts.js',
  '/images/logo.svg',

  '/offline.html',

  '/',
];

self.addEventListener('install', function(e) {
  console.log('[ServiceWorker] Install');
  e.waitUntil(
    caches.open(cacheName).then(function(cache) {
      console.log('[ServiceWorker] Caching app shell');
      return cache.addAll(filesToCache);
    })
  );
}); 

Оптимизируйте свой JavaScript

  1. Предварительно загрузите самые важные ресурсы
  2. Отрисовка первоначального маршрута как можно скорее
  3. Предварительно кешируйте оставшиеся объекты
  4. Ленивая загрузка других маршрутов и некритических ресурсов

Предварительно загрузите критически важный ресурс

Preload - это декларативный запрос на выборку, который сообщает браузеру, что нужно как можно скорее запросить ресурс. Предварительно загрузите важные ресурсы, добавив тег <link> с rel="preload" в заголовок вашего HTML-документа:

На моем сайте один из членов моей команды обнаружил, что шрифт, который занимал около 50 КБ, занимал больше времени для начальной загрузки. Он обнаружил, что путем предварительной загрузки шрифтов или любого важного ресурса, который необходим, можно предварительно загрузить до вызова события window.onLoad.

<link rel="preload" as="style" href="css/style.css">

мы только что добавили предварительную загрузку в наш файл index.html, который теперь помогает нам повысить производительность на 2%.

Браузер устанавливает более подходящий уровень приоритета для ресурса, чтобы попытаться загрузить его раньше, не задерживая событие window.onload.

Сделайте начальный маршрут как можно скорее

Чтобы улучшить First Paint, мы обнаружили, что встраивание критического JavaScript и откладывание остального с помощью async, а также встраивание критического CSS используется в верхней части страницы. Это повышает производительность за счет исключения циклических обращений к серверу для получения ресурсов, блокирующих рендеринг. Однако встроенный код сложнее поддерживать с точки зрения разработки, и браузер не может кэшировать его отдельно.

Другой подход к улучшению First Paint - это рендеринг исходного HTML вашей страницы на стороне сервера. Это немедленно отображает содержимое для пользователя, пока сценарии все еще выбираются, анализируются и выполняются. Однако это может значительно увеличить полезную нагрузку HTML-файла, что может повредить Time to Interactive или время, необходимое для того, чтобы ваше приложение стало интерактивным и могло реагировать на ввод пользователя.

Не существует единого правильного решения для уменьшения количества First Paint в вашем приложении, и вам следует рассматривать встраивание стилей и рендеринг на стороне сервера только в том случае, если преимущества перевешивают компромиссы для вашего приложения.

На нашем сайте мы откладываем неприоритетный ресурс, используя ключевое слово async в index.html, которое использовалось для аналитических целей.

Вот фрагмент кода для того же.

<script async defer>
 window.dataLayer = window.dataLayer || [];
 function gtag() {
 dataLayer.push(arguments);
 }
 gtag(‘js’, new Date());
gtag(‘config’, ‘UA-148031339–1’);
 </script>

Предварительное кеширование ресурсов

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

Используйте стороннюю библиотеку, чтобы упростить процесс создания сервис-воркера, если у вас нет более сложных требований к кэшированию, чем то, что может предоставить библиотека. Например, Workbox предоставляет набор инструментов, которые позволяют создавать и поддерживать сервис-воркера для кэширования ресурсов. Для получения дополнительной информации о сервис-воркерах и автономной надежности обратитесь к руководству сервис-воркера в схеме обучения надежности.

Ленивая загрузка

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

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

Популярные пакеты модулей, такие как webpack, Parcel и Rollup, позволяют разделять пакеты с помощью динамического импорта. Например, рассмотрим следующий фрагмент кода, который показывает пример метода someFunction, который запускается при отправке формы.

import moduleA from "library";

form.addEventListener("submit", e => {
  e.preventDefault();
  someFunction();
});

const someFunction = () => {
  // uses moduleA
}

Здесь someFunction использует модуль, импортированный из определенной библиотеки. Если этот модуль не используется где-либо еще, блок кода можно изменить, чтобы использовать динамический импорт, чтобы получить его только тогда, когда форма отправлена ​​пользователем.

form.addEventListener("submit", e => {
  e.preventDefault();
  import('library.moduleA')
    .then(module => module.default) // using the default export
    .then(someFunction())
    .catch(handleError());
});

const someFunction = () => {
    // uses moduleA
}

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

Тем не менее, есть возможные способы оптимизации вашего кода javascript, которые будут скрыты в следующем сеансе.

Теперь переходим к

Оптимизируйте свой CSS

1 Отложите некритичный CSS
2 Уменьшите CSS

Отложите некритичный CSS

Файлы CSS являются ресурсами, блокирующими рендеринг: они должны быть загружены и обработаны до того, как браузер отобразит страницу. Отображение веб-страниц, содержащих излишне большие стили, занимает больше времени.

Возьмем пример. загрузите этот следующий URL-адрес в свой браузер



Эта страница запрашивает файл CSS с восемью классами, но не все из них необходимы для отображения «видимого» содержимого.

Цель этого руководства - оптимизировать эту страницу, чтобы синхронно загружались только критические стили, а остальные (например, применяемые к абзацам) загружались неблокирующим образом.

Запустите lighout на странице и проверьте вкладку производительности в chrome dev tool.

Отчет показывает метрику First Contentful Paint со значением «1 с» и возможность Устранить ресурсы, блокирующие рендеринг, указывая на файл style.css:

Чтобы визуализировать, как этот CSS блокирует рендеринг:

  1. Откройте страницу в Chrome.
  2. Нажмите Control+Shift+J (или Command+Option+J на Mac), чтобы открыть DevTools.
  3. Щелкните вкладку «Производительность».
  4. На панели «Производительность» нажмите «Обновить».

На полученной кривой вы увидите, что маркер FCP размещается сразу после завершения загрузки CSS:

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

Оптимизировать

Чтобы оптимизировать эту страницу, вам нужно знать, какие классы считаются «критическими». Для этого вы воспользуетесь инструментом покрытия:

  1. В DevTools откройте меню команд, нажав Control+Shift+P или Command+Shift+P (Mac).
  2. Введите «Покрытие» и выберите «Показать покрытие».
  3. Нажмите кнопку «Обновить», чтобы перезагрузить страницу и начать сбор информации.

Дважды щелкните отчет, чтобы увидеть классы, отмеченные двумя цветами:

  • Зеленый (критический): это классы, необходимые браузеру для отображения видимого содержимого (например, заголовка, подзаголовка и кнопок-гармошек).
  • Красный (некритичный): эти стили применяются к контенту, который не отображается сразу (например, абзацам внутри аккордеонов).
  • С помощью этой информации оптимизируйте свой CSS, чтобы браузер начинал обрабатывать критические стили сразу после загрузки страницы, а некритический CSS откладывал на потом:
<style type="text/css">
.accordion-btn {background-color: #ADD8E6;color: #444;cursor: pointer;padding: 18px;width: 100%;border: none;text-align: left;outline: none;font-size: 15px;transition: 0.4s;}.container {padding: 0 18px;display: none;background-color: white;overflow: hidden;}h1 {word-spacing: 5px;color: blue;font-weight: bold;text-align: center;}
</style>
  • Затем асинхронно загрузите остальные классы, применив следующий шаблон:
<link rel="preload" href="styles.css" as="style" onload="this.onload=null;this.rel='stylesheet'">
<noscript><link rel="stylesheet" href="styles.css"></noscript>
  • link rel="preload" as="style" запрашивает таблицу стилей асинхронно. Вы можете узнать больше о preload в руководстве «Предварительная загрузка критических ресурсов».
  • Атрибут onload в link позволяет обрабатывать CSS после завершения загрузки.
  • «Обнуление» обработчика onload после его использования помогает некоторым браузерам избежать повторного вызова обработчика при переключении атрибута rel.
  • Ссылка на таблицу стилей внутри элемента noscript работает как резервный вариант для браузеров, которые не выполняют JavaScript.

Теперь вы можете снова проверить работоспособность.

Маркер FCP появляется перед тем, как страница запрашивает CSS, что означает, что браузеру не нужно ждать загрузки CSS перед визуализацией страницы:

Сократите CSS:

Файлы CSS могут содержать ненужные символы, такие как комментарии, пробелы и отступы. В производственной среде эти символы можно безопасно удалить, чтобы уменьшить размер файла, не влияя на то, как браузер обрабатывает стили. Этот прием называется минификацией.

Минификацию можно сделать с помощью webpack

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

  • Mini-css-extract-plugin извлечет каждую таблицу стилей в отдельный файл в качестве одного из шагов процесса сборки.
  • Webpack-fix-style-only-entries используется для исправления проблемы в wepback 4, чтобы избежать создания дополнительного файла JS для каждого файла CSS, указанного в webpack-config.js.

Вам необходимо внести изменения в ваш файл webpack.

откройте файл webpack.config.js и внесите следующие изменения:

const OptimizeCSSAssetsPlugin = require("optimize-css-assets-webpack-plugin");

Затем передайте экземпляр плагина в массив плагинов:

plugins: [
    new HtmlWebpackPlugin({template: "./src/index.html"}),
    new MiniCssExtractPlugin({filename: "[name].css"}),
    new FixStyleOnlyEntriesPlugin(),
    new OptimizeCSSAssetsPlugin({})
  ]

добавив следующие изменения, вы можете проверить размер файла css на вкладке сети. Проверьте размер файла перед внесением изменений в веб-пакет и проверьте размер после внесения изменений в файл веб-пакета.

Как видите, размер файла Css уменьшится примерно на несколько процентов.

Чтобы узнать больше, дайте мне знать в разделе комментариев :)

Идентификатор Github-mitulshah96
Идентификатор Twitter- @MitulJShah

Рад служить вам.