11# Lesson 23
2+
23## OTUS Javascript Basic
34
45### Разделение логики и представления
910
1011#### Цели занятия
1112
12- * Разобрать, как разделение кода на составляющие помогает с переиспользованием кода и его поддержкой (на простых примерах с запросами и DOM).
13- * Узнать подходы: принцип единственной ответственности, представление, шаблонизация, сервисный слой, MVC и увидеть, как они выражаются в коде (используя то, что уже известно: функции, async/await, fetch, DOM, события).
14- * На практике: маленькие примеры и рефакторинг (без классов — только функции).
13+ - Разобрать, как разделение кода на составляющие помогает с переиспользованием кода и его поддержкой (на простых примерах с запросами и DOM).
14+ - Узнать подходы: принцип единственной ответственности, представление, шаблонизация, сервисный слой, MVC и увидеть, как они выражаются в коде (используя то, что уже известно: функции, async/await, fetch, DOM, события).
15+ - На практике: маленькие примеры и рефакторинг (без классов — только функции).
1516
1617<!-- v-->
1718
1819#### ** Компетенции:**
19- * Применение метанавыков для обработки информации и принятия решений.
20- * Умение структурировать программы.
20+
21+ - Применение метанавыков для обработки информации и принятия решений.
22+ - Умение структурировать программы.
2123
2224<!-- v-->
2325
2426### План занятия
2527
26- * Введение: Зачем разделять — как "сортировка" в коде
27- * Теория: Определения и подходы.
28- * Маленькие примеры с запросами и DOM: "до/после"
29- * Лайвкодинг: Плохой код → шаг за шагом в MVC
30- * Итоги
28+ - Введение: Зачем разделять — как "сортировка" в коде
29+ - Теория: Определения и подходы.
30+ - Маленькие примеры с запросами и DOM: "до/после"
31+ - Лайвкодинг: Плохой код → шаг за шагом в MVC
32+ - Итоги
3133
3234<!-- s-->
3335
3436### Введение:
3537
3638<!-- v-->
3739
38-
3940### Почему разделение — это легко и выгодно?
4041
4142Вы уже знаете DOM, async/await и fetch, функции и объекты. Но в простых скриптах часто всё смешивается: fetch в обработчике клика + innerHTML там же. Результат — дубли, ошибки при изменении (сломал UI — сломал запрос).
5556### fetch в обработчике + DOM
5657
5758``` javascript
58- document .getElementById (' btn' ).addEventListener (' click' , async () => {
59- const response = await fetch (' https://jsonplaceholder.typicode.com/users/1' );
59+ document .getElementById (" btn" ).addEventListener (" click" , async () => {
60+ const response = await fetch (" https://jsonplaceholder.typicode.com/users/1" );
6061 const data = await response .json ();
61- document .getElementById (' output' ).innerHTML = ` <div >${ JSON .stringify (data)} </div >` ;
62+ document .getElementById (" output" ).innerHTML = ` <div >${ JSON .stringify (
63+ data
64+ )} </div >` ;
6265});
6366```
6467
6568Плохо: данные и отображение смешаны, нет переиспользования, любая ошибка в fetch ломает UI
6669
67-
6870<!-- s-->
6971
7072### Представление (View)
@@ -79,31 +81,34 @@ document.getElementById('btn').addEventListener('click', async () => {
7981
8082``` javascript
8183function renderDataAndFetch () {
82- fetch (' https://jsonplaceholder.typicode.com/users/1' )
83- .then (res => res .json ())
84- .then (data => {
85- document .getElementById (' output' ).innerHTML = ` <pre >${ JSON .stringify (data, null , 2 )} </pre >` ;
84+ fetch (" https://jsonplaceholder.typicode.com/users/1" )
85+ .then ((res ) => res .json ())
86+ .then ((data ) => {
87+ document .getElementById (" output" ).innerHTML = ` <pre >${ JSON .stringify (
88+ data,
89+ null ,
90+ 2
91+ )} </pre >` ;
8692 });
8793}
8894renderDataAndFetch ();
8995```
9096
9197Плохо: функция одновременно делает fetch и вставку в DOM, нарушается принцип единственной ответственности
9298
93-
9499<!-- s-->
95100
96101### Шаблонизация
97102
98103Определение: генерация HTML из заготовки и данных.
99104
100- Пример:
105+ Пример:
101106
102107<!-- v-->
103108
104109``` js
105110for (let i = 0 ; i < data .length ; i++ ) {
106- html += ' <p>' + data[i] + ' </p>' ;
111+ html += " <p>" + data[i] + " </p>" ;
107112}
108113```
109114
@@ -113,23 +118,22 @@ for (let i = 0; i < data.length; i++) {
113118
114119Функции для запросов и расчётов.
115120
116- Пример:
121+ Пример:
117122
118123<!-- v-->
119124
120125### сервисный fetch встроен в обработчик
121126
122127``` javascript
123- document .getElementById (' btn' ).addEventListener (' click' , async () => {
124- const res = await fetch (' https://jsonplaceholder.typicode.com/users/1' );
128+ document .getElementById (" btn" ).addEventListener (" click" , async () => {
129+ const res = await fetch (" https://jsonplaceholder.typicode.com/users/1" );
125130 const data = await res .json ();
126131 console .log (data);
127132});
128133```
129134
130135Плохо: нет переиспользования, каждый обработчик дублирует fetch, тестировать сложно
131136
132-
133137<!-- s-->
134138
135139### Теория: MVC
@@ -142,12 +146,11 @@ Controller — связывает (addEventListener → model → view).
142146
143147Преимущества: структура и масштабируемость.
144148
145-
146149<!-- s-->
147150
148151### Примеры: "До" и "После"
149152
150- Используем fetch, addEventListener, innerHTML.
153+ Используем fetch, addEventListener, innerHTML.
151154
152155Каждый пример: "плохой" (смешанный) и "хороший" (разделённый).
153156
@@ -159,11 +162,11 @@ Controller — связывает (addEventListener → model → view).
159162<button id =" btn" >Показать</button >
160163<div id =" name" ></div >
161164<script >
162- function showName () {
163- var name = ' Алекс' ;
164- document .getElementById (' name' ).innerHTML = ' Имя: ' + name;
165- }
166- document .getElementById (' btn' ).addEventListener (' click' , showName);
165+ function showName () {
166+ var name = " Алекс" ;
167+ document .getElementById (" name" ).innerHTML = " Имя: " + name;
168+ }
169+ document .getElementById (" btn" ).addEventListener (" click" , showName);
167170 </script >
168171```
169172
@@ -174,13 +177,17 @@ document.getElementById('btn').addEventListener('click', showName);
174177### Пример 1: "После"
175178
176179``` javascript
177- function getName () { return ' Алекс' ; }
180+ function getName () {
181+ return " Алекс" ;
182+ }
178183
179184function renderName (name ) {
180- document .getElementById (' name' ).innerHTML = ` Имя: ${ name} ` ;
185+ document .getElementById (" name" ).innerHTML = ` Имя: ${ name} ` ;
181186}
182187
183- document .getElementById (' btn' ).addEventListener (' click' , () => renderName (getName ()));
188+ document
189+ .getElementById (" btn" )
190+ .addEventListener (" click" , () => renderName (getName ()));
184191```
185192
186193Разделение: данные и представление отделены.
@@ -192,21 +199,20 @@ document.getElementById('btn').addEventListener('click', () => renderName(getNam
192199### Пример 2: "До" — список имён
193200
194201``` javascript
195- const names = [' Маша' , ' Петя' ];
196- let html = ' <ul>' ;
202+ const names = [" Маша" , " Петя" ];
203+ let html = " <ul>" ;
197204
198205for (let i = 0 ; i < names .length ; i++ ) {
199206 // Здесь потенциальная уязвимость:
200- // если names[i] придёт из ненадёжного источника,
207+ // если names[i] придёт из ненадёжного источника,
201208 // вставка через + в innerHTML позволит выполнить HTML/JS
202- html += ' <li>' + names[i] + ' </li>' ;
209+ html += " <li>" + names[i] + " </li>" ;
203210}
204211
205- html += ' </ul>' ;
212+ html += " </ul>" ;
206213
207214// Здесь тоже уязвимость: innerHTML вставляет HTML напрямую
208- document .getElementById (' list' ).innerHTML = html;
209-
215+ document .getElementById (" list" ).innerHTML = html;
210216```
211217
212218Недостаток: небезопасно.
@@ -216,16 +222,16 @@ document.getElementById('list').innerHTML = html;
216222### Пример 2: "После" — шаблон
217223
218224``` javascript
219- const names = [' Маша' , ' Петя' ];
225+ const names = [" Маша" , " Петя" ];
220226
221227function renderListSafe (items , containerId ) {
222228 const container = document .getElementById (containerId);
223229
224230 // создаём новый ul
225- const ul = document .createElement (' ul ' );
231+ const ul = document .createElement (" ul " );
226232
227- items .map (name => {
228- const li = document .createElement (' li ' );
233+ items .map (( name ) => {
234+ const li = document .createElement (" li " );
229235 li .textContent = name; // безопасно
230236 ul .appendChild (li);
231237 });
@@ -234,16 +240,13 @@ function renderListSafe(items, containerId) {
234240 container .replaceChildren (ul);
235241}
236242
237- renderListSafe (names, ' list' );
238-
239-
243+ renderListSafe (names, " list" );
240244```
241245
242246безопаснее
243247
244248<!-- s-->
245249
246-
247250### Пример 3: "До" — расчёт суммы
248251
249252``` javascript
@@ -252,20 +255,21 @@ let sum = 0;
252255for (let i = 0 ; i < prices .length ; i++ ) {
253256 sum += prices[i];
254257}
255- document .getElementById (' total' ).innerText = ' Сумма: ' + sum;
258+ document .getElementById (" total" ).innerText = " Сумма: " + sum;
256259```
257260
258261Недостаток: логика и DOM смешаны.
259262
260263<!-- v-->
264+
261265### Пример 3: "После" — сервис
262266
263267``` javascript
264268function calculateSum (prices ) {
265269 return prices .reduce ((total , price ) => total + price, 0 );
266270}
267271function renderTotal (sum ) {
268- document .getElementById (' total' ).innerText = ` Сумма: ${ sum} ` ;
272+ document .getElementById (" total" ).innerText = ` Сумма: ${ sum} ` ;
269273}
270274renderTotal (calculateSum ([100 , 200 ]));
271275```
@@ -280,26 +284,27 @@ renderTotal(calculateSum([100, 200]));
280284<button id =" btn" >Загрузить</button >
281285<div id =" user" ></div >
282286<script >
283- document .getElementById (' btn' ).addEventListener (' click' , function () {
284- fetch (' https://jsonplaceholder.typicode.com/users/1' )
285- .then (r => r .json ())
286- .then (user => {
287- document .getElementById (' user' ).innerHTML = user .name ;
288- });
289- });
287+ document .getElementById (" btn" ).addEventListener (" click" , function () {
288+ fetch (" https://jsonplaceholder.typicode.com/users/1" )
289+ .then (( r ) => r .json ())
290+ .then (( user ) => {
291+ document .getElementById (" user" ).innerHTML = user .name ;
292+ });
293+ });
290294 </script >
291295```
292296
293297Недостаток: асинхронный код и DOM в обработчике.
294298
295299<!-- v-->
300+
296301### Пример 4: "После" — сервис + View
297302
298303``` javascript
299304// Сервис: получает данные пользователя
300305async function getUser (id ) {
301306 const res = await fetch (` https://jsonplaceholder.typicode.com/users/${ id} ` );
302- if (! res .ok ) throw new Error (' Ошибка запроса' );
307+ if (! res .ok ) throw new Error (" Ошибка запроса" );
303308 return await res .json ();
304309}
305310
@@ -319,14 +324,15 @@ async function handleClick(id, containerId) {
319324}
320325
321326// Пример привязки к кнопке
322- document .getElementById (' btn' ).addEventListener (' click' , () => handleClick (1 , ' user' ));
327+ document
328+ .getElementById (" btn" )
329+ .addEventListener (" click" , () => handleClick (1 , " user" ));
323330```
324331
325332Код становится чище и надёжнее.
326333
327334<!-- s-->
328335
329-
330336### Практика
331337
332338<!-- s-->
@@ -335,22 +341,20 @@ document.getElementById('btn').addEventListener('click', () => handleClick(1, 'u
335341
336342Основные выводы:
337343
338- * SRP, View, шаблоны, сервисы и MVC разделяют fetch и DOM.
339- * Переиспользование: общие функции для разных мест.
344+ - SRP, View, шаблоны, сервисы и MVC разделяют fetch и DOM.
345+ - Переиспользование: общие функции для разных мест.
340346
341347<!-- v -->
342348
343- ### Домашнее задание: Смотрите на портале
344-
349+ ### Домашнее задание: Смотрите на портале
345350
346351<!-- s-->
347352
348353### Дополнительные материалы
349354
350- * SRP и SOLID в JavaScript
351- * MVC без фреймворков
352- * Шаблоны в JavaScript
353-
355+ - SRP и SOLID в JavaScript
356+ - MVC без фреймворков
357+ - Шаблоны в JavaScript
354358
355359<!-- s-->
356360
0 commit comments