Студенческий материал. Его можно использовать как самостоятельный конспект, раздаточный материал и справочник после занятия.
Angular и React появляются в нашем курсе не в качестве магических инструментов, а как следующий слой над базовыми механизмами веба.
Если студент начинает с команды генерации проекта, он довольно быстро получает экран в браузере, но не понимает, что именно делает framework. При первой нестандартной ошибке такой навык ломается. Поэтому первый урок строится от основания: браузер, HTTP, HTML, CSS, JavaScript, данные, компоненты и только затем Angular и React.
Аналогия. Представьте TaskFlow как холодильник с магнитиками-заметками: пользователь добавляет новую заметку, перечёркивает выполненную, отбирает только важные. Чтобы собрать такой «холодильник» на компьютере, нам нужно сделать несколько вещей сразу: нарисовать интерфейс, запомнить, что уже сделано (состояние), откликаться на нажатия, обращаться к серверу за данными, ловить ошибки и держать код в порядке, чтобы его можно было расширять.
В этом уроке мы создадим общую карту. Вы не обязаны запомнить каждую команду. Важнее к концу занятия уметь ответить на вопрос: какая часть системы отвечает за данный процесс?
const state = {
tasks: [{ id: 1, title: "Изучить HTTP", completed: false }],
filter: "all",
loading: false
};
// после клика "Выполнено"
state.tasks[0].completed = true;
// UI должен показать зачёркнутый текст
state будет жить в любом компоненте — и в Angular, и в React.state — как листок с текущим статусом заказа в пиццерии: «в работе / готов / ошибка». Пока статус не поменялся, повара и клиент смотрят в один и тот же листок.tasks — массив задач; у каждой есть id, title и флаг completed.filter — что показываем: "all" (все), "active" (не сделанные) или "done" (сделанные). done здесь — значение фильтра, а НЕ поле задачи!loading — идёт ли сейчас загрузка с сервера.state.tasks[0].completed = true меняет флаг первой задачи.Веб-приложение - это программа, которой пользователь управляет через веб-браузер. В отличие от обычного документа, приложение хранит некоторое состояние и меняет интерфейс в ответ на действия пользователя или новые данные.
Когда вы открываете страницу интернет-магазина, читаете статью, перед вами в основном документ. Но если вы добавляете товар в корзину, меняете количество, фильтруете каталог или оформляете заказ, браузер становится интерфейсом программной системы.
Поэтому полезно различать содержимое и поведение. HTML описывает структуру, CSS - визуальное представление, JavaScript - поведение. Angular и React помогают организовать поведение и представление сложного интерфейса.
В TaskFlow состояние может включать список задач, выбранный фильтр, содержимое формы и состояние загрузки. Уже здесь видно, что UI - это не просто HTML-файл. Это визуальное представление данных и состояния.
Браузер одновременно является программой для просмотра страниц и средой выполнения frontend-кода. Он загружает ресурсы, создаёт DOM, применяет CSS, выполняет JavaScript, обрабатывает события и выполняет сетевые запросы.
После получения HTML браузер строит структуру документа. JavaScript может найти элементы, изменить их содержимое или классы, зарегистрировать обработчики событий и запросить дополнительные данные. Именно поэтому пользователь может взаимодействовать с интерфейсом без полной перезагрузки страницы.
Современный frontend использует эти возможности косвенно. Angular и React предоставляют свой способ описывать UI, но конечная работа всё равно происходит в браузере. Если вы понимаете DOM и события, вы будете лучше понимать и работу framework.
Откройте DevTools. Откройте DevTools: нажмите F12 (Windows/Linux) или Cmd+Option+I (Mac). Или щёлкните правой кнопкой мыши на странице → «Просмотреть код». В Console мы увидим сообщения и runtime-ошибки. Во вкладке Network можно посмотреть реальные запросы. В Elements можно увидеть DOM, который браузер получил после работы приложения. Это три разных окна в одну и ту же систему.
URL описывает ресурс и способ обращения к нему. В простой форме мы видим схему, хост и путь. В приложениях появляются query-параметры и фрагменты.
https://taskflow.example/tasks/42?completed=false#detailsЗдесь https - схема, taskflow.example - хост, /tasks/42 - путь, ?completed=false - query string, #details - fragment. Для frontend особенно важен путь: он может означать маршрут интерфейса или адрес ресурса API.
Например, Angular Router или React Router могут сопоставлять путь /tasks/42 с экраном задачи. При этом API может использовать /api/tasks/42. Сходство имён не означает, что это один и тот же механизм.
Людям удобнее запоминать доменные имена, а сетевые соединения используют адреса узлов. DNS связывает эти представления. Когда приложение открывается по имени, браузеру и операционной системе нужно получить информацию, позволяющую установить соединение.
Для frontend-разработчика DNS важен как часть диагностики. Если домен не разрешается, а приложение работает по IP, причина может быть вообще не в frontend-коде. Если соединение не устанавливается, поиск ошибки в компоненте не даст результата.
В production архитектура может содержать несколько DNS-записей: отдельный домен для приложения, API, статических ресурсов, CDN и других сервисов. Это одна из причин, почему полезно понимать систему целиком.
HTTP - базовый протокол взаимодействия веб-клиента и сервера. Для frontend важны метод, URL, заголовки, тело и статус ответа.
GET /api/tasks/42 HTTP/1.1
Host: taskflow.example
Accept: application/json
HTTP/1.1 200 OK
Content-Type: application/json
{
"id": 42,
"title": "Изучить HTTP"
}
/api/tasks), говорите «дайте мне вот это» (метод GET), а продавец либо даёт посылку (ответ 200 + JSON), либо «нет в наличии» (404), либо «не положено» (403).GET — «покажи список задач».Host и Accept уточняют сервер и формат ответа (JSON).200 OK — всё хорошо.id и title.Метод GET обычно используется для получения данных, POST - для создания или выполнения операции, PATCH - для частичного изменения, PUT - для замены ресурса, DELETE - для удаления. В реальном API встречаются дополнительные правила, но эта схема полезна как база.
Статус-код - ещё один обязательный элемент чтения ответа. 2xx обычно означает успех. 4xx указывает на проблему запроса клиента или его прав. 5xx - на ошибку сервера. Frontend должен обрабатывать эти состояния явно.
API задаёт правила общения frontend и backend. Хороший API позволяет клиенту знать, какие операции доступны и какой формат данных ожидается. Это особенно важно, когда две команды работают независимо.
Для TaskFlow мы можем определить простую модель CRUD: список задач, получение одной задачи, создание, изменение и удаление. Контракт описывает не только URL, но и поля, типы, обязательность, возможные ошибки.
В зрелом проекте API может описываться средствами OpenAPI. Тогда frontend может опираться на формальный контракт, а часть клиентов и типизации может генерироваться автоматически.
| Операция | Метод | Endpoint | Что получает клиент |
|---|---|---|---|
| Список | GET | /api/tasks | Task[] |
| Одна задача | GET | /api/tasks/42 | Task |
| Создание | POST | /api/tasks | Созданная Task |
| Изменение | PATCH | /api/tasks/42 | Обновлённая Task |
| Удаление | DELETE | /api/tasks/42 | Статус операции |
В production API обычно используют префикс /api. В наших учебных упражнениях с json-server используется более короткий путь /tasks.
JSON часто используется в HTTP API, потому что он компактный и легко преобразуется в объекты JavaScript. Но JSON содержит только данные. В нём нет методов класса, обработчиков или UI.
{
"id": 42,
"title": "Изучить JSON",
"completed": false,
"tags": ["web", "frontend"],
"owner": null
}
id и title — как в предыдущем примере.completed — false, значит задача ещё не выполнена.tags — это массив (список) строк, как несколько ярлыков на папке.owner: null — владелец пока не задан (пусто).На frontend эти данные обычно превращаются в структуры, с которыми работает приложение. TypeScript позволяет описать ожидаемую форму данных, но не гарантирует, что удалённый сервер реально пришлёт корректное содержимое. Поэтому на границе приложения могут понадобиться runtime-проверки.
HTML отвечает за структуру. В хорошем интерфейсе элементы выбираются не только по внешнему виду, но и по смыслу. Заголовки, формы, кнопки, списки и навигация имеют семантику.
<main>
<h1>Мои задачи</h1>
<form>
<label>
Название задачи
<input type="text" name="title">
</label>
<button type="submit">Добавить</button>
</form>
<ul>
<li>Изучить HTTP</li>
<li>Создать Angular-проект</li>
</ul>
</main>
<main> — главная область страницы.<form> с <input> — поле, куда пользователь впишет название новой задачи.<ul> / <li> — ненумерованный список задач.Семантический HTML полезен для доступности, сопровождения и понимания кода. Framework не заменяет HTML; он помогает программатически управлять им.
В дальнейшем Angular template или React JSX будут выглядеть похожими на HTML, но это не обычный статический документ: они связаны с данными и компонентами.
CSS управляет внешним видом, расположением, адаптивностью и состояниями представления. В TaskFlow задача completed может оставаться тем же объектом данных, а CSS-класс отвечает за визуальное оформление завершённой задачи.
.task {
padding: 12px;
border: 1px solid #ddd;
}
.task.completed {
text-decoration: line-through;
opacity: 0.65;
}
completed, и CSS делает её «тусклой» и перечёркнутой..task — стиль для каждой задачи: отступ и рамка..task.completed — стиль только для выполненных: перечёркивание и полупрозрачность.Это простой пример разделения ответственности. Если сервер меняет completed с false на true, бизнес-состояние меняется в данных. UI получает новый результат и применяет соответствующее оформление.
В рамках курса отдельно будем изучать flexbox, grid, responsive design и компонентные стили. На первом занятии достаточно увидеть границу: CSS описывает визуальное поведение, а не бизнес-правила вроде «кто имеет право удалить задачу».
JavaScript даёт странице поведение. Он может читать ввод, реагировать на click, отправлять запрос, считать данные и менять состояние. Именно JavaScript делает веб-приложение программой.
const tasks = []; // массив задач — наше состояние
// Функция-действие: добавляет новую задачу в состояние
function addTask(title) {
tasks.push({
id: tasks.length + 1, // простой автоинкремент id
title, // название пришло из аргумента
completed: false // новая задача ещё не выполнена
});
}
addTask('Изучить JavaScript'); // вызов меняет состояние
addTask — как кассир, который берёт название задачи, выдаёт ей номерок (id) и кладёт в общую стопку.tasks.addTask('Изучить JavaScript') в массив добавляется объект с новым id, названием и флагом completed: false.tasks лежит одна задача.С появлением десятков компонентов ручное управление DOM становится сложнее. Нужно помнить, какой элемент обновить, какие классы убрать, какие счётчики пересчитать. Framework решает проблему не потому, что JavaScript плох, а потому, что крупному приложению нужна организованная модель управления UI.
DOM представляет документ в памяти браузера. JavaScript может напрямую изменять его. Для небольшого примера это нормально. Для большого приложения ручная синхронизация DOM и состояния становится источником ошибок.
Предположим, в списке сто задач. Пользователь завершил одну. Мы должны обновить checkbox, класс, счётчик, возможно, положение элемента после фильтрации и содержимое панели статистики. Если каждый кусок UI обновляется отдельно, появляется риск рассинхронизации.
Декларативная модель говорит иначе: вот текущее состояние данных, вот правила отображения. Framework сам применяет необходимые изменения. React строит UI из компонентов и состояния; Angular использует templates и реактивную модель. В обоих случаях мы двигаемся от «какой DOM-элемент поменять» к «какое состояние должно быть».
ng serve / npm run dev).body), от него ветки (div, ul), а на ветках листики (li). JS «приклеивает» к веткам нужный текст.TypeScript расширяет JavaScript системой типов. Для frontend это особенно важно, потому что одна сущность может пройти длинный путь: API → service → state → component → template. Если форма объекта меняется, типы помогают обнаружить часть мест, требующих изменения.
// Модель данных задачи — контракт полей и типов
interface Task {
id: number; // уникальный идентификатор
title: string; // текст задачи
completed: boolean; // выполнена или нет
}
// Чистая функция: возвращает НОВЫЙ объект вместо изменения старого
function toggleTask(task: Task): Task {
return {
...task, // копируем все поля
completed: !task.completed // инвертируем только флаг
};
}Но типы не проверяют содержимое внешнего HTTP-ответа во время выполнения. Если сервер прислал completed как строку, TypeScript сам по себе не остановит сетевой ответ. Поэтому типизация и runtime validation дополняют друг друга.
В дальнейшем TypeScript станет общей основой и Angular, и React-части курса.
Сетевой запрос не завершается мгновенно. Код должен уметь ждать результат и обрабатывать ошибку. Promise представляет будущее значение или причину отказа операции.
/api/tasks?Очень важный момент для новичка: выражение fetch('/api/tasks') не создаёт API. Оно только отправляет HTTP-запрос по указанному адресу. Поэтому прежде чем использовать такой пример, нам нужно поднять сервер, который действительно отвечает на этот запрос.
В учебных упражнениях мы используем локальный mock API. Это небольшой сервер, который имитирует backend и хранит тестовые данные в JSON-файле.
taskflow-api, а не внутри Angular-проекта или React-проекта. Иначе порт может конфликтовать или клиент не найдёт mock-данные.Создайте отдельную папку для API и откройте в ней терминал:
mkdir taskflow-api
cd taskflow-api
npm init -y
npm install json-server
npm install — как покупка нужного инвентаря в хозяйственном магазине: сказали «дай json-server», и он появился в вашем наборе инструментов.taskflow-api/db.json со стартовыми данными задач (содержимое — в блоке ниже).Создайте файл db.json:
{
"tasks": [
{
"id": 1,
"title": "Изучить HTTP",
"completed": false
},
{
"id": 2,
"title": "Создать первый компонент",
"completed": false
}
]
}
Запустите mock API:
npx json-server db.json --port 3000
После запуска откройте в браузере http://localhost:3000/tasks. Вы должны увидеть JSON-массив задач. Теперь у нас действительно существует endpoint, к которому frontend может обратиться.
// async — функция возвращает Promise; await «ждёт» ответа
async function loadTasks() {
const response =
await fetch('http://localhost:3000/tasks'); // запрос к mock API
// Явно проверяем HTTP-статус, не только успех сети
if (!response.ok) {
throw new Error(`HTTP ${response.status}`);
}
// Парсим тело ответа из JSON в обычный объект JS
return await response.json();
}
await — это «подожди, пока посылку не привезут». Пока ждём, программа не виснет.await fetch(...) отправляет запрос и ждёт ответа.response.ok — проверяем, что сервер ответил успешно (не 404/500).response.json() превращает текст ответа в удобный JS-объект.В интерфейсе нужно учитывать состояние ожидания. Кнопка может быть временно отключена, пользователь должен видеть прогресс, а при ошибке - понятное сообщение и возможность повторить операцию. Поэтому работа с API - это не только одна строка fetch, а целый пользовательский сценарий.
localhost:3000.fetch — как курьер, который везёт заказ: вы отправляете fetch (курьер едет за данными), ждёте посылку (.then), распаковываете её (JSON), а если курьер упал по дороге — ловите ошибку (.catch).Frontend отвечает за взаимодействие пользователя с системой: экран, ввод, локальное состояние, навигацию, отображение ошибок. Backend отвечает за операции, которым доверять клиенту нельзя: авторизацию, бизнес-правила, доступ к базе, проверку полномочий и согласованность данных.
Например, кнопка удаления может быть скрыта в интерфейсе для обычного пользователя, но это не защита. Backend должен проверить права независимо от того, каким был интерфейс.
# Anyone can send this request regardless of UI
curl -X DELETE http://localhost:3000/tasks/42
# Server must verify permissions
if (!user.hasPermission('delete')) {
return res.status(403).json({ error: 'Denied' });
}
curl — способ отправить HTTP-запрос вручную, минуя браузер.hasPermission('delete').403 (доступ запрещён).MPA означает многостраничную модель: переход может приводить к получению нового HTML-документа. SPA строит основную оболочку приложения на клиенте и меняет экран внутри неё. Оба подхода имеют право на существование.
SPA особенно удобна для приложений, где пользователь много времени проводит внутри интерфейса: CRM, админка, редактор, система задач. MPA может быть проще и эффективнее для некоторых сайтов с преимущественно независимыми страницами.
Важно: Angular и React не означают автоматически «только SPA». Современные инструменты поддерживают разные стратегии рендеринга. Поэтому архитектура выбирается под продукт, а не под название framework.
// Angular
// Angular: массив маршрутов — URL сопоставляется с компонентом
const routes: Routes = [
{ path: 'tasks', component: TaskListPage }, // /tasks -> список
{ path: 'tasks/:id', component: TaskDetailPage } // :id — параметр
];
// React (React Router) — тот же принцип, но в JSX
<Route path="/tasks" element={<TaskListPage />} />
<Route path="/tasks/:id" element={<TaskDetailPage />} />
/tasks открывает список задач, а /tasks/5 — одну задачу с номером 5. Роутинг сопоставляет адрес и экран./tasks) вас провожают в нужный кабинет, не перестраивая здание.routes.<Route>.:id — переменная часть адреса (номер задачи).В обоих случаях Framework сопоставляет URL с нужным компонентом и отображает его. Студенту не нужно вручную загружать страницу при каждом переходе — клиентский роутинг перехватывает навигацию и обновляет только необходимую часть экрана.
CSR - Client-Side Rendering: HTML-интерфейс в значительной степени строится в браузере. SSR - Server-Side Rendering: сервер формирует HTML для запроса. SSG или prerendering - HTML формируется заранее. Hydration - процесс, при котором клиент подключает интерактивность к уже полученной HTML-разметке.
| Подход | Где строится HTML | Когда | Инструмент |
|---|---|---|---|
| CSR | В браузере | После загрузки JS | Angular, React (по умолчанию) |
| SSR | На сервере | При каждом запросе | Angular Universal, Next.js |
| SSG | На сервере | При сборке | Astro, Next.js (getStaticProps) |
Современный frontend часто смешивает стратегии. Маркетинговая страница может быть prerendered, публичный каталог - SSR, а личный кабинет - client-heavy. Такой подход сложнее, но позволяет подбирать стратегию под требования страницы.
Слова framework и library полезно рассматривать как описание степени ответственности инструмента. Angular предоставляет полноценную платформу: официальный CLI, компоненты, DI, routing, forms и HTTP-инструменты входят в общую экосистему Angular. React сосредоточен на UI-компонентах и опирается на широкую экосистему для маршрутизации, данных и других задач.
Это не делает React слабее и Angular сильнее. Разница означает разные уровни свободы. В Angular больше решений стандартизировано. В React разработчик чаще выбирает инструменты и архитектуру сам.
В актуальном Angular компоненты по умолчанию являются standalone. Официальная документация прямо указывает, что standalone components можно импортировать напрямую в другие standalone components, а старые компоненты могут использовать NgModule-ориентированный подход. Это означает, что новый курс не должен начинаться с обязательного AppModule.
Angular CLI предназначен для создания, разработки, тестирования, сборки и сопровождения приложений. В актуальной команде ng new standalone включён по умолчанию; strict mode также включён по умолчанию. Актуальная CLI-документация показывает Vitest как стандартный test runner для новых проектов.
npm install -g @angular/cli
ng new task-flow-angular
cd task-flow-angular
ng serve --open
ng new — как заказ готового набора конструктора: коробка уже с инструментами и инструкцией, осталось только собирать детали.task-flow-angular/, выполнив команды из блока ниже в терминале.Это важное обновление относительно старого учебного материала, который был приложен к курсу: старые подходы можно знать для чтения legacy-проектов, но не стоит делать их стартовой моделью обучения.
Официальный Angular описывает component как основной строительный блок приложения. У компонента есть TypeScript-класс, декоратор @Component, template и selector; у standalone-компонента также есть imports, содержащий зависимости шаблона.
import { Component } from '@angular/core'; // декоратор компонента
@Component({
selector: 'app-task-page', // тег, по которому компонент вставляется в шаблон
template: `
<!-- Заголовок страницы -->
<h1>TaskFlow</h1>
<!-- Приветственный текст -->
<p>Мои задачи</p>
`
})
export class TaskPage {} // класс хранит логику и данные компонента
@Component — «бирка», которая говорит Angular: это компонент.selector: 'app-task-page' — имя тега, как <app-task-page></app-task-page>, чтобы вставить компонент в страницу.template — что показывать (заголовок и текст).export class TaskPage — «мозг» компонента, где позже появятся данные и методы.Здесь класс содержит логику и данные, template описывает представление, selector определяет использование компонента, а decorator сообщает Angular, как этот класс должен обрабатываться.
В следующих занятиях мы научимся разделять такие компоненты, передавать данные, обрабатывать события и подключать сервисы.
Template связывает HTML-подобную разметку с данными компонента. Интерполяция {{ title }} показывает значение. Property binding задаёт свойства DOM, event binding реагирует на события. Современный Angular также предоставляет новый control flow syntax, например @if и @for.
<!-- Интерполяция: значение переменной title попадает в текст -->
<h1>{{ title }}</h1>
@if (tasks.length === 0) {
<!-- Новый control flow Angular: условный блок -->
<p>Задач пока нет.</p>
}
<!-- Event binding: клик вызывает метод компонента -->
<button (click)="addDemoTask()">
Добавить пример
</button>
{{ title }} — как рамка для фотографии: вы вставляете туда любую картинку (значение), и на стене (экране) показывается именно она. А (click) — как звонок: нажали → сработало действие.{{ title }} подставляет значение переменной в заголовок.@if показывает блок только когда задач нет.(click)="addDemoTask()" вызывает метод при нажатии.Важно понимать направление связи. Template может отображать значение, пользователь может вызвать событие, а обработчик меняет состояние компонента. Это отличается от старой модели, где разработчик вручную искал DOM-элемент и менял его.
{{ }} используется {expression} в JSX, вместо (click) — onClick={handler}, а вместо @if/@for — условные выражения JavaScript и метод .map(). Подробнее — в разделе 22.{{ }}, () и [] кажутся искусственными. После нескольких практик они читаются как обозначение направления связи.React строит UI из компонентов. Компонент - JavaScript-функция, которая возвращает JSX. Props передают данные от родителя к ребёнку. State хранит информацию, которую компонент должен помнить между рендерами.
import { useState } from 'react'; // хук состояния
export default function Counter() {
// count — текущее значение, setCount — функция обновления
const [count, setCount] = useState(0);
return (
<section>
<p>Создано задач: {count}</p>
{/* клик увеличивает состояние на 1 */}
<button onClick={() => setCount(count + 1)}>
Добавить
</button>
</section>
);
}
count хранит текущее значение (как статус заказа), а setCount его меняет. Как только статус меняется, интерфейс показывает обновлённое значение.useState(0) создаёт переменную count (начало 0) и функцию setCount для изменения.{count} выводит число на экран.setCount(count + 1) число увеличивается на 1, и React перерисовывает компонент.Официальная документация React подчёркивает, что state является локальной «памятью» компонента и хранится отдельно для каждой позиции компонента в дереве. Это важная концепция, которая позже станет основой для обсуждения state management.
useState. Оба подхода связывают данные с UI.Props позволяют сделать компонент переиспользуемым. Вместо жёстко заданной задачи TaskItem получает title и completed от родителя.
// Дочерний компонент получает данные через props
function TaskItem({ title, completed }) {
return (
<li>
{completed ? '✓' : '○'} {title}
</li>
);
}
// Родительский компонент: хранит список и композирует детей
export default function TaskList() {
const tasks = [
{ id: 1, title: 'Изучить HTTP', completed: true },
{ id: 2, title: 'Изучить React', completed: false }
];
return (
<ul>
{/* .map превращает данные в компоненты; key нужен React для повторного использования */}
{tasks.map(task => (
<TaskItem key={task.id} {...task} />
))}
</ul>
);
}
TaskItem получает title и completed через props.TaskList хранит массив задач.tasks.map(...) для каждой задачи создаёт свой <TaskItem>.key={task.id} помогает React не путать строки при обновлении.Композиция означает, что большой UI строится из меньших компонентов. React рекомендует разделять компоненты на файлы по мере роста приложения, чтобы код было легче читать и переиспользовать.
На этом этапе полезно сделать паузу и сравнить две технологии без оценки «лучше/хуже». Обе строят UI из компонентов, но дают разные инструменты для представления, state и инфраструктуры.
| Задача | Angular | React |
|---|---|---|
| Компонент | @Component + template | function + JSX |
| Данные вниз | Inputs / bindings | Props |
| Событие | Event binding / outputs | event handlers / callbacks |
| Реактивное состояние | Signals и другие API | useState и другие Hooks |
| HTTP | HttpClient и экосистема Angular | fetch и выбранные библиотеки |
| Routing | Angular Router | React Router (библиотека) |
| Forms | Reactive Forms / template-based | controlled inputs и библиотеки |
| Архитектура | более стандартизирована | больше выбора |
Мы вернёмся к этой таблице после каждого большого блока курса и будем сравнивать реальные решения на одном TaskFlow.
Node.js нужен не потому, что браузер не умеет JavaScript, а потому, что инструменты разработки должны работать за пределами браузера. Angular CLI, тестовые средства, сборщики и множество утилит запускаются в Node.js.
npm управляет пакетами и скриптами проекта. В package.json хранятся scripts и зависимости. package-lock.json фиксирует дерево конкретных версий зависимостей. Для студента важно уметь читать эти файлы, но не нужно заучивать каждое поле.
node --version
npm --version
npm install
npm run build
npm test
npm install — закупка деталей, npm run build — сборка изделия, npm test — проверка на брак перед выдачей.Git нужен, чтобы видеть историю изменений и безопасно двигаться небольшими шагами. Для учебного курса достаточно освоить несколько команд и понимать смысл каждой.
git status
git diff
git add .
git commit -m "Первый экран TaskFlow"
git log --oneline
commit — это точка сохранения, к которой можно вернуться.node_modules в коммит — убедитесь, что .gitignore существует (Angular CLI создаёт его автоматически); 2) перед git add . выполните git status и убедитесь, что вы коммитите только нужные файлы.Хороший commit - это не архив всех действий за неделю, а осмысленная точка. Например, «Добавлен список задач» лучше, чем «fix» или «сделал». В профессиональной разработке Git связан с code review и CI/CD.
Хороший frontend-разработчик не только пишет код, но и умеет наблюдать за его выполнением. В Console можно увидеть runtime-ошибку и stack trace. В Network можно увидеть запрос, его URL, метод, статус, заголовки, тело и время выполнения.
Если API вернул 404, не нужно начинать менять HTML. Если запрос вернул 500, нужно проверить сервер. Если запрос 200, но интерфейс пустой, вероятны проблемы с форматом ответа, обработкой данных или рендерингом.
Такая классификация ошибок постепенно превращает отладку из угадывания в последовательный процесс.
Проверить DNS-разрешение можно командой:
nslookup taskflow.example
Если команда вернёт IP-адрес — домен разрешается. Если ошибка — проблема в DNS, а не в приложении.
Доступность начинается с семантики. Кнопка должна быть button, поле - input с понятной подписью, изображение - с подходящим alt, структура заголовков - логичной. Framework не отменяет эти требования.
Почему это относится к архитектуре? Потому что хороший component должен иметь не только внешний вид, но и правильное поведение. Если компонент использует div вместо кнопки только ради стиля, клавиатурная навигация и доступность могут пострадать.
<div class="btn" onclick="save()">Сохранить</div><button type="button" (click)="save()">Сохранить</button><button>, <nav>, <main>); 2) Добавляйте alt к изображениям; 3) Обеспечивайте навигацию с клавиатуры (Tab, Enter, Escape).В дальнейших уроках мы будем учитывать accessibility при создании форм, диалогов, таблиц и навигации.
Всё, что выполняется в браузере, находится под контролем пользователя. Поэтому frontend не является доверенной зоной. Скрытие кнопки, disabled-состояние или проверка роли в компоненте не являются заменой серверной авторизации.
Например, если пользователь не должен удалять задачи другого пользователя, backend обязан проверить право на DELETE-запрос. Даже если кнопка удаления не отображается в UI, запрос можно сформировать вручную.
Это фундаментальная архитектурная идея: удобство пользователя обеспечивается frontend, а безопасность - сервером и инфраструктурой.
Производительность frontend зависит не от одного фактора. Важны сетевой RTT, размер JavaScript, изображения, количество запросов, порядок загрузки, серверное время и стратегия рендеринга. Поэтому утверждение «SPA быстрее» слишком общее.
На практике разработчик измеряет, а не угадывает. Lighthouse, DevTools Performance, Network и Web Vitals помогают понять, что увидит и почувствует пользователь.
В TaskFlow мы сначала построим правильную архитектуру, а затем научимся измерять. Оптимизация до измерения часто приводит к усложнению кода без реального эффекта.
Состояние должно содержать минимальный набор самостоятельных фактов. Если количество завершённых задач можно вычислить из массива tasks, отдельный completedCount обычно не нужен. Иначе два источника истины могут разойтись.
React официально рекомендует избегать redundant state. Та же логика полезна и в Angular: вычисляемое значение лучше получать из источника, чем хранить отдельно без необходимости.
const completedTasks = tasks.filter(task => task.completed);
const completedCount = completedTasks.length;Этот принцип станет особенно важным при изучении signals, computed, selectors и state management.
Не вся информация в приложении одинаковая. Local state может описывать открытие диалога или выбранную вкладку. Server state - данные, пришедшие от API и потенциально изменяющиеся вне текущего компонента.
Server state сложнее: его нужно загружать, кэшировать, обновлять, инвалидировать и обрабатывать при ошибках. Поэтому библиотеки для запросов и state management решают задачи, которые не сводятся к простому useState или одной переменной.
// Local state — локальное состояние UI
const [isOpen, setIsOpen] = useState(false);
// Оно живёт только в этом компоненте
// Server state — данные с сервера
const [tasks, setTasks] = useState([]);
useEffect(() => {
// Загрузка server state при монтировании компонента
fetch('/api/tasks')
.then(res => res.json())
.then(data => setTasks(data)); // кладём ответ в состояние
}, []); // пустой массив — эффект выполняется один раз
// Эти данные могут измениться на сервере
// независимо от действий пользователя
isOpen — локальная «галочка» открытого диалога, живёт в компоненте.tasks — данные с сервера.useEffect с пустым массивом загружает задачи один раз при старте.setTasks.В Angular и React мы разберём эту границу отдельно, потому что она существенно влияет на архитектуру приложения.
Базовых состояния три: loading, success, error. Но success делится на два подтипа: с данными и с пустым списком. Пустой список и незагруженный список - разные состояния. Во время запроса приложение ещё не знает результат. Если запрос завершился и сервер вернул [], это уже успешное, но пустое состояние.
| Состояние | Что знает приложение | Что показывает UI |
|---|---|---|
| Loading | Ответ ещё не получен | Индикатор загрузки |
| Success + data | Данные получены | Список |
| Success + empty | Данные получены, список пуст | Сообщение «пока нет задач» |
| Error | Запрос не завершился успешно | Ошибка + повторить |
Если эти состояния смешать, пользователь будет видеть «ничего» и не понимать, идёт ли загрузка или система сломалась. Это одна из самых частых ошибок первых frontend-приложений.
Перед написанием компонента полезно описать сценарий пользователя. Например: пользователь открывает /tasks, видит список, нажимает «Добавить», вводит название, сохраняет, получает новую задачу и видит её в списке.
Из сценария выводятся UI-компоненты, состояния и API-вызовы. Не наоборот. Это важный метод проектирования: сначала поведение продукта, затем техническая реализация.
Сценарий: создание задачи
1. Открыть форму
2. Ввести title
3. Нажать «Создать»
4. Показать loading
5. POST /api/tasks
6. Обработать успех или ошибку
7. Обновить список
8. Показать результат
Перед практикой проверьте Node.js, npm и Git. Установите VS Code или другой удобный редактор. Angular CLI можно установить глобально через npm согласно официальной документации.
node --version
npm --version
git --version
npm install -g @angular/cli
ng version
На этом шаге не нужно изучать все флаги CLI. Важно увидеть, что команды выполняются, версии выводятся и среда готова.
Если команда не найдена, не перескакивайте сразу к случайным решениям из поисковой выдачи: проверьте PATH, версию Node.js и способ установки. Для учебной группы лучше заранее использовать одну согласованную LTS-версию Node.js.
node --version должен показать v20 или v22. Если версия старше — обновите Node.js с официального сайта.sudo. Вместо этого установите Node.js через nvm (Node Version Manager) или переустановите Node.js с правами администратора.%APPDATA%\npm. Перезапустите терминал после установки.Создайте проект TaskFlow. Современный Angular CLI использует standalone API по умолчанию. Это важно: проект, который вы увидите, будет отличаться от приложенного старого материала, где центральное место занимает AppModule.
ng new task-flow-angular
cd task-flow-angular
ng serve --openПосле запуска измените главный шаблон так, чтобы на странице появились заголовок TaskFlow и короткое описание. Затем перезапустите или дождитесь автоматической пересборки и убедитесь, что браузер показывает изменения.
src/app/app.html. Это HTML-похожий файл со стилями и SVG-логотипом. Замените его содержимое на простой текст:<h1>TaskFlow</h1>
<p>Приложение для управления задачами</p>
Сохраните файл. Angular CLI отслеживает изменения и автоматически пересобирает приложение. Обновление страницы в браузере не требуется — hot reload подставит новый контент.
Создайте отдельный компонент для списка задач. В современном Angular standalone component можно использовать напрямую из другого standalone component. CLI создаёт его как самостоятельную единицу.
ng generate component task-list
ng generate — как пресс-форма: назвали деталь «task-list», и вам выдали готовую заготовку с нужными отверстиями.Добавьте в шаблон три задачи. Не усложняйте упражнение API, сервисами и маршрутизацией. Цель - почувствовать компонентную границу и увидеть, что экран можно собирать из нескольких частей.
<h2>Мои задачи</h2>
<ul>
<li>Изучить HTTP</li>
<li>Создать компонент</li>
<li>Подготовить TaskFlow</li>
</ul>
src/app/task-list/. Файл task-list.ts содержит класс и декоратор, task-list.html — шаблон. Не редактируйте app.ts и app.html на этом шаге — вернёмся к ним позже.Компонент TaskList создан, но он существует отдельно — его никто не использует. Чтобы он появился на экране, нужно подключить его к корневому компоненту App. В Angular 22 это делается за три шага.
Откройте src/app/app.ts. В Angular 22 standalone-компоненты подключаются через массив imports в декораторе @Component — точно так же, как подключается RouterOutlet. Добавьте импорт класса TaskList и укажите его в массиве:
import { Component, signal } from '@angular/core';
import { RouterOutlet } from '@angular/router';
import { TaskList } from './task-list/task-list';
@Component({
selector: 'app-root',
imports: [RouterOutlet, TaskList],
templateUrl: './app.html',
styleUrl: './app.css'
})
export class App {
protected readonly title = signal('task-flow-angular');
}
imports — как список гостей на вечеринке: пока TaskList не в списке, его не пустят в дом (а значит, тег <app-task-list> не сработает).Обратите внимание: путь './task-list/task-list' указывает на файл task-list.ts внутри папки task-list/. Расширение .ts указывать не нужно — TypeScript разберётся сам.
TaskList в imports, Angular не узнает селектор <app-task-list> и выдаст ошибку в консоли браузера: «'app-task-list' is not a known element».Теперь, когда TaskList импортирован, его селектор <app-task-list> становится доступен в шаблоне. Откройте src/app/app.html и полностью замените его содержимое на:
<div class="app-container">
<h1>TaskFlow</h1>
<app-task-list />
</div>
<router-outlet />
<app-task-list /> — как розетка: вы вставляете в неё готовый прибор (компонент), и он начинает работать в этом месте страницы.Строка <app-task-list /> — это само-закрывающийся тег. Angular видит его, находит соответствующий компонент в imports и вставляет шаблон task-list.html прямо в это место. На экране вы увидите заголовок «TaskFlow» и список задач.
app.html и замените на код выше.Откройте src/app/app.css и добавьте стили:
:host {
display: block;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
Helvetica, Arial, sans-serif;
}
.app-container {
max-width: 600px;
margin: 0 auto;
padding: 2rem 1rem;
}
h1 {
text-align: center;
color: #333;
margin-bottom: 2rem;
}
:host — как покраска самого дома снаружи: правило применяется к зданию целиком, а не к отдельной комнате.Свойство :host стилизует сам корневой элемент <app-root>. Без него display: block не сработает, потому что по умолчанию Angular-компонент — инлайновый.
| Файл | Что сделано | Зачем |
|---|---|---|
app.ts | Добавлен импорт TaskList в imports | Сообщить Angular, что корневой компонент знает про TaskList |
app.html | Заменён шаблон, добавлен <app-task-list /> | Вставить содержимое task-list.html в разметку главной страницы |
app.css | Добавлены стили контейнера | Оформление страницы: шрифт, центрирование, отступы |
В Angular standalone-компоненты — это автономные единицы. Каждый имеет свой селектор, шаблон и стили. Чтобы использовать один компонент внутри другого, достаточно:
imports массивНе нужен NgModule, не нужно регистрировать компоненты в модулях — всё работает напрямую. Это одно из ключевых отличий Angular 22 от предыдущих версий.
Запустите приложение и убедитесь, что всё работает:
npm start
npm start — как нажать «вкл» на собранном приборе: всё, что мы собирали, оживает и открывается в браузере.Откройте http://localhost:4200. На экране вы должны увидеть заголовок TaskFlow и список из трёх задач под ним. Если компонент не появился — проверьте консоль браузера на наличие ошибок компиляции.
app.ts, app.html и task-list/task-list.ts сохранены.В отличие от предыдущего фрагмента, здесь мы не предполагаем, что React уже установлен. Сначала создадим полноценный проект. Для учебного курса используем Vite и шаблон React + TypeScript.
npm create vite@latest taskflow-react -- --template react-ts
cd taskflow-react
npm install
npm run dev
npm create vite — как выбор пустого трейлера под ваши нужды: вы сказали «хочу React + TypeScript», и вам собрали базу, которую осталось наполнить.После npm run dev Vite покажет локальный адрес, обычно вида http://localhost:5173. Откройте его в браузере. Теперь у нас есть настоящий React-проект, а не изолированный пример кода.
В типичном проекте Vite основная точка приложения находится в src. Файл App.tsx содержит корневой компонент, а main.tsx запускает React-приложение в браузере.
Замените содержимое src/App.tsx на:
type Task = {
id: number;
title: string;
completed: boolean;
};
const tasks: Task[] = [
{ id: 1, title: 'Изучить HTTP', completed: false },
{ id: 2, title: 'Создать UI', completed: false },
{ id: 3, title: 'Изучить React', completed: false }
];
function TaskList() {
return (
<ul>
{tasks.map(task => (
<li key={task.id}>
{task.title}
</li>
))}
</ul>
);
}
export default function App() {
return (
<main>
<h1>TaskFlow</h1>
<TaskList />
</main>
);
}
map() — как конвейер на заводе: на входе лежит список деталей (задач), на выходе — готовые коробки (<li>). Одна деталь → один элемент списка.Task описывает форму задачи.tasks — готовый список из трёх задач.TaskList через map() превращает каждую задачу в строку списка.App собирает заголовок и список вместе.Здесь студент видит несколько важных идей одновременно: React-компонент является функцией, JSX находится внутри TypeScript-кода, массив можно обрабатывать обычным JavaScript методом map(), а key нужен для стабильной идентификации элементов списка.
Возьмём простой сценарий: отобразить название задачи и кнопку. В Angular шаблон использует Angular syntax, а в React JSX использует JavaScript-выражения и props.
Angular
<h3>{{ task.title }}</h3>
<button (click)="toggleTask()">
Изменить статус
</button>React
<h3>{task.title}</h3>
<button onClick={toggleTask}>
Изменить статус
</button>
(click) против onClick), но обе нажимают «переключить канал».Смысл почти одинаковый: показать данные и связать действие с обработчиком. Синтаксис отличается, а ментальная модель похожа. Именно такие параллели будут основой второй половины курса.
Теперь переведём идею состояния из теории в работающий код. Сначала сделаем это в React, потому что здесь хорошо видно разделение между текущим значением и функцией его изменения.
import { useState } from 'react';
export default function TaskStatus() {
const [completed, setCompleted] = useState(false);
return (
<section>
<p>
Статус: {completed ? 'Готово' : 'В работе'}
</p>
<button onClick={() => setCompleted(!completed)}>
Изменить статус
</button>
</section>
);
}
completed — это статус (в работе / готово), а setCompleted его переключает. Как только статус меняется, интерфейс показывает новое слово сам, без вашего участия.useState(false) — статус изначально «В работе».<p> через ? : выбираем текст в зависимости от completed.setCompleted(!completed) значение переворачивается.После клика setCompleted изменяет состояние, React выполняет новый рендер компонента, и текст на странице меняется. Это простой пример state-driven UI: интерфейс зависит от состояния.
В современном Angular для реактивного локального значения можно использовать signal:
import { Component, signal } from '@angular/core';
@Component({
selector: 'app-task-status',
template: `
<p>Статус: {{ completed() ? 'Готово' : 'В работе' }}</p>
<button (click)="toggle()">
Изменить статус
</button>
`
})
export class TaskStatusComponent {
completed = signal(false);
toggle() {
this.completed.update(value => !value);
}
}
signal(false) — реактивное значение «не готово».completed() (со скобками!) читает значение в шаблоне.toggle() через update() переворачивает значение.Здесь completed() читает значение signal, а update() изменяет его. В следующем Angular-блоке курса мы разберём signals подробнее, включая computed() и связь с template.
Самостоятельная работа в этом пособии построена по принципу «сначала попытка, затем проверка». Не открывайте ответы сразу: сначала попробуйте решить задачу самостоятельно, используя код и схемы из урока.
Если задача не решается, используйте подсказку. Подсказка должна помочь выбрать направление, но не обязательно выдавать готовый код целиком.
После выполнения задания сравните своё решение с кратким ответом в конце. Совпадение строк кода не является целью. Важно, чтобы совпадала логика и результат.
// Находим элемент и меняем вручную
const li = document.querySelector('#task-1');
li.style.textDecoration = 'line-through';
li.classList.add('completed');
document.querySelector('#counter').textContent = count;
<!-- Описываем результат при данном состоянии -->
<li [class.completed]="task.completed">
{{ task.title }}
</li>
<span>Выполнено: {{ completedCount() }}</span>
Во втором случае мы описываем что должно быть, а Framework применяет изменения к DOM самостоятельно.
Попробуйте устно объяснить следующую историю. Пользователь вводит адрес TaskFlow. Браузер разбирает URL, получает сетевую информацию, устанавливает HTTPS-соединение и делает HTTP-запрос. Сервер отвечает HTML и/или данными. JavaScript/TypeScript формирует интерактивный UI. Пользователь нажимает «Создать». Frontend формирует запрос к API. Backend проверяет данные и права, сохраняет новую запись и возвращает ответ. UI обновляет state и показывает новую задачу.
Если вы можете объяснить эту цепочку без подсказок, то основная цель занятия достигнута. Angular и React на следующем уровне помогут организовать клиентскую часть этой цепочки.
После этой базы мы перейдём к Angular практически. Сначала - компоненты и templates. Затем - data binding (связь данных с шаблоном), события, conditional rendering (условный рендеринг), списки, inputs/outputs (передача данных между компонентами), lifecycle (этапы жизни компонента от создания до уничтожения), services (сервисы для бизнес-логики) и dependency injection (механизм предоставления зависимостей компоненту). После этого появятся forms, HTTP, routing, signals и state.
Каждая тема будет привязана к TaskFlow. Например, forms - это создание задачи, HTTP - получение и сохранение задач, routing - экран списка и экран отдельной задачи, signals - реактивное состояние фильтров и списка.
Такой порядок позволяет не изучать API изолированно. Каждый новый механизм появляется как решение конкретной задачи продукта.
После Angular мы построим React-версию TaskFlow. Сначала компоненты и JSX, затем props, state, события, списки и формы. Затем Hooks (механизмы React для state и побочных эффектов), Context (— способ передачи данных компонентам без props), производительность и тестирование, производительность и тестирование.
Особое внимание будет уделено не копированию Angular-модели, а пониманию React mental model. Например, state в React локален компоненту, а его организация зависит от места компонента в дереве. Официальная документация отдельно подчёркивает принципы выбора структуры state и избегания избыточных данных.
В конце мы сможем реализовать одинаковые сценарии TaskFlow двумя подходами и предметно сравнить решения.
Задание 1. Нарисуйте архитектуру TaskFlow от пользователя до базы данных и подпишите ответственность каждого уровня.
Задание 2. Создайте JSON с пятью задачами. Для каждой используйте id, title, completed и createdAt.
Задание 3. Создайте небольшой HTML/JS-прототип списка задач с добавлением новой записи.
Задание 4. Создайте и запустите Angular-проект task-flow-angular.
Задание 5. Сравните Angular и React в восьми предложениях: что общего, а что различается по архитектуре.
1. Что делает браузер? (раздел 3) 2. Что такое URL? (раздел 4) 3. Зачем нужен DNS? (раздел 5) 4. Что содержит HTTP-запрос? (раздел 6) 5. Что означает статус 404? (раздел 6) 6. Что такое API? (раздел 7) 7. Чем JSON отличается от базы данных? (раздел 8) 8. Для чего нужен TypeScript? (раздел 13) 9. Почему компонент является удобной единицей архитектуры? (разделы 20, 22) 10. Что такое state? (разделы 31, 32) 11. Что такое SPA? (раздел 16) 12. Чем CSR отличается от SSR? (раздел 17) 13. Почему frontend не является доверенной зоной? (разделы 15, 29) 14. Зачем нужен Node.js? (раздел 25) 15. Для чего нужен npm? (раздел 25) 16. Почему нужен Git? (раздел 26) 17. Что общего у Angular и React? (разделы 18, 24) 18. Чем отличается их экосистемный подход? (раздел 18) 19. Почему не стоит дублировать derived state? (раздел 31) 20. Что нужно проверить в Network при ошибке API? (раздел 27)
Этот раздел нужен для самостоятельной сверки. Сначала выполните работу без подсказок. Затем проверьте идею решения.
Подсказка: нарисуйте не инструменты, а ответственность уровней.
Пользователь
↓
Браузер / Angular / React
↓
HTTP API
↓
Backend
↓
DatabaseОжидаемая идея: frontend отвечает за UI и клиентское взаимодействие, backend — за серверные правила и доступ к данным, database — за долговременное хранение.
Минимальный пример:
{
"id": 1,
"title": "Изучить HTTP",
"completed": false,
"createdAt": "2026-08-17"
}Остальные четыре записи должны иметь другие id.
Подсказка: заведите массив задач, найдите ul и после добавления заново сформируйте список.
Главная проверка: после клика появляется новая запись, а не просто сообщение в Console.
Минимум: выполнить ng new task-flow-angular, перейти в каталог, выполнить ng serve --open и изменить стартовый template.
Проверяйте смысл, а не формулировку: обе технологии компонентные; Angular предлагает более встроенную платформу, React концентрируется на UI и оставляет больше инфраструктурных решений экосистеме.
Если fetch('http://localhost:3000/tasks') возвращает ошибку, проверьте три вещи:
/tasks в db.json.| Термин | Определение |
|---|---|
| Browser | Среда, которая загружает и выполняет веб-код. |
| DOM | Объектное представление документа в браузере. |
| HTTP | Протокол веб-обмена сообщениями. |
| URL | Адрес ресурса. |
| API | Контракт взаимодействия между программами. |
| JSON | Формат структурированных данных. |
| Frontend | Клиентская часть приложения. |
| Backend | Серверная часть приложения. |
| Component | Переиспользуемый строительный блок UI. |
| State | Данные, определяющие текущее состояние интерфейса. |
| CSR | Формирование UI преимущественно на клиенте. |
| SSR | Формирование HTML на сервере. |
| SSG | Предварительное формирование HTML. |
| Framework | Инструмент/платформа, задающая значительную часть структуры приложения. |
| Library | Переиспользуемый код, который приложение вызывает по необходимости. |
| TypeScript | Язык с типовой системой поверх JavaScript. |
| Node.js | Среда выполнения JavaScript вне браузера. |
| npm | Менеджер пакетов и скриптов. |
| Git | Система контроля версий. |
| Promise | Объект, представляющий будущий результат асинхронной операции. |
| async/await | Синтаксис для удобной работы с Promise внутри асинхронных функций. |
| Props | Входные данные React-компонента, переданные родителем. |
| Hook | Специальная React-функция для использования state и других возможностей внутри функционального компонента. |
| Signal | Реактивное значение Angular, изменение которого может отслеживаться системой реактивности. |
| DI | Dependency Injection: получение зависимости из внешнего механизма создания зависимостей. |
| Hydration | Подключение клиентской интерактивности к HTML, который уже был сформирован сервером. |
| Mock API | Учебная имитация backend API, обычно используемая для разработки frontend без настоящего сервера. |
| Vite | Инструмент разработки и сборки frontend-проектов; в курсе используется для создания React-приложения. |
| JSX | Синтаксис, позволяющий описывать UI-структуру внутри JavaScript/TypeScript-кода React-компонента. |
| Key | Стабильный идентификатор элемента списка, который React использует для сопоставления элементов между обновлениями. |
| Endpoint | Конкретный адрес API, по которому клиент может выполнить определённую операцию. |
| Runtime | Среда, в которой фактически выполняется программа. |
Этот курс ориентируется на современный workflow и поэтому ссылки должны использоваться как часть обучения, а не только как список литературы.
Angular. Для создания проекта используйте официальную документацию Installation и ng new. В актуальном CLI standalone API включён по умолчанию, а ng serve --open запускает локальный development server.
Angular standalone. Angular рекомендует использовать standalone components вместо NgModule для нового кода. Это особенно важно как противопоставление старому приложенному учебному материалу, где центральное место занимал AppModule.
React и Vite. Для React в этом курсе используется шаблон react-ts через Vite. Официальный Vite предоставляет шаблоны react и react-ts.
Примечание о версиях. Команды CLI и tooling развиваются. Если студент проходит курс спустя значительное время после его публикации, сначала нужно сверить команды с официальной документацией, а уже затем сравнивать результат с текстом пособия.
Новичку особенно трудно понять, где искать проблему. Используйте одинаковый порядок проверки.
ng version и затем ng serve без дополнительных флагов, чтобы увидеть полное сообщение.npm install, затем npm run dev.http://localhost:3000/tasks.Выполните упражнение без framework. Создайте кнопку «Загрузить задачи». При нажатии выведите в Console сообщение, затем выполните GET-запрос к тестовому endpoint. Проследите запрос во вкладке Network и сравните данные в Response с тем, что выводится в Console.
async function load() {
console.log('Начинаем загрузку');
const response = await fetch('/api/tasks');
const data = await response.json();
console.log('Получено:', data);
}Главная цель лаборатории - увидеть полный цикл: событие пользователя → JavaScript → HTTP → response → данные. Если backend ещё не подготовлен, используйте локальный mock или любой согласованный учебный endpoint.
ПримерСоздайте простой компонент, который различает loading, success и error. На старте покажите кнопку. После нажатия установите loading. Если запрос успешен, покажите список. Если произошла ошибка, покажите сообщение и кнопку повторной попытки.
Это упражнение кажется простым, но оно вводит важную идею state machine. Интерфейс не просто «есть» или «нет». Он находится в одном из нескольких состояний, а переходы между ними вызываются событиями и результатами операций.
idle -> loading -> success
-> error -> loading
import { useState } from 'react';
export default function TaskLoader() {
const [status, setStatus] = useState('idle');
const [tasks, setTasks] = useState([]);
const [error, setError] = useState(null);
// TODO: реализуйте handleLoad
// 1) setStatus('loading')
// 2) fetch tasks
// 3) setStatus('success') или setStatus('error')
return (
<div>
{status === 'idle' && <button onClick={handleLoad}>Загрузить</button>}
{status === 'loading' && <p>Загрузка...</p>}
{status === 'error' && <p>Ошибка: {error}</p>}
{status === 'success' && (
<ul>{tasks.map(t => <li key={t.id}>{t.title}</li>)}</ul>
)}
</div>
);
}
Опишите Task так, чтобы модель была достаточна для первого прототипа, но не содержала лишней информации. Затем придумайте, какие поля нужны только UI, а какие приходят от сервера.
interface Task {
id: number;
title: string;
completed: boolean;
createdAt: string;
}
interface TaskForm {
title: string;
}
TaskForm — это черновик (нужно только имя), а Task — готовая запись, куда сервер добавил номер и дату.Разделение Task и TaskForm полезно: форма создания не обязана знать id и createdAt до отправки на сервер. Такой маленький пример показывает, почему типы помогают моделировать границы данных.
ПримерНарисуйте дерево TaskFlow и распределите ответственность: AppShell, Header, Sidebar, TaskPage, TaskList, TaskItem, TaskForm, FilterBar. Для каждого компонента запишите две вещи: какие данные ему нужны и какие события он может инициировать.
AppShell: данные=__, события=__
Header: данные=__, события=__
Sidebar: данные=__, события=__
TaskPage: данные=__, события=__
TaskList: данные=__, события=__
TaskItem: данные=__, события=__
TaskForm: данные=__, события=__
FilterBar: данные=__, события=__
Заполните шаблон, опираясь на код из предыдущих разделов. Не существует единственного правильного ответа — важно уметь обосновать свой выбор.
Если два компонента начинают постоянно обмениваться большим числом данных, подумайте, не находится ли их общее состояние слишком низко или слишком высоко. Это станет центральной темой при изучении state architecture.
ПримерВозьмите одну задачу: «Показать список задач и кнопку изменения статуса». Сначала опишите поведение словами: данные приходят в компонент; UI показывает title и completed; click вызывает действие; state меняется; UI обновляется.
После этого отдельно реализуйте её на Angular и React. Не переносите синтаксис напрямую. Сначала сформулируйте архитектуру, затем выберите соответствующий механизм каждой технологии.
| Шаг | Angular | React |
|---|---|---|
| Получить данные | component/service/API | component/API или hook |
| Передать данные | input/binding | props |
| Событие | event binding/output | callback/event handler |
| Изменить состояние | signal/service и др. | setState/reducer и др. |
| Отобразить | template | JSX |
После первого занятия у вас должна появиться не коллекция команд, а рабочая ментальная модель.
На следующем занятии мы перестанем говорить о framework в общих терминах и начнём строить Angular-приложение как систему компонентов. С этого момента каждая теория будет сразу превращаться в код и часть TaskFlow.
Актуальность технических сведений проверена 17 августа 2026 года. При выпуске нового учебного потока версии Node.js, Angular CLI и другого tooling следует сверить с официальной документацией.
| Термин | Жизненная аналогия | Коротко |
|---|---|---|
| Angular | Конструктор с подробной инструкцией: взял коробку — и собирай по шагам | Полноценный фреймворк для веб-приложений (компоненты, маршруты, формы) |
| React | Набор качественных инструментов, из которых ты сам строишь свой набор | Библиотека для создания интерфейсов из компонентов (ядро — только UI) |
| Компонент | Деталь конструктора или запчасть машины: маленькая, понятная, соединяется с другими | Независимый кусочек интерфейса с данными и поведением |
| Фреймворк | Конструктор с инструкцией: он говорит, где что лежит и в каком порядке собирать | Каркас приложения, задающий правила и структуру |
| Библиотека | Набор инструментов в ящике: берёшь нужное, остальное решаешь сам | Набор готовых функций, которые вызываешь по своему усмотрению |
| Шаблон (template) | Чертёж детали: на нём помечено, куда вставить надписи и кнопки | Разметка компонента, связанная с его данными |
| Дерево компонентов | Матрёшка: большая фигура внутри содержит всё меньшие и меньшие | Вложенность компонентов: один содержит другие |
1. Angular и React — это одно и то же, просто разные названия одной библиотеки. Верно или неверно?
Ответ: Неверно. Angular — это фреймворк (конструктор с инструкцией: всё «из коробки»), а React — библиотека (набор инструментов, где остальное ты выбираешь сам).
2. Компонент — это целое готовое приложение, которое нельзя разбить на части. Верно или неверно?
Ответ: Неверно. Компонент — это деталь конструктора: маленькая, понятная часть, из которых собирается большое приложение, как матрёшка из вложенных фигур.
3. Шаблон компонента похож на чертёж, куда подставляются данные. Верно или неверно?
Ответ: Верно. Шаблон — как чертёж детали: на нём отмечено место для текста, кнопок и списков, а конкретные значения приходят из данных компонента.