JAMstack. Современный способ создания статических сайтов

Хотите, используя свой любимый веб-фреймворк, создавать невероятно быстрые сайты c поддержкой последних рекомендаций SEO, PWA и доступности интерфейса, не написав ни строчки серверного кода? Добро пожаловать под кат!

JAMstack

JAM = JavaScript + APIs + Markup

Несмотря на название, это не набор инструментов, а общий подход к проектированию приложений, который состоит из трёх компонентов:

  • JavaScript – язык программирования, нужный нам для разработки браузерного интерактива. Для совсем простых сайтов можно обойтись и без него

  • APIs – источники данных, из которых будут генерироваться статические страницы. В качестве источника может выступать что угодно: базы данных, CMS или git-репозиторий

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

Архитектура

архитектура JAM

В качестве бэкенда используются облачные сервисы. Вся разработка сводится к проектированию шаблонов для SSG, которые вместе с данными размещаются в git-репозитории. Репозиторий подключается к сервису непрерывной доставки (CD). Cервис CD отслеживает изменения в исходном коде, запускает генератор и размещает полученные страницы в сети доставки контента (CDN). Разработчки обновляют сайт, отправляя коммиты в репозиторий напрямую, а нетехнические специалисты используют облачную CMS, также подключенную к хранилищу.

Преимущества использования JAMstack

Высокая скорость загрузки и отказоустойчивость

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

Безопасность

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

Масштабируемость и низкие расходы на поддержку

За счёт использования CDN обеспечивается стабильное быстрое время отклика и высокий аптайм. А лёгкая интеграция git-репозитория с сервисами непрерывной интеграции и доставки (CI/CD) позволяет эффективно автоматизировать процессы разработки, тестирования, развертывания и масштабирования приложения.

Ограничения использования JAMstack

Медленное обновление контента

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

Загрузка динамических данных

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

При большом количестве динамического контента лучше использовать традиционные подходы с серверным рендерингом

Применение

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

Пример разработки блога с использованием Gatsby, Netlify, NetlifyCMS и GitHub

Данный гайд предполагает, что вы уже знакомы с программной платформой NodeJS, консольным клиентом git и библиотекой React. А также имеете аккаунт на GitHub.

Существует огромное количество средств для создания JAM приложений. В этом примере будет использоваться следующий стек:

  • Gatsby – генератор статических сайтов из React + GraphQL приложений с открытым исходным кодом. С одной стороны он имеет удобную систему плагинов, которая позволяет разрабатывать сайты с минимальными затратами по времени, с другой – очень гибкую настройку

  • NetlifyCMS – открытая система управления контентом, которая отделяет слой данных от слоя отображения (headless CMS). Имеет удобную интеграцию с git-репозиториями

  • Netlify – условно-бесплатная облачная платформа, предоставляющая удобные инструменты для автоматического развертывания проекта и собственную CDN

Для хранения контента и исходного кода будет использоваться GitHub.

Создаем блог с помощью Gatsby

Устанавливаем gatsby с помощью пакетного менеджера npm:

npm i -g gatsby-cli

Создаем новый проект:

gatsby new gatsby-blog-example

Переходим в папку:

cd gatsby-blog-example

Созданный проект имеет следующую структуру:

/
|-- /src
|-- /pages
|-- /images
|-- /components
|-- gatsby-config.js
|-- gatsby-node.js
|-- gatsby-ssr.js
|-- gatsby-browser.js

Подробнее о файлах конфигурации в Gatsby:

  • gatsby-config.js — конфигурация сайта, включающая различные метаданные проекта (заголовок, описание, автора и.т.д) и подключённые плагины

  • gatsby-node.js — используется для изменения логики сборки проекта

  • gatsby-browser.js — используется для подключения сторонних модулей, специфичных для браузера (импортирование css и полифилов, настройка стратегии кеширования)

  • gatsby-ssr.js — используется для подключения сторонних модулей, специфичных для серверного рендеринга React-приложения

По умолчанию Gatsby создает страницы из файлов в папке src/pages.

Запускаем локальный сервер для разработки:

gatsby develop

Вы можете просмотреть сайт по адресу http://localhost:8000/.

шаблон сайта

Вам также доступен браузерный обозреватель для GraphQL (http://localhost:8000/___graphql), где вы можете просмотреть данные и схемы запросов, которые используются при рендере страниц.

обозреватель graphQL

Посты для блога мы будем хранить в разметке markdown. Давайте создадим отдельную папку для записей:

mkdir content

И добавим туда файл first_post.md со следующим содержимым:

---
path: '/blog/my-first-post'
date: '2019-12-09'
title: 'My first blog post'
---
# Hello world!
this is my first blog post in markdown.

В начале записи мы объявляем набор переменных с помощью синтаксиса yaml, который называется frontmatter.

Для того, что бы Gatsby автоматически создавал страницы из markdown файлов и распознавал данные из frontmatter, нам необходимо использовать два плагина:

gatsby-source-filesystem уже включён в стартовый шаблон, так что нам остается поставить только второй плагин:

npm i gatsby-transformer-remark

Подключаем плагины в gatsby-config.js:

plugins: [
{
resolve: `gatsby-source-filesystem`,
options: {
name: `blog`,
path: `${__dirname}/content`,
},
},
`gatsby-transformer-remark`,
...
]

Перезапускаем дев-сервер. Перейдя в обозреватель graphQL мы увидим, что нам стали доступны новые данные. Сформировав тестовый запрос, получим валидный ответ от сервера разработки.

тестовый запрос

У нас получилось добавить новый источник данных для нашего генератора! Осталось только отобразить контент на сайте.

В первую очередь создадим шаблон записи блога в файле src/templates/blogPost.js:

import React from 'react'
import { graphql } from 'gatsby'
import Layout from '../components/layout'
export default function Template({
data, // это свойтсво передается из GraphQL запроса, который описан ниже.
}) {
const { markdownRemark } = data // data.markdownRemark содержит информацию о записи
const { frontmatter, html } = markdownRemark
return (
<Layout>
<h1>{frontmatter.title}</h1>
<h2>{frontmatter.date}</h2>
<div dangerouslySetInnerHTML={{ __html: html }} />
</Layout>
)
}
export const pageQuery = graphql`
query($path: String!) {
markdownRemark(frontmatter: { path: { eq: $path } }) {
html
frontmatter {
date(formatString: "MMMM DD, YYYY")
path
title
}
}
}
`

И опишем, как будет происходить рендер постов в gatsby-node.js. Для этого используем интерфейс createPages, описанный в спецификации Gatsby Node APIs:

const path = require(`path`)
exports.createPages = async ({ actions, graphql, reporter }) => {
const { createPage } = actions
const blogPostTemplate = path.resolve(`src/templates/blogTemplate.js`)
const result = await graphql(`
{
allMarkdownRemark(
sort: { order: DESC, fields: [frontmatter___date] }
limit: 1000
) {
edges {
node {
frontmatter {
path
}
}
}
}
}
`)
// Отлавливаем ошибки
if (result.errors) {
reporter.panicOnBuild(`Error while running GraphQL query.`)
return
}
result.data.allMarkdownRemark.edges.forEach(({ node }) => {
createPage({
path: node.frontmatter.path,
component: blogPostTemplate,
})
})
}

После перезапуска сервера разработки наша запись будет доступна по адресу http://localhost:8000/blog/first-post/.

Запись блога на сайте

Самое время написать второй пост! Создадим файл second-post.md в папке content:

---
path: '/blog/second-post'
date: '2019-12-09'
title: 'My second blog post'
---
this is my second amazing post.

И в заключение, добавим список всех постов на главную страницу, отредактировав файл src/pages/index.js:

import React from 'react'
import { Link, graphql } from 'gatsby'
import Layout from '../components/layout'
import SEO from '../components/seo'
const IndexPage = ({ data }) => {
const posts = data.allMarkdownRemark.edges
return (
<Layout>
<SEO title="Home" />
<h1>Hi people</h1>
<p>Welcome to your new Gatsby site.</p>
<p>Now go build something great.</p>
<h2>Blog Posts</h2>
<ul>
{posts.map(({ node: post }) => (
<li key={`${post.frontmatter.title}`}>
<Link to={post.frontmatter.path}>{post.frontmatter.title}</Link>
<br />
<small>{post.frontmatter.date}</small>
</li>
))}
</ul>
<hr />
<Link to="/page-2/">Go to page 2</Link>
</Layout>
)
}
export default IndexPage
export const pageQuery = graphql`
query MyQuery {
allMarkdownRemark {
edges {
node {
frontmatter {
date
path
title
}
}
}
}
}
`

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

Главная страница блога

Очистим кеш следующей командой:

gatsby clean

И отрендерим всю статику:

gatsby build

Результат работы SSG сохраняется в папку public.

Наш блог готов к публикации в сети!

Публикуем проект с помощью GitHub и Netlify

Многие ip адреса Netlify были заблокированы на территории РФ во время борьбы РКН с telegram. У вас могут возникнуть проблемы с доступом как к сервису CD, так и CDN.

Создадим на GitHub пустой репозиторий gatsby-blog-example и загрузим туда наш код:

git add .
git commit -m "init"
git remote add origin https://github.com/[Ваш аккаунт]/gatsby-blog-example.git
git push -u origin master

Переходим на Netlify и регистрируемся с помощью аккаунта Github. После успешной регистрации вы попадёте в панель управления CD.

Панель управления Netlify

Добавление нового сайта разбита на три простых шага:

Сначала мы выбираем необходимый git провайдер. В нашем примере это GitHub.

Выбор провайдера

Затем подключаем репозиторий.

Выбор репозитория

Последним шагом задаём параметры сборки. Netlify по умолчанию отслеживает изменения в master ветке репозитория. В качестве команды сборки указываем gatsby build, а в качестве пути к статике выбираем папку public.

Параметры

Во время сборки Netlify загружает исходный код из Github, запускает Gatsby и разворачивает проект в собственной CDN, регистрируя случайно сгенерированный домен и SSL сертификат. В настройках вы можете поменять домен на свой, а так же ознакомиться с логами процесса CD в режиме реального времени.

Сборка проекта

Спустя непродолжительное время, ваш блог будет доступен всем пользователям сети. Данный пример располагается по адресу https://reverent-hodgkin-05f070.netlify.com/.

Успешный деплой

Когда вы отправляете коммит в master, Netlify автоматически пересобирает сайт.

Подключаем NetlifyCMS

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

Для начала нам понадобится добавить в проект пару новых зависимостей:

  • netlify-cms-app – Панель управления контентом с статичных сайтов, представляющая из себя одностраничное React приложение, работающее на стороне клиента

  • gatsby-plugin-netlify-cms – Плагин Gatsby, добавляющий netlify-cms-app в сборку генератора

npm i netlify-cms-app gatsby-plugin-netlify-cms

И подключим плагин в gatsby-config.js:

plugins: [
...
`gatsby-plugin-netlify-cms`
]

Cоздадим файл config.yml в папке static/admin со следующим содержимым:

backend:
name: git-gateway
branch: master
media_folder: static/img
public_folder: /img
collections:
- name: "blog"
label: "Blog"
folder: "content"
create: true
editor:
preview: false
fields:
- { label: "Path", name: "path", widget: "string" }
- { label: "Title", name: "title", widget: "string" }
- { label: "Publish Date", name: "date", widget: "string" }
- { label: "Body", name: "body", widget: "markdown" }

Отправим наши изменения на GitHub:

git add .
git commit -m "add NetlifyCMS"
git push

Настроим авторизацию для админской панели:

Открываем панель управления Netlify. Переходим в Site Settings > Identity, и активируем Identity service.

Активация Netlify Identity

По умолчанию регистрация в CMS открыта. Настроим её так, что бы авторы регистрировались по приглашению.

Настройка регистрации

Переходим во вкладку Identity основной панели Netlify и добавляем пользователя по почте.

Добавление нового пользователя

На указанную почту придёт приглашение. Перейдя по ссылке регистрируемся и получаем доступ к панели CMS.

Панель CMS

CMS доступна по URL /admin. Теперь у нас есть возможность взаимодействовать с контентом через браузер.

Ссылки

Исходный код примера

Пример блога, опубликованный на Netlify