CSI, SSI, ESI в композиции микрофронтендов

2 года назад·1 мин. на чтение

Что такое Client Side Include, Server Side Include и Edge Side Include?

Client Side Include - CSI (Включение на стороне клиента)

Client Side Include (CSI) является довольно знакомой для фронтенд-разработчиков возможностью, в основном это вызов Ajax. Что выделяет его, так это контент, который возвращает запрос. Тип контента - html/text, похожий на обычный HTML, который мы используем. В этом контексте мы не запрашиваем фрагмент данных, вместо этого мы ожидаем фрагмент HTML.
<body>
  Hello World
  <footer>
    <h-include src="a server address"></h-include>
  </footer>
</body>
Реализация CSI не нова, CSI используется еще с 2000 года. Проблема CSI заключается в том, что если вы используете этот подход слишком интенсивно, пользователь сайта может ждать контента во время загрузки сайта. Это означает, что поисковая система может быть не в состоянии проиндексировать нужный контент при первой загрузке. По этой причине рекомендуется использовать этот подход только для определенных элементов, таких как счетчик, футер или что-либо, что не привлекает основное внимание пользователя и не находится в верхней части экрана.

Server Side Include - SSI (Включение на стороне сервера)

Как видно из названия, включение на стороне сервера (SSI) не происходит на компьютере клиента.
<body>
  Hello World
  <!--#include virtual="/..." -->
</body>
Вместо этого, когда сервер анализирует HTML-файл, он проверяет определенные строки, помеченные #include. Для любых найденных включений он извлечет содержимое и вставит его в это место, прежде чем вернуть окончательный документ. Преимущество SSI заключается в том, что, как правило, серверы работают быстрее по сравнению с клиентским компьютером. Не говоря уже о том, что количество запросов между клиентами и серверами сокращается до 1. Таким образом, обычно мы можем получить время отклика около 1 мс по сравнению с временем отклика 50 мс при вызове API. При этом не стоит злоупотреблять использованием большого количества включений на одной страницу. Для каждого включения серверу необходимо выполнить несколько вызовов и дождаться завершения всех вызовов, прежде чем он сможет агрегировать файл. Это может занять больше времени, чем вы ожидаете, не говоря уже о том, что один из вызовов может завершиться сбоем. Таким образом время отклика может вырасти до 500 мс и больше. Любое время, выходящее за рамки 1 сек, может стать критичным для продакшен сервера. Поэтому разумно использовать SSI только для основного содержимого страницы, а не для каждого декоративного элемента (например счетчик или футер и т.д.). Также можно попробовать комбинировать CSI и SSI. SSI также не новая технология. Во времена, когда существовали только бэкенд-разработчики, каждый фрагмент HTML-шаблона, собранный на сервере, можно было назвать SSI.

Edge Side Include - ESI

Идея включения на Edge Side (на стороне CDN), была предложена еще в 2001 году, но с тех пор она так и не стала общепринятой. Тем не менее, она может решить проблемы SSI. Имейте в виду, что ESI происходит и на сервере.
<body>
  Hello World
  <esi:include src="a server address" />
</body>
С точки зрения использования, это не слишком отличается от включения SSI. Считается, что в некоторых реализациях ESI это улучшает время до первого байта (TTFB). Потому что он может сначала вернуть доступный документ, прежде чем все включения будут скачаны и возвращены. Это похоже на комбинацию CSI и SSI, за исключением того, что все обрабатывается на сервере.

Итоги

Существует множество подходов для реализации микрофронтендов, в том числе CSI, SSI и ESI. Эти подходы могут казаться запутанными, отчасти потому, что разработчики использовали их, не зная терминологии.

Кастомный React хук для изменения темы веб-приложения

2 года назад·2 мин. на чтение

Пишем кастомный React хук useTheme, который динамически меняет тему веб-приложения с помощью CSS переменных.

В этой статье напишем кастомный хук на ReactJS, который будет менять тему веб-приложения. Особенности хука useTheme, который мы реализуем:
  • использует CSS переменные
  • быстро кастомизируется
  • плавный переход между темами
  • умеет сохранять выбранную тему в local storage

Код хука useTheme

Сам хук выглядит довольно просто.
// hooks/use-theme.js

import { useLayoutEffect, useState } from 'react'

const isDarkTheme = window?.matchMedia('(prefers-color-scheme: dark)').matches
const defaultTheme = isDarkTheme ? 'dark' : 'light'

export const useTheme = () => {
  const [theme, setTheme] = useState(
    localStorage.getItem('app-theme') || defaultTheme
  )

  useLayoutEffect(() => {
    document.documentElement.setAttribute('data-theme', theme)
    localStorage.setItem('app-theme', theme)
  }, [theme])

  return { theme, setTheme }
}
prefers-color-scheme используется для определения выбранной пользователем темы (светлая или темная). Пользователь указывает свои предпочтения через настройку операционной системы или через настройку user agent. document.documentElement.setAttribute('data-theme', theme) - этой строчкой мы добавляем кастомный data-атрибут в тег html. Таким образом, например, при выборе светлой темы в теге html появится data-атрибут data-theme="light".

Добавление CSS переменных

Далее мы должны указать CSS переменные, которые будут иметь различные значение для разных тем. Например, --button-text-color имеет значение #ffffff при темной теме и #252525 - при светлой.
/* index.css */

html[data-theme='dark'] {
  --button-text-color: #ffffff;
  --button-background-color: #4e005c;
  --button-border-color: #ba8fc2;

  --background-color: #292929;
  --icon-color: #ba8fc2;
}

html[data-theme='light'] {
  --button-text-color: #252525;
  --button-background-color: #f9d4ff;
  --button-border-color: #4e005c;

  --background-color: #dfdfdf;
  --icon-color: #4e005c;
}

Применение CSS переменных

Далее нам нужно применить эти CSS переменные к соответствующим элементам. Добавление transition дает плавность при переключении тем.
/* App.css */

.app__container {
  background-color: var(--background-color);
  transition: background-color 200ms linear;
}

.app__logo {
  color: var(--icon-color);
  transition: color 500ms linear;
}

.app__button,
.app__button:hover,
.app__button:focus,
.app__button:active,
.app__button:not(:disabled):not(.disabled):active {
  color: var(--button-text-color);
  background-color: var(--button-background-color);
  border-color: var(--button-border-color);
  transition: color 500ms linear, background-color 500ms linear,
    border-color 500ms linear;
}

Использование хука useTheme

Здесь добавлены две кнопки, каждая из которых отвечает за переключение на светлую или темную тему. Они обернуты в ButtonGroup. Элементы логотип, контейнер и кнопка имеют соответствующие CSS классы, которые мы описали выше - app__logo, app__container и app__button. В этих классах были использованы CSS переменные.
import React from 'react'

import Button from 'react-bootstrap/Button'
import ButtonGroup from 'react-bootstrap/ButtonGroup'

import { FaCoffee } from 'react-icons/fa'
import { useTheme } from './hooks/use-theme'

import './App.css'

export default function App() {
  const { theme, setTheme } = useTheme()

  const handleLightThemeClick = () => {
    setTheme('light')
  }
  const handleDarkThemeClick = () => {
    setTheme('dark')
  }

  return (
    <div className="app__container w-100 h-100 d-flex flex-column">
      
      <div className="p-3 d-flex justify-content-end">
        <ButtonGroup aria-label="Theme toggle">
          <Button variant="secondary" onClick={handleLightThemeClick}>
            Light
          </Button>
          <Button variant="secondary" onClick={handleDarkThemeClick}>
            Dark
          </Button>
        </ButtonGroup>
      </div>

      <div className="flex-grow-1 d-flex flex-column justify-content-center align-items-center">
        <FaCoffee size={100} className="app__logo mb-5" />
        <div className="d-flex">
          <Button className="app__button" type="button">
            Subscribe
          </Button>
        </div>
      </div>
    </div>
  )
}
Исходный код