1
1
# Lesson 23
2
+
2
3
## OTUS Javascript Basic
3
4
4
5
### Разделение логики и представления
9
10
10
11
#### Цели занятия
11
12
12
- * Разобрать, как разделение кода на составляющие помогает с переиспользованием кода и его поддержкой (на простых примерах с запросами и DOM).
13
- * Узнать подходы: принцип единственной ответственности, представление, шаблонизация, сервисный слой, MVC и увидеть, как они выражаются в коде (используя то, что уже известно: функции, async/await, fetch, DOM, события).
14
- * На практике: маленькие примеры и рефакторинг (без классов — только функции).
13
+ - Разобрать, как разделение кода на составляющие помогает с переиспользованием кода и его поддержкой (на простых примерах с запросами и DOM).
14
+ - Узнать подходы: принцип единственной ответственности, представление, шаблонизация, сервисный слой, MVC и увидеть, как они выражаются в коде (используя то, что уже известно: функции, async/await, fetch, DOM, события).
15
+ - На практике: маленькие примеры и рефакторинг (без классов — только функции).
15
16
16
17
<!-- v-->
17
18
18
19
#### ** Компетенции:**
19
- * Применение метанавыков для обработки информации и принятия решений.
20
- * Умение структурировать программы.
20
+
21
+ - Применение метанавыков для обработки информации и принятия решений.
22
+ - Умение структурировать программы.
21
23
22
24
<!-- v-->
23
25
24
26
### План занятия
25
27
26
- * Введение: Зачем разделять — как "сортировка" в коде
27
- * Теория: Определения и подходы.
28
- * Маленькие примеры с запросами и DOM: "до/после"
29
- * Лайвкодинг: Плохой код → шаг за шагом в MVC
30
- * Итоги
28
+ - Введение: Зачем разделять — как "сортировка" в коде
29
+ - Теория: Определения и подходы.
30
+ - Маленькие примеры с запросами и DOM: "до/после"
31
+ - Лайвкодинг: Плохой код → шаг за шагом в MVC
32
+ - Итоги
31
33
32
34
<!-- s-->
33
35
34
36
### Введение:
35
37
36
38
<!-- v-->
37
39
38
-
39
40
### Почему разделение — это легко и выгодно?
40
41
41
42
Вы уже знаете DOM, async/await и fetch, функции и объекты. Но в простых скриптах часто всё смешивается: fetch в обработчике клика + innerHTML там же. Результат — дубли, ошибки при изменении (сломал UI — сломал запрос).
55
56
### fetch в обработчике + DOM
56
57
57
58
``` 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" );
60
61
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 >` ;
62
65
});
63
66
```
64
67
65
68
Плохо: данные и отображение смешаны, нет переиспользования, любая ошибка в fetch ломает UI
66
69
67
-
68
70
<!-- s-->
69
71
70
72
### Представление (View)
@@ -79,31 +81,34 @@ document.getElementById('btn').addEventListener('click', async () => {
79
81
80
82
``` javascript
81
83
function 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 >` ;
86
92
});
87
93
}
88
94
renderDataAndFetch ();
89
95
```
90
96
91
97
Плохо: функция одновременно делает fetch и вставку в DOM, нарушается принцип единственной ответственности
92
98
93
-
94
99
<!-- s-->
95
100
96
101
### Шаблонизация
97
102
98
103
Определение: генерация HTML из заготовки и данных.
99
104
100
- Пример:
105
+ Пример:
101
106
102
107
<!-- v-->
103
108
104
109
``` js
105
110
for (let i = 0 ; i < data .length ; i++ ) {
106
- html += ' <p>' + data[i] + ' </p>' ;
111
+ html += " <p>" + data[i] + " </p>" ;
107
112
}
108
113
```
109
114
@@ -113,23 +118,22 @@ for (let i = 0; i < data.length; i++) {
113
118
114
119
Функции для запросов и расчётов.
115
120
116
- Пример:
121
+ Пример:
117
122
118
123
<!-- v-->
119
124
120
125
### сервисный fetch встроен в обработчик
121
126
122
127
``` 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" );
125
130
const data = await res .json ();
126
131
console .log (data);
127
132
});
128
133
```
129
134
130
135
Плохо: нет переиспользования, каждый обработчик дублирует fetch, тестировать сложно
131
136
132
-
133
137
<!-- s-->
134
138
135
139
### Теория: MVC
@@ -142,12 +146,11 @@ Controller — связывает (addEventListener → model → view).
142
146
143
147
Преимущества: структура и масштабируемость.
144
148
145
-
146
149
<!-- s-->
147
150
148
151
### Примеры: "До" и "После"
149
152
150
- Используем fetch, addEventListener, innerHTML.
153
+ Используем fetch, addEventListener, innerHTML.
151
154
152
155
Каждый пример: "плохой" (смешанный) и "хороший" (разделённый).
153
156
@@ -159,11 +162,11 @@ Controller — связывает (addEventListener → model → view).
159
162
<button id =" btn" >Показать</button >
160
163
<div id =" name" ></div >
161
164
<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);
167
170
</script >
168
171
```
169
172
@@ -174,13 +177,17 @@ document.getElementById('btn').addEventListener('click', showName);
174
177
### Пример 1: "После"
175
178
176
179
``` javascript
177
- function getName () { return ' Алекс' ; }
180
+ function getName () {
181
+ return " Алекс" ;
182
+ }
178
183
179
184
function renderName (name ) {
180
- document .getElementById (' name' ).innerHTML = ` Имя: ${ name} ` ;
185
+ document .getElementById (" name" ).innerHTML = ` Имя: ${ name} ` ;
181
186
}
182
187
183
- document .getElementById (' btn' ).addEventListener (' click' , () => renderName (getName ()));
188
+ document
189
+ .getElementById (" btn" )
190
+ .addEventListener (" click" , () => renderName (getName ()));
184
191
```
185
192
186
193
Разделение: данные и представление отделены.
@@ -192,21 +199,20 @@ document.getElementById('btn').addEventListener('click', () => renderName(getNam
192
199
### Пример 2: "До" — список имён
193
200
194
201
``` javascript
195
- const names = [' Маша' , ' Петя' ];
196
- let html = ' <ul>' ;
202
+ const names = [" Маша" , " Петя" ];
203
+ let html = " <ul>" ;
197
204
198
205
for (let i = 0 ; i < names .length ; i++ ) {
199
206
// Здесь потенциальная уязвимость:
200
- // если names[i] придёт из ненадёжного источника,
207
+ // если names[i] придёт из ненадёжного источника,
201
208
// вставка через + в innerHTML позволит выполнить HTML/JS
202
- html += ' <li>' + names[i] + ' </li>' ;
209
+ html += " <li>" + names[i] + " </li>" ;
203
210
}
204
211
205
- html += ' </ul>' ;
212
+ html += " </ul>" ;
206
213
207
214
// Здесь тоже уязвимость: innerHTML вставляет HTML напрямую
208
- document .getElementById (' list' ).innerHTML = html;
209
-
215
+ document .getElementById (" list" ).innerHTML = html;
210
216
```
211
217
212
218
Недостаток: небезопасно.
@@ -216,16 +222,16 @@ document.getElementById('list').innerHTML = html;
216
222
### Пример 2: "После" — шаблон
217
223
218
224
``` javascript
219
- const names = [' Маша' , ' Петя' ];
225
+ const names = [" Маша" , " Петя" ];
220
226
221
227
function renderListSafe (items , containerId ) {
222
228
const container = document .getElementById (containerId);
223
229
224
230
// создаём новый ul
225
- const ul = document .createElement (' ul ' );
231
+ const ul = document .createElement (" ul " );
226
232
227
- items .map (name => {
228
- const li = document .createElement (' li ' );
233
+ items .map (( name ) => {
234
+ const li = document .createElement (" li " );
229
235
li .textContent = name; // безопасно
230
236
ul .appendChild (li);
231
237
});
@@ -234,16 +240,13 @@ function renderListSafe(items, containerId) {
234
240
container .replaceChildren (ul);
235
241
}
236
242
237
- renderListSafe (names, ' list' );
238
-
239
-
243
+ renderListSafe (names, " list" );
240
244
```
241
245
242
246
безопаснее
243
247
244
248
<!-- s-->
245
249
246
-
247
250
### Пример 3: "До" — расчёт суммы
248
251
249
252
``` javascript
@@ -252,20 +255,21 @@ let sum = 0;
252
255
for (let i = 0 ; i < prices .length ; i++ ) {
253
256
sum += prices[i];
254
257
}
255
- document .getElementById (' total' ).innerText = ' Сумма: ' + sum;
258
+ document .getElementById (" total" ).innerText = " Сумма: " + sum;
256
259
```
257
260
258
261
Недостаток: логика и DOM смешаны.
259
262
260
263
<!-- v-->
264
+
261
265
### Пример 3: "После" — сервис
262
266
263
267
``` javascript
264
268
function calculateSum (prices ) {
265
269
return prices .reduce ((total , price ) => total + price, 0 );
266
270
}
267
271
function renderTotal (sum ) {
268
- document .getElementById (' total' ).innerText = ` Сумма: ${ sum} ` ;
272
+ document .getElementById (" total" ).innerText = ` Сумма: ${ sum} ` ;
269
273
}
270
274
renderTotal (calculateSum ([100 , 200 ]));
271
275
```
@@ -280,26 +284,27 @@ renderTotal(calculateSum([100, 200]));
280
284
<button id =" btn" >Загрузить</button >
281
285
<div id =" user" ></div >
282
286
<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
+ });
290
294
</script >
291
295
```
292
296
293
297
Недостаток: асинхронный код и DOM в обработчике.
294
298
295
299
<!-- v-->
300
+
296
301
### Пример 4: "После" — сервис + View
297
302
298
303
``` javascript
299
304
// Сервис: получает данные пользователя
300
305
async function getUser (id ) {
301
306
const res = await fetch (` https://jsonplaceholder.typicode.com/users/${ id} ` );
302
- if (! res .ok ) throw new Error (' Ошибка запроса' );
307
+ if (! res .ok ) throw new Error (" Ошибка запроса" );
303
308
return await res .json ();
304
309
}
305
310
@@ -319,14 +324,15 @@ async function handleClick(id, containerId) {
319
324
}
320
325
321
326
// Пример привязки к кнопке
322
- document .getElementById (' btn' ).addEventListener (' click' , () => handleClick (1 , ' user' ));
327
+ document
328
+ .getElementById (" btn" )
329
+ .addEventListener (" click" , () => handleClick (1 , " user" ));
323
330
```
324
331
325
332
Код становится чище и надёжнее.
326
333
327
334
<!-- s-->
328
335
329
-
330
336
### Практика
331
337
332
338
<!-- s-->
@@ -335,22 +341,20 @@ document.getElementById('btn').addEventListener('click', () => handleClick(1, 'u
335
341
336
342
Основные выводы:
337
343
338
- * SRP, View, шаблоны, сервисы и MVC разделяют fetch и DOM.
339
- * Переиспользование: общие функции для разных мест.
344
+ - SRP, View, шаблоны, сервисы и MVC разделяют fetch и DOM.
345
+ - Переиспользование: общие функции для разных мест.
340
346
341
347
<!-- v -->
342
348
343
- ### Домашнее задание: Смотрите на портале
344
-
349
+ ### Домашнее задание: Смотрите на портале
345
350
346
351
<!-- s-->
347
352
348
353
### Дополнительные материалы
349
354
350
- * SRP и SOLID в JavaScript
351
- * MVC без фреймворков
352
- * Шаблоны в JavaScript
353
-
355
+ - SRP и SOLID в JavaScript
356
+ - MVC без фреймворков
357
+ - Шаблоны в JavaScript
354
358
355
359
<!-- s-->
356
360
0 commit comments