Почему портфолио решает всё, или как не застрять на уровне «Hello, World»
На собеседованиях по JavaScript работодатель смотрит не на список пройденных курсов, а на три вещи: код, логику и умение доводить дело до конца. Именно проекты для портфолио на JavaScript становятся тем самым мостом между «я знаю теорию» и «я могу решить задачу бизнеса».
Проблема многих новичков — они застревают в бесконечных туториалах. «Сделай 10 проектов за неделю» — звучит круто, но на выходе получается 10 клонов одного и того же калькулятора. Я предлагаю другой путь: 5 проектов, которые реально покажут ваш уровень и закроют 90% требований вакансий Junior/Middle JavaScript Developer.
В этой статье вы найдёте не просто идеи, а готовые архитектурные решения, куски реального кода и объяснение, почему именно этот проект выделит вас среди толпы кандидатов. Поехали?
1. To-Do List с продвинутой логикой: не просто «добавить-удалить»
Самый банальный проект на свете. Но если вы сделаете его как все — вас не заметят. Сделайте Kanban-доску с drag-and-drop на чистом JavaScript. Это покажет, что вы умеете работать с DOM-событиями, LocalStorage и асинхронностью.
Что должно быть внутри
- Три колонки: «В планах», «В работе», «Готово».
- Перетаскивание карточек с сохранением позиции в LocalStorage.
- Фильтрация по тегам (работа/личное/срочно).
- Тёмная тема (показывает заботу о UX).
Пример реализации drag-and-drop на чистом JS
const draggables = document.querySelectorAll('.task');
const containers = document.querySelectorAll('.column');
draggables.forEach(task => {
task.addEventListener('dragstart', () => {
task.classList.add('dragging');
});
task.addEventListener('dragend', () => {
task.classList.remove('dragging');
saveToLocalStorage(); // кастомная функция сохранения
});
});
containers.forEach(column => {
column.addEventListener('dragover', e => {
e.preventDefault();
const afterElement = getDragAfterElement(column, e.clientY);
const draggable = document.querySelector('.dragging');
if (afterElement == null) {
column.appendChild(draggable);
} else {
column.insertBefore(draggable, afterElement);
}
});
});
function getDragAfterElement(container, y) {
const draggableElements = [...container.querySelectorAll('.task:not(.dragging)')];
return draggableElements.reduce((closest, child) => {
const box = child.getBoundingClientRect();
const offset = y - box.top - box.height / 2;
if (offset < 0 && offset > closest.offset) {
return { offset: offset, element: child };
} else {
return closest;
}
}, { offset: Number.NEGATIVE_INFINITY }).element;
}
Почему это круто: вы показываете понимание событийной модели браузера, работу с координатами и оптимизацию производительности (debounce не нужен, но можно упомянуть в readme).
2. Погодное приложение с геолокацией и анимациями
API-запросы — база современного фронтенда. Проект на JavaScript с использованием fetch, async/await и обработкой ошибок — обязательный пункт в портфолио. Добавьте визуализацию: анимированные облака, дождь или снег на CSS/Canvas.
Ключевые фичи
- Определение местоположения через Geolocation API.
- Поиск городов с подсказками (debounce на input).
- Отображение температуры, влажности, ветра и иконки погоды.
- График температуры на 5 дней (Chart.js или кастомный SVG).
Пример запроса к OpenWeatherMap
async function getWeather(city) {
const API_KEY = 'ваш_ключ';
const url = `https://api.openweathermap.org/data/2.5/weather?q=${city}&appid=${API_KEY}&units=metric&lang=ru`;
try {
const response = await fetch(url);
if (!response.ok) {
throw new Error(`Город не найден: ${response.status}`);
}
const data = await response.json();
displayWeather(data);
} catch (error) {
showError(error.message);
}
}
function displayWeather(data) {
const { temp, humidity } = data.main;
const { speed } = data.wind;
const description = data.weather[0].description;
document.querySelector('.temp').textContent = `${Math.round(temp)}°C`;
document.querySelector('.humidity').textContent = `Влажность: ${humidity}%`;
document.querySelector('.wind').textContent = `Ветер: ${speed} м/с`;
document.querySelector('.desc').textContent = description;
}
Совет: спрячьте API-ключ в переменные окружения или используйте серверную прослойку (например, Netlify Functions). Это покажет, что вы думаете о безопасности.
3. SPA-приложение «Менеджер паролей» (без бэкенда)
Тема безопасности всегда в тренде. Сделайте приложение, которое шифрует пароли с помощью Web Crypto API и хранит их в LocalStorage. Это покажет, что вы разбираетесь в криптографии на уровне браузера.
Архитектура
- Главный пароль (master password) для доступа.
- Генератор случайных паролей (с настройками длины и символов).
- Шифрование AES-GCM перед сохранением.
- Копирование пароля в буфер обмена одной кнопкой.
Фрагмент шифрования
async function encryptData(plaintext, password) {
const encoder = new TextEncoder();
const keyMaterial = await crypto.subtle.importKey(
'raw',
encoder.encode(password),
'PBKDF2',
false,
['deriveKey']
);
const key = await crypto.subtle.deriveKey(
{ name: 'PBKDF2', salt: encoder.encode('salt123'), iterations: 100000, hash: 'SHA-256' },
keyMaterial,
{ name: 'AES-GCM', length: 256 },
false,
['encrypt']
);
const iv = crypto.getRandomValues(new Uint8Array(12));
const encrypted = await crypto.subtle.encrypt(
{ name: 'AES-GCM', iv: iv },
key,
encoder.encode(plaintext)
);
return { iv: Array.from(iv), data: Array.from(new Uint8Array(encrypted)) };
}
Почему это выделит вас: 90% джунов не знают про SubtleCrypto. Этот проект покажет глубокое понимание безопасности и работы с бинарными данными.
4. Реалтайм-чат на WebSocket (без фреймворков)
Сделайте простой чат с комнатами на WebSocket и Node.js (или Deno). Это покажет, что вы умеете работать с сетевым взаимодействием и серверным JavaScript.