HTML Diff
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>