Модуль 3: JavaScript — Интерактивность и логика
Освоим основы JavaScript, взаимодействие с DOM, обработку событий, работу с формами и хранилищами, а также Fetch API для получения данных.
3.1 Введение в JavaScript. Синтаксис и подключение
JS добавляет интерактивность. Подключают перед закрывающим </body>
, чтобы не блокировать загрузку.
<script src="app.js"></script>
Переменные и типы
let count = 0; // можно переназначить
const appName = 'SKL'; // константа
console.log(typeof 42, typeof 'str', typeof true, typeof null, typeof undefined);
Консоль
console.log('info');
console.warn('warning');
console.error('error');
3.2 Операторы и условия
const hour = new Date().getHours();
if (hour < 12) {
console.log('Доброе утро');
} else if (hour < 18) {
console.log('Добрый день');
} else {
console.log('Добрый вечер');
}
const role = 'admin';
switch (role) {
case 'admin': console.log('Администратор'); break;
case 'user': console.log('Пользователь'); break;
default: console.log('Гость');
}
Практика
Напишите скрипт, который приветствует пользователя в зависимости от времени суток.
3.3 Функции
function sum(a, b) { return a + b; } // declaration
const multiply = (a, b) => a * b; // arrow
const greet = (name = 'мир') => `Привет, ${name}!`; // значение по умолчанию
console.log(sum(2,3), multiply(2,3), greet());
Практика
Сделайте набор функций‑калькуляторов: сумма, разность, среднее, площадь круга.
3.4 Циклы и массивы
const projects = ['Сайт', 'Бот', 'Игра'];
projects.push('Чат');
projects.forEach((p, i) => console.log(i + 1, p));
for (let i = 0; i < projects.length; i++) {
if (i === 2) break;
console.log('for:', projects[i]);
}
Практика
Выведите в консоль нумерованный список проектов и посчитайте их количество.
3.5 Работа с DOM — Часть 1 (Поиск и изменение)
const title = document.getElementById('title');
const button = document.querySelector('.btn');
button.addEventListener('click', () => {
title.textContent = 'Новый заголовок';
title.style.color = '#4f46e5';
});
Практика
По клику на кнопку меняйте текст и стиль произвольного элемента.
3.6 Работа с DOM — Часть 2 (События)
const modal = document.getElementById('modal');
const openBtn = document.getElementById('open');
const closeBtn = document.getElementById('close');
openBtn.addEventListener('click', () => modal.classList.add('open'));
closeBtn.addEventListener('click', () => modal.classList.remove('open'));
document.addEventListener('keydown', (e) => {
if (e.key === 'Escape') modal.classList.remove('open');
});
Практика
Соберите простое модальное окно, которое открывается и закрывается по клику и клавише Esc.
3.7 Работа с формами в JS
const form = document.querySelector('form');
const email = form.querySelector('input[type="email"]');
form.addEventListener('submit', (e) => {
e.preventDefault();
const value = email.value.trim();
const isValid = /.+@.+\..+/.test(value);
if (!isValid) {
alert('Введите корректный email');
return;
}
alert('Спасибо! Форма принята');
});
Практика
Соберите форму обратной связи и проверьте обязательные поля (имя, email).
3.8 Хранение данных на клиенте
// Тема
const theme = localStorage.getItem('theme') || 'light';
localStorage.setItem('theme', theme);
// Простые заметки
const notes = JSON.parse(localStorage.getItem('notes') || '[]');
notes.push('Сделать портфолио');
localStorage.setItem('notes', JSON.stringify(notes));
Практика
Сделайте список заметок/задач, который сохраняется между перезагрузками.
3.9 (Бонус) Асинхронность. Fetch API
async function loadPosts() {
const res = await fetch('https://jsonplaceholder.typicode.com/posts?_limit=3');
const posts = await res.json();
console.log(posts);
}
loadPosts();
Практика
Получите 3 поста из JSONPlaceholder и выведите заголовки на страницу.
3.10 Современный JS (ES6+) и модули
- Деструктуризация, rest/spread, шаблонные строки, опциональная цепочка
?.
, nullish‑объединение??
. - Коллекции:
Map
,Set
,WeakMap
,WeakSet
. - Модули:
<script type="module">
,import
/export
, отложенная загрузка.
<script type="module">
import { sum } from './utils/math.js';
console.log(sum(2, 3));
</script>
3.11 Web APIs: события, Fetch/Streams, Storage
События
const controller = new AbortController();
const handler = (e) => console.log('mousemove');
document.addEventListener('mousemove', handler, { passive: true, signal: controller.signal });
setTimeout(() => controller.abort(), 3000);
Fetch + Streams
const res = await fetch('/big.json');
const reader = res.body.getReader();
let received = 0;
while (true) {
const { done, value } = await reader.read();
if (done) break;
received += value.length;
console.log('Получено байт:', received);
}
Cache/Storage
await caches.open('app-v1').then((cache) => cache.add('/offline.html'));
const db = await indexedDB.open('app-db', 1);
3.12 Производительность (LCP/CLS/INP)
- LCP: оптимизируйте главное изображение:
preload
/fetchpriority
, подходящий формат. - CLS: резервируйте место медиаконтенту, не вставляйте динамику над контентом.
- INP: обработчики событий — быстрые, используйте
passive
, делайте работу вне главного потока черезrequestIdleCallback
/Web Workers
.
<link rel="preload" as="image" href="/img/hero@1x.avif" imagesrcset="/img/hero@1x.avif 1x, /img/hero@2x.avif 2x" imagesizes="100vw">
3.13 Безопасность: CSP, XSS, CORS
- CSP: ограничивает источники скриптов/стилей/изображений. Избегайте inline‑скриптов, используйте nonce/sha256.
- XSS: никогда не вставляйте пользовательский ввод как HTML; экранируйте, используйте
textContent
. - CORS: сервер должен явно разрешать домены/методы/заголовки; на клиенте — используйте
mode: 'cors'
.
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self' 'nonce-abc'; img-src 'self' data:; object-src 'none'">
3.14 Инструменты: npm, Vite/Webpack, PostCSS, Sass
- npm: управление зависимостями, команды
scripts
. - Vite: дев‑сервер, HMR, сборка с esbuild/rollup.
- Webpack: гибкая сборка модулей, код‑сплиттинг.
- PostCSS/Sass: автопрефиксы, nested‑синтаксис, переменные и миксины.
{
"scripts": {
"dev": "vite",
"build": "vite build",
"preview": "vite preview"
}
}
3.15 Тестирование: Jest и Playwright
- Jest: модульные тесты, моки, снапшоты.
- Playwright: e2e‑тесты в реальном браузере; авто‑ожидания, тестирование доступности.
// jest
import { sum } from './sum';
test('adds', () => expect(sum(1,2)).toBe(3));
// playwright.config.ts
// test('home page', async ({ page }) => { await page.goto('/'); await expect(page).toHaveTitle(/SKL/); });
3.16 Фреймворки: React и Vue
Фреймворки упрощают построение сложных интерфейсов за счёт компонентов и реактивности.
React
import { useState } from 'react';
function Counter() {
const [count, setCount] = useState(0);
return <button onClick={() => setCount(count + 1)}>{count}</button>;
}
Vue
<script setup>
import { ref } from 'vue';
const count = ref(0);
</script>
<template>
<button @click="count++">{{ count }}</button>
</template>
Лучшие практики: компонентная архитектура, состояние как источник истины, типизация (TS), тесты, линтеры (ESLint/Prettier), разделение кода.
3.17 Best practices
- Прогрессивное улучшение: базовая функциональность без JS, улучшения поверх.
- Доступность по умолчанию: семантика, фокус‑индикаторы, контраст, клавиатурная навигация.
- Производительность: бюджет по размерам, lazy‑loading, code‑splitting, критический CSS, кэширование.
- Безопасность: CSP, избегать
innerHTML
, валидация на клиенте и сервере, sanitize. - Код‑стайл: ESLint + Prettier, единый формат, meaningful‑имена, маленькие функции.
- Типизация: TypeScript для масштабируемости и снижения числа ошибок.
- Git‑практики: маленькие коммиты, осмысленные сообщения, PR‑ревью, CI.
- Тесты: пирамида тестирования: unit > integration > e2e, покрытия критических путей.
- Мониторинг: логирование ошибок (Sentry), метрики Web Vitals, профилирование.
Итоговый проект модуля 3
Добавьте интерактивность на сайт‑портфолио: мобильное меню, слайдер галереи, рабочая форма с валидацией, тёмная тема с сохранением в localStorage
.