Создание легкого 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:  Знаете ли вы, почему цвет флагов ближневосточных стран идентичен?

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

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

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

Наш каталог 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:  Alnylam представляет прогресс в трансформации лечения сердечно -сосудистых заболеваний с помощью RNAI Therapeutics на Конгрессе Европейского общества кардиологии 2025

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

Самой важной частью этого 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:  Лейбористское правительство Австралии отправляет ракеты, военные самолеты и войска для участия в незаконной войне с Ираном

Почему 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.