Создание легкого SaaS CMS-решения Optimizely с помощью 11ty

Современная веб-разработка часто требует соблюдения сложного баланса между производительностью сайта и гибкостью, необходимой редакторам контента. Чтобы решить эту проблему, я и мой коллега из Optimizely OMVP Грэм Карр приступила к проверке концепции (POC) для создания решения, в котором приоритет отдается скорости и простоте, при этом полностью используя мощные возможности Optimizely SaaS CMS.

Задача: производительность против предварительного просмотра

Наш фронтенд-архитектор/ведущий специалист, Аллин Томаспоставили четкую задачу: найти решение с минимальным раздуванием, которое генерируется статически, но при этом полностью совместимо с возможностями предварительного просмотра CMS. Хотя такие платформы, как Next.js, популярны, они часто содержат значительный объем клиентского JavaScript (гидратации), который не всегда необходим для сайтов с большим количеством контента. Нам хотелось чего-то более компактного.

Целью было создать сайт, который:

  • Статически созданный: для невероятно быстрой загрузки и безопасности.
  • Легкий: Избегание «налога на гидратацию» более тяжелых фреймворков.
  • Удобный для редактора: разрешение редакторам просматривать предварительный просмотр своего контента в Optimizely Visual Builder перед публикацией.

Решение: 11ty соответствует Optimize SaaS CMS

Мы выбрали 11ти (Одиннадцать) в качестве нашего генератора статических сайтов (SSG). 11ty известен своей простотой и гибкостью. В отличие от других фреймворков, которые заставляют вас использовать конкретную клиентскую архитектуру, 11ty дает вам полный контроль над выходными данными. Он генерирует чистый HTML, а это именно то, что нам нужно, чтобы свести раздувание к минимуму.

Чтобы связать 11ty с Optimizely, мы использовали Оптимизировать SaaS CMS и JS SDK для контента.

Оптимизация SaaS CMS и графика контента

Наше решение основано на Optimizely SaaS CMS в качестве автономного хранилища контента. Мы используем Оптимизировать график контентавысокопроизводительный API GraphQL для получения контента. Это позволяет нам запрашивать именно то, что нам нужно, и тогда, когда нам это нужно, сохраняя эффективность процесса сборки.

Контент JS SDK

JS SDK для контента был важен для нашей архитектуры. Это позволило нам определять наши модели контента непосредственно в коде, используя типобезопасный шаблон построения.

Например, определение «Блока кнопок» становится простым определением TypeScript:

import { contentType } from '@optimizely/cms-sdk';

export const ButtonBlock = contentType({
  key: 'ButtonBlock',
  baseType: '_block',
  displayName: 'Button Block',
  properties: {
    Text: { type: 'string', displayName: 'Text' },
    Url: { type: 'url', displayName: 'Url' },
  },
});

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

Read more:  Лучшее мнение BS: Создание власти, искусства и равенства в 2025 году | Специальные мнения

Структурировано для масштабируемости

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

Папка моделей

Наш каталог src/models является источником истины для всех определений контента. Мы организовали его так, чтобы отразить различные типы контента в CMS:

  • источник/модели/страницы/: определения для полных страниц (например, ArticlePage, LandingPage).
  • источник/модели/опыт/: типы контента на основе композиции, используемые в Visual Builder.
  • источник/модели/компоненты/: повторно используемые блоки контента (например, ButtonBlock, TextBlock).

Библиотека атомарных компонентов

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

Наш каталог src/comComponents структурирован следующим образом:

  • атомы/: основные строительные блоки, такие как кнопки, поля ввода и текстовые блоки.
  • молекулы/: группы атомов, работающих вместе (например, форма поиска).
  • организмы/: сложные разделы пользовательского интерфейса, такие как верхние и нижние колонтитулы и главные баннеры.
  • шаблоны/: макеты на уровне страницы, объединяющие все вместе.

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

Динамическая маршрутизация с 11-кратной нумерацией страниц

Одной из проблем генератора статических сайтов является сопоставление динамического контента из CMS со статическими маршрутами. 11ty элегантно справляется с этим благодаря своей нумерация страниц особенность. Мы рассматриваем весь наш репозиторий контента как единый набор данных с разбивкой на страницы, где каждый элемент становится страницей.

Сначала мы извлекаем весь маршрутизируемый контент в файл глобальных данных src/_data/routes.js:

// src/_data/routes.js

module.exports = async function () {
  // ... client setup ...

  // Fetch all content items that have a URL
  const query = getRoutePagesQuery(blockFragments);
  const data = await client.request(query);

  // Filter out items without a default URL
  const pages = data._Content.items.filter(item =>
    item._metadata &&
    item._metadata.url &&
    item._metadata.url.default
  );

  return pages;
};

Затем мы создаем один шаблон src/pages.11ty.ts, который обрабатывает эти данные. Установив размер: 1, 11ty генерирует отдельный HTML-файл для каждого элемента массива маршрутов. Функция постоянной ссылки гарантирует, что файл будет сохранен по правильному пути, определенному в CMS.

// src/pages.11ty.ts

export const data = {
  pagination: {
    data: 'routes',
    size: 1,
    alias: 'contentItem',
    addAllPagesToCollections: true,
  },
  layout: 'base.11ty.ts',
  permalink: (data: any) => {
    const item = data.pagination.items[0];
    return item._metadata.url.default;
  },
  // ...
};

export function render(data: any): string {
  const item = data.pagination.items[0];
  return ComponentFactory(item);
}

Этот шаблон невероятно мощный. Это означает, что нам не нужно вручную настраивать маршруты или создавать отдельные шаблоны для каждого типа страниц. Когда редакторы добавляют новые страницы в Optimizely, 11ty автоматически обнаруживает их и создает соответствующие статические страницы.

Фабрика компонентов

Чтобы обрабатывать разнообразные типы контента, возвращаемые CMS, мы реализовали Фабрика компонентов. Эта функция действует как диспетчер, проверяя __typename или тип контента каждого элемента и отображая соответствующий компонент.

// src/components/ComponentFactory.ts

export function ComponentFactory(content: ContentItem): string {
  // Handle CompositionComponentNode wrapper
  if (content.component) {
    return ComponentFactory(content.component);
  }

  const types = content._metadata?.types || [];
  const typename = content.__typename || '';

  // Dynamic component lookup from registry
  const ComponentView = getView(typename);
  if (ComponentView) {
    return ComponentView(content);
  }

  // Fallback for unknown types
  return `
    

${content.Title || content._metadata?.displayName || 'Unknown'}

Unknown component type: ${typename}

`; }

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

Read more:  Ведущая цифровая больница открывает новые горизонты с помощью Doctolib и решений на базе искусственного интеллекта: E-HEALTH-COM

Включение предварительного просмотра в реальном времени

Самой важной частью этого POC было обеспечение того, чтобы статичность 11ty не мешала редактированию. Мы реализовали специальный Сервер экспресс-просмотра который работает вместе с нашей сборкой 11ty.

Этот сервер действует как динамический мост между Optimizely Visual Builder и нашими статическими шаблонами.

Реализация экспресс-сервера

Мы выбрали Выражать за его надежность и простоту использования. Сервер выполняет несколько ключевых функций:

  1. HMAC-аутентификация: безопасно извлекает черновик контент с использованием токенов предварительного просмотра.
  2. Осведомленность о контексте: переключение между «Режимом редактирования» (вставка атрибутов data-epi-*) и «Режимом предварительного просмотра».
  3. Обновления в реальном времени: прослушивает веб-перехватчики для запуска 11ty перестроений при публикации контента.

Вот краткий обзор того, как мы обрабатываем запросы предварительного просмотра в src/preview/server.ts:

// src/preview/server.ts

app.get('/preview/:contentKey', async (req: Request, res: Response) => {
  const { contentKey } = req.params;
  const { preview_token, ctx } = req.query;

  try {
    // 1. Set context mode for Visual Builder (edit vs preview)
    const contextMode =
      ctx === 'edit' ? 'edit' :
      ctx === 'preview' ? 'preview' :
      null;
    setContextMode(contextMode);

    // 2. Set preview token to enable access to draft content
    if (preview_token && typeof preview_token === 'string') {
      previewClient.setPreviewToken(preview_token);
    }

    // 3. Fetch content dynamically
    const content = await previewClient.getContentByKey(contentKey);

    if (!content) {
      return res.status(404).send(renderNotFound(contentKey));
    }

    // 4. Render using the same shared templates as 11ty
    const html = renderContent(content);
    res.send(html);
  } catch (error) {
    res.status(500).send(renderError('Server Error', String(error)));
  }
});

Обработка вебхуков

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

// src/preview/server.ts

app.post('/webhook/content-published', (req: Request, res: Response) => {
  // ... validation ...

  // Debounce rebuilds to handle rapid updates efficiently
  if (rebuildTimeout) {
    clearTimeout(rebuildTimeout);
  }

  rebuildTimeout = setTimeout(() => {
    triggerRebuild();
  }, REBUILD_DEBOUNCE_MS);

  res.json({ status: 'ok', message: 'Rebuild scheduled' });
});

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

Read more:  Банки ужесточают процесс адаптации с помощью видео KYC, эксперты объясняют, почему | Личные финансы

Почему 11ty вместо Next.js?

Хотя Next.js — фантастический фреймворк, для этого конкретного случая использования 11ty предлагает явные преимущества:

  • Нулевой клиентский JS по умолчанию: 11ty не предполагает, что вам нужно одностраничное приложение (SPA). Он обслуживает HTML. Если вам нужен JavaScript, вы добавляете его. Это приводит к значительному уменьшению размеров пакетов и сокращению времени взаимодействия (TTI).
  • Скорость сборки: 11ty невероятно быстро создает статические страницы, что имеет решающее значение при масштабировании до тысяч страниц.
  • Простота: Кривая обучения более плавная, и об архитектуре легче рассуждать. Нет сложной логики гидратации, которую нужно отлаживать.

Заключение

Этот POC продемонстрировал, что вам не нужна тяжелая среда JavaScript для создания современного, динамичного и удобного для редактирования веб-сайта с помощью Optimizely SaaS CMS. Объединив мощь и простоту 11ty с надежными API-интерфейсами Optimizely, мы создали решение, которое понравится как разработчикам, так и редакторам контента.

Мы достигли лучшего из обоих миров: производительность статического сайта с возможностями динамического редактирования приложения на базе CMS.

Посмотреть окончательный результат можно здесь: 11ty-netcel-saas.netlify.app

Полный исходный код доступен на GitHub: github.com/MineshS/11ty (В настоящее время это частный репозиторий, но, пожалуйста, свяжитесь с именем пользователя Git, чтобы можно было добавить)

3 декабря 2025 г.

Читайте также

Leave a Comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.