Skip to content

Commit 90e95e5

Browse files
Update lesson.md
1 parent 4fe2ab1 commit 90e95e5

File tree

1 file changed

+356
-0
lines changed

1 file changed

+356
-0
lines changed

lessons/lesson23/lesson.md

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

0 commit comments

Comments
 (0)