Возрождение простых сайтов. Статика, 0kB JS, ничего лишнего

в 8:00, , рубрики: docker, Elder.js, FirstDEDIC, firstvds, Go, go-bindata, HTTPServ, Hugo, MPA, mTCP, Qwik, Redbean, Service Workers, SourceHut, SPA, WebP, Блог компании FirstVDS, браузеры, Разработка веб-сайтов, хостинг
Возрождение простых сайтов. Статика, 0kB JS, ничего лишнего - 1
Как мы обсуждали в прошлый раз, удручающее ожирение сайтов и софта вернуло моду на простые, маленькие проекты. И сейчас происходит своеобразный ренессанс веба 90-х, вплоть до стиля Geocities (такой был бесплатный хостинг) и веб-страниц в виде PDF. Таковы примеры самореализации. У каждого человека — уникальный сайт, который отличается от остальных и отражает его личность.

Статический сайт можно выполнить в одном файле HTML, а динамический — в одном бинарнике (под катом). Тенденция видна везде. Современные фреймворки даже хвалятся «0кБ JavaScript» по дефолту, а браузеры внедрили технические усовершенствования, которые во многом аннулируют преимущества использования SPA.

Сайт в одном бинарнике

Сайт в исполняемом файле — ещё более интересная идея, чем в одном HTML, о котором мы рассказывали в прошлый раз. Если в двух словах, на сервере постоянно запущена программа на Go, которая отслеживает входящие запросы и выдаёт эмбеды изнутри себя.

В каком-то смысле это полная противоположность сайту в одном файле HTML, потому что в данном случае у нас формально не статичные странички, а динамически генерируемый контент. Более того, это полноценная система CI/CD, если так можно сказать, потому что программа постоянно обновляется из репозитория, куда удобно коммитить.

Скрипт для деплоя на сервере:

 #!/bin/sh -eu
#
# deploy my website

cd /home/j3s/code/j3s.sh
git fetch origin
if git status | grep -q behind; then
git merge origin/main
go build
mv j3s.sh /usr/local/bin/j3s.sh
service j3s.sh restart
fi

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

Такую систему внедрил американский разработчик Джес Олсон (Jes Olson) для своего личного сайта-бинарника j3s.sh. Он руководствовался несколькими принципами, которые можно назвать универсальными.

  1. Простой, понятный код.
  2. Быстрая разработка (логично вытекает из первого пункта), простая итерабельность.
  3. Надёжность и долговечность.
  4. Минимальные усилия в поддержке. При этом максимальная простота в устранении неполадок. Один человек способен тривиально поддерживать все системы без чужой помощи. В идеале вещи должны быть настолько простыми и надёжными, что будут ломаться крайне редко. Если какая-то неприятность всё же случилась, способ исправления проблемы должен быть очевидным.

Автор начал по классике: попробовал генераторы статических сайтов типа Hugo, а также Ghost, Jekyll, SourceHut + tarball и редактирование html вручную, но ни один вариант его не удовлетворил. Даже самая очевидная связка из генератора Hugo и хостинга на Github Pages ему не понравилась из-за множества зависимостей от конкретных программ, компаний, проектов и экосистем.

Он написал странички на старом добром HTML — и захостил на машине, которая находится под его контролем (кстати, Джес работает в известной компании, которая занимается хостингом). Казалось бы, в любой момент можно открыть vim и отредактировать любую страничку, что мгновенно отразится на сайте.

В этот момент пришла идея: если сайт написан на одном языке, то его может генерировать один бинарник. В данном случае на Go, хотя такой же бинарник можно написать на Rust, на C или любом другом языке. Суть одна и та же: это динамически обновляемый генератор статических страничек.

В этом есть свои преимущества. Например, чтобы посмотреть IP-адрес посетителя, достаточно написать функцию в четыре строчки:

 func ipHandler(w http.ResponseWriter, r *http.Request) {
    w.Header().Set("Content-Type", "text/plain")
    fmt.Fprintf(w, r.Header.Get("X-Forwarded-For")+"n")
}

В случае необходимости можно вызвать эту функцию в любой момент:

 http.HandleFunc("/ip", ipHandler)

То есть автор отказался от генератора Hugo, а вместо него написал свою программу Go, которая постоянно работает на сервере, принимает соединения — и выдаёт клиентам контент в нужном формате по их запросам. Все статические файлы переносятся внутрь бинарника, например, с помощью пакета go-bindata или stdlib embed.

Вы можете запустить программу на любом сервере — и вот ваш «сайт» автоматически переехал на новый хостинг. Если пофантазировать, то каждый пользователь может запускать «сайт» на своём компьютере, генерировать и просматривать контент локально, каждый сам у себя (как делает Redbean).

Генератор статических HTML и CSS типа Hugo Свой бинарник
Зависимости Обновления Hugo, плагины, Docker (если зафиксировать текущую версию Hugo в контейнере) Linux, стандартная библиотека Go
Долговечность стека текущих зависимостей (прогноз) 10 лет 20–30 лет

Возрождение простых сайтов. Статика, 0kB JS, ничего лишнего - 2

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

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

Минимализм становится стандартом

Выше был упомянут SourceHut — это набор опенсорсных инструментов для веб-разработки, без использования JavaScript, без трекинга и прочих излишеств. Только самые быстрые и лёгкие инструменты. Набор создан в мае 2022 года специально по запросам общественности (сейчас в публичной альфе).

Современные фреймворки типа Astro, Qwik и Elder.js тоже по умолчанию начали предлагать опцию «0кБ JavaScript». Таким образом, баланс постепенно смещается от тяжеловесных SPA в сторону радикально оптимизированных MPA без JS, с бюджетом 30–50 кБ.

Возрождение простых сайтов. Статика, 0kB JS, ничего лишнего - 3

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

Что касается изображений, мы получаем чистый выигрыш места за счёт простой конвертации старых JPEG и PNG в более современный формат, такой как WebP, AVIF или JPEG XL. Из этих трёх форматов только WebP поддерживается во всех браузерах (кроме IE 11). Его можно использовать смело, в то время как AVIF пока не реализовали в Safari, а самый продвинутый JPEG XL вообще нигде не поддерживается. То есть JPEG XL — это пока формат не для веба, а для личного фотоархива.

Простое преобразование из PNG в WebP одной командой convert в ImageMagick иногда даёт результат, особенно если фотографии занимают основную часть места на хостинге, как часто бывает в статических сайтах.

По степени сжатия WebP с потерями выигрывает у обычного JPEG примерно 10%:

Возрождение простых сайтов. Статика, 0kB JS, ничего лишнего - 4

Cжатие без потерь в WebP тоже вне конкуренции (там используется отдельный кодек):

Возрождение простых сайтов. Статика, 0kB JS, ничего лишнего - 5

Апгрейд браузеров убивает SPA

В последние годы в браузерах реализован ряд технических усовершенствований, которые аннулируют главные преимущества использования одностраничных приложений (SPA) вместо нормальных многостраничных сайтов (MPA):

  1. Chrome реализовал удержание заливки (paint holding) — больше нет «белой вспышки» при переходе между страницами (Safari сделал это в 2019 г).
  2. Chrome внедрил кэширование «назад-вперёд» (back-forward caching) — теперь эта оптимизация реализована во всех основных браузерах, так что навигация вперёд-назад между страницами MPA стала практически мгновенной.
  3. Service Workers — когда-то экспериментальная функция теперь стала практически на 100% доступной во всех браузерах. Она позволяет внедрить автономную навигацию по страницам без маршрутизации на стороне клиента (и связанных с этим сложностей).
  4. Если Shared Element Transitions будут приняты и реализованы в браузерах, появится возможность анимировать переходы между страницами MPA, что ранее было возможно (хотя и сложно) только в SPA.

Если вас это не убедило отказаться от толстых одностраничных приложений, оцените такой аргумент от «защитников SPA».

Возможно, в будущем SPA останутся только в специфических нишах, где без них действительно не обойтись. Например, если хотите сохранить на экране определённый объект (окно плеера или виджет чата) во время навигации по сайту. Это модная концепция transitional apps.

Похоже, что SPA повторяют судьбу библиотеки jQuery. Она предоставляла уникальные и удобные API, которые не поддерживались в браузерах. Но когда браузеры реализовали стандартно querySelector, fetch и другие вызовы, необходимость использования jQuery стала уже не такой очевидной.

Крошечный образ Docker для раздачи статики

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

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

thttpd -D -h 0.0.0.0 -p 3000 -d /static-website -u static-user -l - -M 60

Затем нашёл маленький дистрибутив Alpine, в состав которого уже входит thttpd. Получился докер-образ 7,77 МБ.

Затем использовал докеровский инструмент scratch для минификации образа. Эта программа оставляет внутри только ядро и скрипт для загрузки всего необходимого, после запуска скачивает и устанавливает Alpine (130 МБ), берёт снаружи файлы веб-сайта — и начинает работать:

FROM alpine:3.13.2 AS builder

ARG THTTPD_VERSION=2.29

# Install all dependencies required for compiling thttpd
RUN apk add gcc musl-dev make

# Download thttpd sources
RUN wget http://www.acme.com/software/thttpd/thttpd-${THTTPD_VERSION}.tar.gz 
&& tar xzf thttpd-${THTTPD_VERSION}.tar.gz 
&& mv /thttpd-${THTTPD_VERSION} /thttpd

# Compile thttpd to a static binary which we can copy around
RUN cd /thttpd 
&& ./configure 
&& make CCOPT='-O2 -s -static' thttpd

# Create a non-root user to own the files and run our server
RUN adduser -D static

# Switch to the scratch image
FROM scratch

EXPOSE 3000

# Copy over the user
COPY --from=builder /etc/passwd /etc/passwd

# Copy the thttpd static binary
COPY --from=builder /thttpd/thttpd /

# Use our non-root user
USER static
WORKDIR /home/static

# Copy the static website
# Use the .dockerignore file to control what ends up inside the image!
COPY . .

# Run thttpd
CMD ["/thttpd", "-D", "-h", "0.0.0.0", "-p", "3000", "-d", "/home/static", "-u", "static", "-l", "-", "-M", "60"]

В итоге сам образ занимает минимальный размер:

> docker images | grep static
static latest ab0699ed2690 About a minute ago 186kB

Всего 186 КБ. Плюс файлы веб-сайта, вот и всё, что нужно носить с собой.

Код лежит на docker-static-website.

Веб-сервер с нашими файлами внутри бинарника

Не менее элегантный трюк — взять веб-сервер Redbean и внедрить внутрь бинарника свои статические файлы. В результате получится файл .com, который нативно запускается под следующими системами: Linux, Mac, Windows, FreeBSD, OpenBSD, NetBSD и BIOS. То есть этот веб-сервер работает на любом компьютере и выдаёт ваш контент в офлайне. Автор программы Redbean и кросс-платформенного формата αcτµαlly pδrταblε εxεcµταblε — известная хакерша Джастин Танни.

Минимализм и сжигание лишних слоёв абстракции — похвальная тенденция не только во фронтенде, но вообще в любом софте. Например, в десктопных ОС ожирение вообще приобрело хроническую форму. Разработчики добавляют новые функции, а не убирают лишние. Почти никто не ставит главной целью разработки увеличение производительности за счёт удаления кода.

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

Как вариант, для древнего железа можно использовать старые версии программ. Например, веб-сервер mTCP (HTTPServ) для старых IBM PC раздаёт сайт serentty.com (копия) с сервера 386 SX 25 МГц и 4 МБ оперативки по каналу 38400 бод:

 HTTP/1.1 200 OK
Server: mTCP HTTPServ Mar 7 2020
Date: Sun, 17 Apr 2022 00:30:00 GMT
Content-Type: text/html
Content-Length: 5355
Expires: Fri, 14 Oct 2022 00:30:00 GMT
Last-Modified: Sun, 17 Apr 2022 00:02:00 GMT
Connection: keep-alive

Хотя сегодня опять упадёт, наверное…

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


НЛО прилетело и оставило здесь промокод для читателей нашего блога:

15% на все тарифы VDS (кроме тарифа Прогрев) — HABRFIRSTVDS.

Автор:
FirstJohn

Источник

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js