Статьи

Компонентный рабочий процесс для Angular 2

Недавно я написал твит о том, как я обычно создаю компоненты.

Отличный рабочий процесс для рефакторинга громоздких компонентов ? https://t.co/each4c2cm9

— Кент С. Доддс (@kentcdodds)
9 декабря 2015 г.

Твит довольно короткий, поэтому давайте более подробно рассмотрим упомянутый рабочий процесс. Также обратите внимание, что здесь я использую компоненты Angular 2, но это верно и во многих других контекстах, Angular 1.x, React и т. Д.

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

Мой рабочий процесс обычно состоит из:

  1. Создание компонента, содержащего этот огромный статический HTML-блок
  2. Идентификация многоразовых или автономных частей
  3. Извлечение этих частей в отдельные компоненты
  4. Итерация, вернитесь к шагу 2.

1. Большой компонент

Итак, на шаге 1 вы получите что-то вроде этого:

@Component({
  selector: 'app-main',
  template: `
    <div class="demo-layout mdl-layout mdl-js-layout mdl-layout--fixed-drawer mdl-layout--fixed-header has-drawer is-upgraded" data-upgraded=",MaterialLayout">
      <header class="demo-header mdl-layout__header mdl-color--grey-100 mdl-color-text--grey-600 is-casting-shadow">
        <div class="mdl-layout__drawer-button"> <i class="material-icons">menu</i>
        </div>
        <div class="mdl-layout__header-row">
          <span class="mdl-layout-title">Home</span>
          <div class="mdl-layout-spacer"></div>
          <div class="mdl-textfield mdl-js-textfield mdl-textfield--expandable is-upgraded" data-upgraded=",MaterialTextfield">
            <label class="mdl-button mdl-js-button mdl-button--icon" for="search" data-upgraded=",MaterialButton"> <i class="material-icons">search</i>
            </label>
            <div class="mdl-textfield__expandable-holder">
              <input class="mdl-textfield__input" type="text" id="search">
              <label class="mdl-textfield__label" for="search">Enter your query...</label>
            </div>
          </div>
          <button class="mdl-button mdl-js-button mdl-js-ripple-effect mdl-button--icon" id="hdrbtn" data-upgraded=",MaterialButton,MaterialRipple">
            <i class="material-icons">more_vert</i>
            <span class="mdl-button__ripple-container">
              <span class="mdl-ripple"></span>
            </span>
          </button>
          <div class="mdl-menu__container is-upgraded">
            <div class="mdl-menu__outline mdl-menu--bottom-right"></div>
            <ul class="mdl-menu mdl-js-menu mdl-js-ripple-effect mdl-menu--bottom-right mdl-js-ripple-effect--ignore-events" for="hdrbtn" data-upgraded=",MaterialMenu,MaterialRipple">
              <li class="mdl-menu__item mdl-js-ripple-effect" tabindex="-1" data-upgraded=",MaterialRipple">
                About
                <span class="mdl-menu__item-ripple-container">
                  <span class="mdl-ripple"></span>
                </span>
              </li>
              <li class="mdl-menu__item mdl-js-ripple-effect" tabindex="-1" data-upgraded=",MaterialRipple">
                Contact
                <span class="mdl-menu__item-ripple-container">
                  <span class="mdl-ripple"></span>
                </span>
              </li>
              <li class="mdl-menu__item mdl-js-ripple-effect" tabindex="-1" data-upgraded=",MaterialRipple">
                Legal information
                <span class="mdl-menu__item-ripple-container">
                  <span class="mdl-ripple"></span>
                </span>
              </li>
            </ul>
          </div>
        </div>
      </header>
      <div class="demo-drawer mdl-layout__drawer mdl-color--blue-grey-900 mdl-color-text--blue-grey-50">
        <header class="demo-drawer-header">
          <img src="images/user.jpg" class="demo-avatar">
          <div class="demo-avatar-dropdown">
            <span>[email protected]</span>
            <div class="mdl-layout-spacer"></div>
            ...
            <div class="mdl-menu__container is-upgraded">
              <div class="mdl-menu__outline mdl-menu--bottom-right"></div>
              ...
            </div>
          </div>
        </header>
        <nav class="demo-navigation mdl-navigation mdl-color--blue-grey-800">
          <a class="mdl-navigation__link" href="">
            <i class="mdl-color-text--blue-grey-400 material-icons" role="presentation">home</i>
            Home
          </a>
          <a class="mdl-navigation__link" href="">
            <i class="mdl-color-text--blue-grey-400 material-icons" role="presentation">inbox</i>
            Inbox
          </a>
        </nav>
      </div>
      <main class="mdl-layout__content mdl-color--grey-100">
        <div class="mdl-grid demo-content">
          <div class="demo-charts mdl-color--white mdl-shadow--2dp mdl-cell mdl-cell--12-col mdl-grid">

            <svg fill="currentColor" width="200px" height="200px" viewBox="0 0 1 1" class="demo-chart mdl-cell mdl-cell--4-col mdl-cell--3-col-desktop">
              ...
            </svg>
            <svg fill="currentColor" width="200px" height="200px" viewBox="0 0 1 1" class="demo-chart mdl-cell mdl-cell--4-col mdl-cell--3-col-desktop">
              ...
            </svg>
          </div>
          ...
        </div>
      </main>
    </div>
  `
})
export class MainCmp {}

Это совершенно непостижимо и не подлежит ремонту. Кроме того, не попадайтесь в ловушку простого вывода этого на HTML-страницу и думайте, что все готово, например:

@Component({
  selector: 'app-main',
  templateUrl: './main.html'
})
export class MainCmp {}

Аккуратно, верно. Ну нет, ты просто прячешь дерьмо .

2. Определите компоненты

Для первого обзора высокого уровня лучше всего посмотреть на статическую HTML-страницу нашего огромного app-mainкомпонента.

Вот пример шаблона панели мониторинга getmdl.io:

Название изображения

Когда я смотрю на это, я могу сразу заметить:

  • область заголовка
  • основная зона / панель приборов
  • боковая панель

3. Извлеките новые компоненты и соберите

После того как мы определили компоненты, мы можем начать создавать новые и извлекать в них соответствующие части HTML.

// header component file
@Component({
  selector: 'app-header',
  template: `
    <div class="mdl-layout__drawer-button"> <i class="material-icons">menu</i>
    </div>
    <div class="mdl-layout__header-row">
      <span class="mdl-layout-title">Home</span>
      <div class="mdl-layout-spacer"></div>
      <div class="mdl-textfield mdl-js-textfield mdl-textfield--expandable is-upgraded" data-upgraded=",MaterialTextfield">
        <label class="mdl-button mdl-js-button mdl-button--icon" for="search" data-upgraded=",MaterialButton"> <i class="material-icons">search</i>
        </label>
        <div class="mdl-textfield__expandable-holder">
          <input class="mdl-textfield__input" type="text" id="search">
          <label class="mdl-textfield__label" for="search">Enter your query...</label>
        </div>
      </div>
      <button class="mdl-button mdl-js-button mdl-js-ripple-effect mdl-button--icon" id="hdrbtn" data-upgraded=",MaterialButton,MaterialRipple">
        <i class="material-icons">more_vert</i>
        <span class="mdl-button__ripple-container">
          <span class="mdl-ripple"></span>
        </span>
      </button>
      <div class="mdl-menu__container is-upgraded">
        <div class="mdl-menu__outline mdl-menu--bottom-right"></div>
        <ul class="mdl-menu mdl-js-menu mdl-js-ripple-effect mdl-menu--bottom-right mdl-js-ripple-effect--ignore-events" for="hdrbtn" data-upgraded=",MaterialMenu,MaterialRipple">
          <li class="mdl-menu__item mdl-js-ripple-effect" tabindex="-1" data-upgraded=",MaterialRipple">
            About
            <span class="mdl-menu__item-ripple-container">
              <span class="mdl-ripple"></span>
            </span>
          </li>
          <li class="mdl-menu__item mdl-js-ripple-effect" tabindex="-1" data-upgraded=",MaterialRipple">
            Contact
            <span class="mdl-menu__item-ripple-container">
              <span class="mdl-ripple"></span>
            </span>
          </li>
          <li class="mdl-menu__item mdl-js-ripple-effect" tabindex="-1" data-upgraded=",MaterialRipple">
            Legal information
            <span class="mdl-menu__item-ripple-container">
              <span class="mdl-ripple"></span>
            </span>
          </li>
        </ul>
      </div>
    </div>
  `
})
export class HeaderCmp {}

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

import { HeaderCmp } from './app-header';
import { SidebarCmp } from './app-sidebar';
import { DashboardCmp } from './app-dashboard';

@Component({
  selector: 'app',
  template: `
    <div class="demo-layout mdl-layout mdl-js-layout mdl-layout--fixed-drawer mdl-layout--fixed-header has-drawer is-upgraded" data-upgraded=",MaterialLayout">
      <app-header class="demo-header mdl-layout__header mdl-color--grey-100 mdl-color-text--grey-600 is-casting-shadow">
      </app-header>

      <app-sidebar class="demo-drawer mdl-layout__drawer mdl-color--blue-grey-900 mdl-color-text--blue-grey-50">
      </app-sidebar>

      <app-main class="mdl-layout__content mdl-color--grey-100">
      </app-main>
    </div>
  `
})
export class MainCmp {}

4. Итерировать

Очевидно, что эти компоненты все еще слишком велики. Итак, итерация, в основном вернитесь к шагу 2 , возьмите первый компонент (т.е. наш app-header) и снова извлеките его в отдельные, пока не дойдете до точки, где у вас есть компоненты разумного размера.

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

Вывод

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

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