Component-Driven Development

Component-Driven Development

  • 7 августа
  • читать 20 мин
Владимир Шайтан
Владимир Шайтан Technical Lead в Zoot, Преподаватель Компьютерной школы Hillel.

Когда-то я услышал слова, что компоненты — это новые примитивы нашего времени. И знаете что я думаю на этот счёт? Так и есть.

В правильном, идеальном мире весь код разбит на компоненты, ну или на то, на что разбить можно всё максимально автономное, пригодное для переиспользования и удобное. Представляете, так можно.

Реальность немного другая, но когда-нибудь будет и так, я верю в это (почти).

Итак, компоненты.

Компоненты — это основа всего. Это фундамент, кирпич, из которого строят дома (проекты). Разница только в том, что компоненты потом можно получить, отредактировать и поставить на место. Удобно? Удобно.

Сейчас весь мир пользуется вебсайтами или веб-приложениями (о разнице между ними можете почитать в одной из моих предыдущих статей), весь мир полагается на них, и бизнесы, и пользователи.

Компоненты — это та штука, которая помогает бизнесу масштабироваться, оптимизироваться, улучшаться.

ТАК ЩО Ж ТАКЕ CDD? 

Разработка, управляемая компонентами (Component-driven development) — это подход к разработке программного обеспечения, описывающий процесс создания модульных и повторно используемых компонентов, которые могут просто интегрироваться в разные части проекта. Короче говоря, на выходе это будут слабосвязанные независимые компоненты.

CDD имеет основные принципы, такие как модульность, возможность переиспользования, изоляция и тестирование.

Давайте отдельно рассмотрим каждый из них:

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

    Второе —может быть использована иерархическая структура компонентов, где одни компоненты содержат другие, что позволяет создавать сложные интерфейсы.
     
  • Повторное использование:
    Компоненты разрабатываются так, чтобы их без проблем можно было использовать в разных частях проекта или даже на других проектах. Также эти универсальные компоненты можно хранить в специальных библиотеках, что также ускоряет поиск нужных компонентов, когда их надо оттуда взять.
     
  • Изоляция:
    Компоненты разрабатываются таким образом, чтобы они могли работать независимо друг от друга, что упрощает моменты, когда нужно что-то пофиксить и когда нужно что-то обновить.
     
  • Тестирование:
    Изолированные компоненты легче тестировать (ожидаемо), потому что их можно проверить отдельно от остального проекта. На практике это ускоряет все процессы: от тестирования до довольного клиента.

Благодаря CDD компании могут закрывать такие задачи, как более быстрая разработка, разобщенные кодовые базы. Отдельные команды делают свои отдельные задачи или разрабатывают отдельные продукты. Если это крупная компания, отдельные кодовые базы позволяют этим командам разрабатывать и тестировать свои компоненты, не влияя и не задерживая работу другим командам.

Кроме того, хочу сказать на собственном опыте, что нам, как разработчикам, намного и намного легче разбираться в новом проекте на новой (или необязательно) работе, когда этот проект написан с помощью CDD. Я сам не раз это проходил, просто поверьте.

ЕСЛИ ВЫ ХОТИТЕ НАУЧИТЬСЯ

Сейчас есть множество платформ, где вы можете создавать, хранить и откуда можно брать компоненты для использования. Одна из самых популярных платформ — это Bit.

С помощью Bit разработчики могут создавать компоненты и делиться ими, документировать их, тестировать их, что упрощает сам процесс разработки тех универсальных компонентов. Также Bit поддерживает все известные технологии JavaScript, React, Angular, Vue.js и другие. То есть, проблем с этим в процессе создания и шеринга компонентов не будет.

ПРЕИМУЩЕСТВА И НЕДОСТАТКИ CDD

Среди явных преимуществ — легкая масштабируемость.

В контексте Component-Driven Development (CDD), масштабируемость означает способность системы, построенной на основе компонентов, эффективно расти и адаптироваться к увеличению сложности, объёму пользователей или функциональным требованиям.

То есть очень просто добавить просто новый компонент, связав его с нужными стариками, правда? И всё будет работать (если всё правильно сделать, конечно). Еще одним преимуществом является гибкость всей системы, где вы можете изменять, заменять и делать многое другое с каждым отдельным компонентом. Благодаря изоляции это не повлияет на весь проект так сильно, как могло бы.

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

Недостатки, само собой, тоже есть, куда без них. Начнём.

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

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

// alert.js

class Alert {
  constructor(message) {
    this.message = message;
    this.element = null;
  }

  create() {
    // Создаём сообщение
    this.element = document.createElement('div');
    this.element.className = 'alert';
    this.element.textContent = this.message;

    const closeButton = document.createElement('span');
    closeButton.className = 'alert-close';
    closeButton.textContent = '×';
    closeButton.addEventListener('click', () => this.close());

    this.element.appendChild(closeButton);

    return this.element;
  }

  close() {
    // Удаляем сообщения с DOM
    if (this.element) {
      this.element.remove();
      this.element = null;
    }
  }
}

export default Alert;

Как мы интегрируем этот компонент в другой компонент:

// main.js

import Alert from './alert.js';

// Создаём новое сообщение
const alert = new Alert('This is an alert message.');

// Добавляем сообщение в документ
document.body.appendChild(alert.create());

// Закрываем сообщение через некоторое время
setTimeout(() => {
  alert.close();
}, 5000);

Объясняю, почему весь код выше — эталонный в CDD.

  • Чётко определены границы компонента:
    - компонент Alert отвечает только за создание и закрытие сообщения,
    - имеет публичные методы create и close, которые чётко определяют как взаимодействовать с компонентом.
  • Минимальные зависимости:
    - компонент не имеет зависимостей от каких-либо других компонентов или внешних ресурсов.

Конечно, примером также может служить компонент кнопки, модального окна и т. д.

Также должен упомянуть, что JavaScript имеет нативную поддержку компонентов. Сейчас я подразумеваю вебкомпоненты.

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

  • Custom Elements (Пользовательские элементы) позволяют расширять HTML, создавая собственные элементы. Это позволяет определить новые HTML-теги с собственными функциями и поведением.
     
  • Shadow DOM (Теневой DOM) позволяет создавать инкапсулированные области в пределах HTML-элементов. Это обеспечивает изоляцию стилей и скриптов, что делает невозможным их влияние на остальной документ.
     
  • HTML Templates (HTML Шаблоны) позволяют определять шаблоны HTML, которые не рендерятся до того, как они не будут явно вставлены в DOM. Это полезно для создания повторно используемых элементов интерфейса.

Прилагаю небольшие примеры для каждого из пунктов:

Custom Elements:

// custom-element.js

class MyElement extends HTMLElement {
  constructor() {
    super();
    this.innerHTML = `<p>Hello from MyElement!</p>`;
  }
}

customElements.define('my-element', MyElement);

Использование:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Custom Element Example</title>
  <script src="custom-element.js" defer></script>
</head>
<body>
  <my-element></my-element>
</body>
</html>

Shadow DOM:

// shadow-dom.js

class ShadowElement extends HTMLElement {
  constructor() {
    super();
    this.attachShadow({ mode: 'open' });
    this.shadowRoot.innerHTML = `
      <style>
        p { color: red; }
      </style>
      <p>This is inside the Shadow DOM</p>
    `;
  }
}

customElements.define('shadow-element', ShadowElement);

Использование:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Shadow DOM Example</title>
  <script src="shadow-dom.js" defer></script>
</head>
<body>
  <shadow-element></shadow-element>
</body>
</html>

HTML Templates:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>HTML Template Example</title>
</head>
<body>
  <template id="my-template">
    <style>
      p { color: green; }
    </style>
    <p>This is a template example.</p>
  </template>

  <script>
    class TemplateElement extends HTMLElement {
      constructor() {
        super();
        const template = document.getElementById('my-template').content;
        this.attachShadow({ mode: 'open' }).appendChild(template.cloneNode(true));
      }
    }

    customElements.define('template-element', TemplateElement);
  </script>

  <template-element></template-element>
</body>
</html>

О ФРЕЙМВОРКАХ

Понятие композиции и декомпозиции — это очень важные понятия для любого фреймворка, и это также связано с CDD.

Объясняю:
Композиция и декомпозиция позволяют создавать масштабируемые и легко поддерживаемые приложения путём разбиения сложных систем на мелкие компоненты (декомпозиция) и объединение этих компонентов для создания более сложных систем (композиция).

Когда вы пишете код на React, Vue, Angular, вы постоянно этим пользуетесь, даже если ещё не знаете этого.

Рассмотрим эти принципы на примере без использования какого-либо конкретного фреймворка. Поняв, вы сможете без проблем написать это на любом используемом фреймворке, просто изменив синтаксис.

Декомпозиция:

// header.js
class HeaderComponent extends HTMLElement {
  constructor() {
    super();
    this.innerHTML = `<header>Header Content</header>`;
  }
}
customElements.define('header-component', HeaderComponent);

// footer.js
class FooterComponent extends HTMLElement {
  constructor() {
    super();
    this.innerHTML = `<footer>Footer Content</footer>`;
  }
}
customElements.define('footer-component', FooterComponent);

// main-content.js
class MainContentComponent extends HTMLElement {
  constructor() {
    super();
    this.innerHTML = `<main>Main Content</main>`;
  }
}
customElements.define('main-content-component', MainContentComponent);

Композиция:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Component Composition Example</title>
  <script src="header.js" defer></script>
  <script src="footer.js" defer></script>
  <script src="main-content.js" defer></script>
</head>
<body>
  <header-component></header-component>
  <main-content-component></main-content-component>
  <footer-component></footer-component>
</body>
</html>

В примере с декомпозицией мы создавали три независимых компонента, каждый из которых отвечает за свою часть интерфейса.

А вот в примере с композицией мы создали также три компонента, которые объединены в полный пользовательский интерфейс.

ВЫВОД

Мы с вами в этой статье только начали «заигрывать» с CDD. В общем это мощный инструмент для разработки программного обеспечения, у которого есть свои преимущества и недостатки, которые непременно следует учитывать.

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

На это понадобится, вероятно, большой бюджет, много усилий и часы и часы в гугле, высматривая, как же правильно всё писать :)

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

До встречи в следующей статье!

Рекомендуем публикации по теме