Недавно я написал твит о том, как я обычно создаю компоненты.
Отличный рабочий процесс для рефакторинга громоздких компонентов ? https://t.co/each4c2cm9
— Кент С. Доддс (@kentcdodds)
9 декабря 2015 г.
Твит довольно короткий, поэтому давайте более подробно рассмотрим упомянутый рабочий процесс. Также обратите внимание, что здесь я использую компоненты Angular 2, но это верно и во многих других контекстах, Angular 1.x, React и т. Д.
Когда вы запускаете новое приложение, чаще всего вы получаете статический документ от вашей команды разработчиков пользовательского интерфейса или запускаете какой-то предопределенный шаблон . При этом вам нужно иметь стратегию для разложения этого огромного статического блока HTML на более мелкие компоненты.
Мой рабочий процесс обычно состоит из:
- Создание компонента, содержащего этот огромный статический HTML-блок
- Идентификация многоразовых или автономных частей
- Извлечение этих частей в отдельные компоненты
- Итерация, вернитесь к шагу 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
) и снова извлеките его в отдельные, пока не дойдете до точки, где у вас есть компоненты разумного размера.
Во время этого процесса вы, естественно, даже можете получить компоненты, настолько общие, что их можно использовать в разных приложениях. Таким образом, вы можете даже извлечь их в отдельную выделенную библиотеку.
Вывод
Я думаю, у вас есть основная идея. Очевидно, что через некоторое время вы будете извлекать и перекомпоновывать намного быстрее, даже в своей голове, «в памяти», но это основная концепция.
Еще когда я был студентом в университете, они объяснили нам концепцию рекурсии в алгоритмах, представив концепцию «разделяй и властвуй». Время от времени мне напоминают об этом, потому что здесь все почти так же. Вы берете кусок, извлекаете его, а затем снова собираете его обратно в основную часть.