0 added
0 removed
Original
2026-01-01
Modified
2026-02-26
1
<p>Наличие выделенного состояния позволяет легче видеть данные с которыми мы работаем и создает единый источник правды, но не избавляет нас от сложной логики в обработчиках. Там все равно, помимо изменения состояния приходится обновлять UI. Для полной картины нужно поправить и эту часть, отделив логику работы с UI от логики работы с состоянием.</p>
1
<p>Наличие выделенного состояния позволяет легче видеть данные с которыми мы работаем и создает единый источник правды, но не избавляет нас от сложной логики в обработчиках. Там все равно, помимо изменения состояния приходится обновлять UI. Для полной картины нужно поправить и эту часть, отделив логику работы с UI от логики работы с состоянием.</p>
2
<p>Обновление внешнего вида на основе состояния обычно выносят в отдельный слой, который называется представлением (View). В простейшем случае представлением является функция, которая принимает на вход состояние, анализирует его и производит необходимые изменения в DOM.</p>
2
<p>Обновление внешнего вида на основе состояния обычно выносят в отдельный слой, который называется представлением (View). В простейшем случае представлением является функция, которая принимает на вход состояние, анализирует его и производит необходимые изменения в DOM.</p>
3
<p>Возьмем пример с формой из прошлого урока и изменим его так, что обновление UI будет изолировано в функции render().</p>
3
<p>Возьмем пример с формой из прошлого урока и изменим его так, что обновление UI будет изолировано в функции render().</p>
4
<p><a>Попрактиковаться</a></p>
4
<p><a>Попрактиковаться</a></p>
5
<p><strong>Разбор изменений</strong></p>
5
<p><strong>Разбор изменений</strong></p>
6
<ul><li><strong>Валидация вынесена в отдельную функцию validate(value)</strong>. Теперь можно использовать эту функцию повторно в других местах.</li>
6
<ul><li><strong>Валидация вынесена в отдельную функцию validate(value)</strong>. Теперь можно использовать эту функцию повторно в других местах.</li>
7
<li><strong>Функция render() отвечает за отрисовку интерфейса</strong>. Вся логика обновления DOM теперь отделена от логики обновления состояния.</li>
7
<li><strong>Функция render() отвечает за отрисовку интерфейса</strong>. Вся логика обновления DOM теперь отделена от логики обновления состояния.</li>
8
<li><strong>Используется classList.toggle для управления стилями</strong>. Вместо ручной установки style.border, теперь применяем CSS-классы.</li>
8
<li><strong>Используется classList.toggle для управления стилями</strong>. Вместо ручной установки style.border, теперь применяем CSS-классы.</li>
9
</ul><p>Преимущество такого подхода начинает проявляться как только у нас добавляются новые обработчики, влияющие на ту же часть UI. Для примера добавим поле email.</p>
9
</ul><p>Преимущество такого подхода начинает проявляться как только у нас добавляются новые обработчики, влияющие на ту же часть UI. Для примера добавим поле email.</p>
10
<p><a>Попрактиковаться</a></p>
10
<p><a>Попрактиковаться</a></p>
11
<p><strong>Что изменилось?</strong></p>
11
<p><strong>Что изменилось?</strong></p>
12
<ul><li><p>Добавили поле email</p>
12
<ul><li><p>Добавили поле email</p>
13
<ul><li>Теперь у нас два поля с независимой валидацией.</li>
13
<ul><li>Теперь у нас два поля с независимой валидацией.</li>
14
</ul></li>
14
</ul></li>
15
<li><p>Вынесли validatePhone и validateEmail в отдельные функции</p>
15
<li><p>Вынесли validatePhone и validateEmail в отдельные функции</p>
16
<ul><li>Теперь легко добавить дополнительные правила или другие поля.</li>
16
<ul><li>Теперь легко добавить дополнительные правила или другие поля.</li>
17
</ul></li>
17
</ul></li>
18
<li><p>Создали validateForm()</p>
18
<li><p>Создали validateForm()</p>
19
</li>
19
</li>
20
<li><p>Проверяет все поля разом.</p>
20
<li><p>Проверяет все поля разом.</p>
21
<ul><li>Записывает ошибки в state.registrationForm.errors.</li>
21
<ul><li>Записывает ошибки в state.registrationForm.errors.</li>
22
<li>Определяет, можно ли отправлять форму.</li>
22
<li>Определяет, можно ли отправлять форму.</li>
23
</ul></li>
23
</ul></li>
24
<li><p>Обновили render()</p>
24
<li><p>Обновили render()</p>
25
<ul><li>Универсально обновляет состояние формы. Обратите внимание, что она вызывается два раза, для первой отрисовки когда никаких событий не было и по событию.</li>
25
<ul><li>Универсально обновляет состояние формы. Обратите внимание, что она вызывается два раза, для первой отрисовки когда никаких событий не было и по событию.</li>
26
<li>Теперь ошибки и стили обновляются централизованно.</li>
26
<li>Теперь ошибки и стили обновляются централизованно.</li>
27
</ul></li>
27
</ul></li>
28
<li><p>Добавили handleInput()</p>
28
<li><p>Добавили handleInput()</p>
29
<ul><li>Одна универсальная функция для обновления полей.</li>
29
<ul><li>Одна универсальная функция для обновления полей.</li>
30
<li>Упрощает код обработчиков событий.</li>
30
<li>Упрощает код обработчиков событий.</li>
31
</ul></li>
31
</ul></li>
32
</ul><p>Несмотря на то, что кода стало больше, уровень его сложности почти не вырос. Внутри мы работаем с отдельными частями (управление состоянием, отрисовка, обработчики), которые сами по себе достаточно простые и односложные. Такое разделение логики масштабируется даже на большие формы с десятками полей.</p>
32
</ul><p>Несмотря на то, что кода стало больше, уровень его сложности почти не вырос. Внутри мы работаем с отдельными частями (управление состоянием, отрисовка, обработчики), которые сами по себе достаточно простые и односложные. Такое разделение логики масштабируется даже на большие формы с десятками полей.</p>
33
<h2>Частичное обновление VS Полное обновление</h2>
33
<h2>Частичное обновление VS Полное обновление</h2>
34
<p>С ростом приложения увеличивается и количество обработчиков. Каждый из них может приводить к изменению только части страницы. Как поступать в таком случае? Создавать по рендеру на каждую ситуацию или описывать все возможные изменения в одной функции render()?</p>
34
<p>С ростом приложения увеличивается и количество обработчиков. Каждый из них может приводить к изменению только части страницы. Как поступать в таком случае? Создавать по рендеру на каждую ситуацию или описывать все возможные изменения в одной функции render()?</p>
35
<p>Наиболее простым решением будет привязка таких функций к элементам состояния. Предположим, что у нас есть страница управления списком уроков в курсе. В состоянии это выглядит так:</p>
35
<p>Наиболее простым решением будет привязка таких функций к элементам состояния. Предположим, что у нас есть страница управления списком уроков в курсе. В состоянии это выглядит так:</p>
36
<p>Для отрисовки этого списка подойдет одна функция renderLessons(), которая будет вызываться во всех обработчиках, изменяющих этот список: удаляющих или добавляющих элементы.</p>
36
<p>Для отрисовки этого списка подойдет одна функция renderLessons(), которая будет вызываться во всех обработчиках, изменяющих этот список: удаляющих или добавляющих элементы.</p>
37
<p>Что происходит внутри этой функции? Кажется, что внутри функции render() нужно соотносить данные в объекте состояния и то, что отображено на экране, а затем менять необходимую часть DOM, например, удалить какой-то элемент, которого больше нет. Посмотрите на то, как бы тогда выглядела функция renderLessons():</p>
37
<p>Что происходит внутри этой функции? Кажется, что внутри функции render() нужно соотносить данные в объекте состояния и то, что отображено на экране, а затем менять необходимую часть DOM, например, удалить какой-то элемент, которого больше нет. Посмотрите на то, как бы тогда выглядела функция renderLessons():</p>
38
<p>В реальности это очень затратный подход, его сложно программировать, так как появляется большое количество условных конструкций. Намного проще выполнять полную перерисовку на любое событие. Тогда код останется максимально простым.</p>
38
<p>В реальности это очень затратный подход, его сложно программировать, так как появляется большое количество условных конструкций. Намного проще выполнять полную перерисовку на любое событие. Тогда код останется максимально простым.</p>
39
<p>Этот код намного проще. Он в несколько раз короче и внутри него нет ни одной условной конструкции.</p>
39
<p>Этот код намного проще. Он в несколько раз короче и внутри него нет ни одной условной конструкции.</p>
40
<p>У такого подхода есть серьезный недостаток - производительность. Но учитывайте два важных момента. Во-первых, производительность - далеко не всегда проблема. Например, при реализации автокомплитов именно так и нужно поступать. Все будет работать быстро в любом случае. Во-вторых, именно эту проблему решают современные фронтенд-фреймворки. Они сами знают, как эффективнее всего обновить DOM.</p>
40
<p>У такого подхода есть серьезный недостаток - производительность. Но учитывайте два важных момента. Во-первых, производительность - далеко не всегда проблема. Например, при реализации автокомплитов именно так и нужно поступать. Все будет работать быстро в любом случае. Во-вторых, именно эту проблему решают современные фронтенд-фреймворки. Они сами знают, как эффективнее всего обновить DOM.</p>
41
<p>Теперь наше приложение разделено на три независимых части: состояние (данные приложения), обработчики и рендеринг. Эта модель работы на тривиальных приложениях (в пару-тройку обработчиков) смотрится избыточной, но если обработчиков станет хотя бы 10, то вы увидите, что с приложением достаточно удобно работать. Виден поток данных, то есть движение данных в приложении от одних частей к другим, от обработчика до отрисовки в DOM. Всегда можно отследить, что изменилось, и как одни части приложения зависят от других. К тому же, сокращается дублирование. Например, изменение состояния может идти из разных частей приложения, но логика отрисовки при этом остается неизменной. В такой ситуации достаточно описать новый способ изменения уже существующего состояния, а рендеринг сделает все остальное.</p>
41
<p>Теперь наше приложение разделено на три независимых части: состояние (данные приложения), обработчики и рендеринг. Эта модель работы на тривиальных приложениях (в пару-тройку обработчиков) смотрится избыточной, но если обработчиков станет хотя бы 10, то вы увидите, что с приложением достаточно удобно работать. Виден поток данных, то есть движение данных в приложении от одних частей к другим, от обработчика до отрисовки в DOM. Всегда можно отследить, что изменилось, и как одни части приложения зависят от других. К тому же, сокращается дублирование. Например, изменение состояния может идти из разных частей приложения, но логика отрисовки при этом остается неизменной. В такой ситуации достаточно описать новый способ изменения уже существующего состояния, а рендеринг сделает все остальное.</p>
42
<p>Кроме наличия разделения на три части, не менее важно то, как они друг с другом взаимодействуют:</p>
42
<p>Кроме наличия разделения на три части, не менее важно то, как они друг с другом взаимодействуют:</p>
43
<ul><li>Состояние не знает ничего про остальные части системы - оно ядро.</li>
43
<ul><li>Состояние не знает ничего про остальные части системы - оно ядро.</li>
44
<li>Рендеринг пользуется состоянием для отрисовки (добавление, изменение или удаление элементов) и добавляет новые обработчики в DOM.</li>
44
<li>Рендеринг пользуется состоянием для отрисовки (добавление, изменение или удаление элементов) и добавляет новые обработчики в DOM.</li>
45
<li>Обработчики знают про состояние, так как обновляют его и инициируют рендеринг.</li>
45
<li>Обработчики знают про состояние, так как обновляют его и инициируют рендеринг.</li>
46
</ul><p>Этот способ разделения по-прежнему обладает одним важным недостатком - изменение состояние требует явного вызова функции отрисовки. Этот недостаток мы устраним в уроке посвященному MVC.</p>
46
</ul><p>Этот способ разделения по-прежнему обладает одним важным недостатком - изменение состояние требует явного вызова функции отрисовки. Этот недостаток мы устраним в уроке посвященному MVC.</p>