В статье рассмотрим, как сделать простое приложение погоды без дизайн-макета. Поработаем с HTML, CSS, JavaScript, Vue, Vite, подключением API, а также развернем проект на облачном сервере. Подробности под катом!
Используйте навигацию, если не хотите читать текст полностью:
→ Начало работы с Vite
→ Верстка приложения
→ Подготовка фреймворка Vue
→ Деплой проекта
→ Заключение
Начало работы с Vite
В качестве инструмента сборки будем использовать Vite. С его помощью мы легко запустим любой JavaScript-фреймворк.
Первым шагом установим Vite. Создаем папку на компьютере, которая будет сохранять данные проекта, и переносим ее в редактор кода VS Code. Далее открываем терминал и вводим код, чтобы создать проект из шаблона:
npm create vite@latest
Теперь добавим в проект необходимые пакеты. Для этого нужно дать согласие системе — пишем «y» и нажимаем Enter.
В ответ получаем конфигурационные файлы и строку Project name. В ней указываем имя будущего проекта. У меня — weather, но можно назвать по-другому. Нажимаем Enter, чтобы продолжить установку.
Далее выбираем необходимый фреймворк и язык программирования. В нашем случае — Vue и JavaScript.
После — вводим в терминал три команды, чтобы начать работу над проектом:
cd weather
npm install
npm run dev
В результате получаем ссылку localhost. Переходим по ней и видим стандартную страницу:
Верстка приложения
На странице Vite и Vue нужно убрать все лишнее, заполнить ее разметкой и стилями будущего проекта. Это поможет понять, как он будет выглядеть и с какими элементами нам придется работать.
Сейчас проект состоит из следующих файлов и папок:
Обратите внимание на папку src — именно с ней мы будем работать. При этом все изменения данных будут отражаться в браузере в момент разработки.
Внутри src есть две папки — assets и components. Поскольку мы делаем простое приложение, предлагаю удалить последнюю папку и ее содержимое. На реальных проектах этого делать не рекомендую, но в нашем случае она ни на что не повлияет. В assets удаляем лишнее изображение формата svg. Саму папку не трогаем.
Далее открываем файл App.vue, в котором отражены ошибки. Они появились из-за того, что мы удалили папку components. Нам внутреннее содержимое файла не потребуется, поэтому просто удалим его. В результате файл должен быть пустым.
Теперь переходим к созданию разметки. Указываем основные теги template, внутрь добавляем div с классом weather и ниже размещаем скрипт:
<template>
<div class="weather"></div>
</template>
<script>
</script>
В div добавляем класс с контейнером, чтобы разместить поисковую строку, кнопку и информацию о погоде. Ниже — класс weather-bg для хранения изображений.
<template>
<div class="weather">
<div class="container">
<div class="card weather-form">
<input type="text" class="weather-form__input" placeholder="Enter city">
<button class="weather-form__btn">Search</button>
</div>
<div class="weather-info">
<div class="weather-info__text">
<p class="card">Phuket</p>
<p class="card">30°C</p>
<p class="card">Sunny</p>
</div>
</div>
</div>
<div class="weather-bg">
<div>
<img class="weather-bg__img bg" src="./assets/bg.jpg" alt="App Background">
<img class="weather-bg__img overcast" src="./assets/overcast.jpg" alt="App Background">
<img class="weather-bg__img partly-cloudy" src="./assets/partly-cloudy.jpg" alt="Partly Cloudy">
<img class="weather-bg__img sunny" src="./assets/sunny.jpg" alt="Sunny">
</div>
</div>
</div>
</template>
Заранее сохраняем картинку, которая будет отображаться при загрузке приложения. Для остальных описываем разные состояния погоды. За основу возьмем overcats (пасмурно), partly cloudy (переменная облачность) и sunny (солнечно). Подходящие фоны можно найти на любом сервисе стоковых изображений.
Все изображения переносим в папку assets. В файл style.css добавляем основные стили. Подключаем шрифт, прописываем переменные и указываем стандартные сбросы.
@import url('https://fonts.googleapis.com/css2?family=Karla:ital,wght@0,200..800;1,200..800&display=swap');
:root {
--font: "Karla", sans-serif;
--accent: #12C0DD;
--accent-rgb: 18, 192, 221;
--light: #FFFFFF;
--border-radius: 10px;
--width-line: 2px;
--tr: .3s;
}
* {
margin: 0;
padding: 0;
}
body, html {
height: 100vh;
}
body {
font-family: var(--font);
height: 100%;
}
input, button {
font-family: var(--font);
}
#app {
height: 100%;
}
Далее стилизуем блок с контентом. Центрируем его посередине экрана, указываем возможность изменения фона и прописываем функцию добавления классов, чтобы они могли меняться в зависимости от типа погоды.
Основные стили:
.weather {
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
position: relative;
overflow: hidden;
}
.weather-bg {
position: absolute;
left: 0;
right: 0;
top: 0;
margin: 0 auto;
width: 100%;
height: 100%;
z-index: -1;
}
.weather-bg > div {
width: 100%;
height: 100%;
position: relative;
overflow: hidden;
}
Основные стили для изображений:
.weather-bg__img {
width: 100%;
height: 100%;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
object-fit: cover;
opacity: 0;
transition: var(--tr) ease-in-out;
}
Стиль для основного фона:
.weather .weather-bg__img.bg {
opacity: 1;
}
Стиль для разных типов погоды. Для примера рассмотрим солнечную (sunny), но вы можете установить несколько вариантов и подобрать несколько состояний — вместо sunny нужно будет записать другие варианты:
.weather.sunny .weather-bg__img:not(.sunny) {
opacity: 0;
}
.weather.sunny .weather-bg__img.sunny {
opacity: 1;
}
.container {
width: 100%;
max-width: 1000px;
margin: 0 auto;
padding-left: 15px;
padding-right: 15px;
display: grid;
grid-template-columns: 1fr 100px 200px;
gap: 20px;
box-sizing: border-box;
}
.card {
background-color: var(--light);
border-radius: var(--border-radius);
padding: 20px 30px;
box-sizing: border-box;
}
.weather-form {
display: flex;
align-items: stretch;
gap: 20px;
}
.weather-form__input {
flex-grow: 1;
font-size: 20px;
border: var(--width-line) solid rgba(var(--accent-rgb), .3);
border-radius: var(--border-radius);
padding: 10px 15px;
box-sizing: border-box;
transition: var(--tr);
}
.weather-form__input:focus {
outline: none;
border-color: var(--accent);
}
.weather-form__btn {
flex-basis: 180px;
font-size: 20px;
background-color: rgba(var(--accent-rgb), .3);
border: none;
border-radius: var(--border-radius);
padding: 10px 15px;
box-sizing: border-box;
cursor: pointer;
transition: var(--tr);
}
.weather-form__btn:hover {
background-color: var(--accent);
}
.weather-form,
.weather-load,
.weather-info {
grid-column: 1 / 4;
}
.weather-load {
display: flex;
align-items: center;
height: 87px;
}
.weather-info__text {
display: grid;
grid-template-columns: 1fr auto auto;
gap: 20px;
font-size: 40px;
}
Подготовка фреймворка Vue
Ранее мы запустили инструмент сборки Vite. Вся структура приложения уже подготовлена за нас. Осталось внести изменения в разметку и дописать скрипты.
Информацию о погоде будем получать из реальных источников. Для этого воспользуемся любым бесплатным API. В нашем случае — Weather API.
Переходим на сайт и авторизируемся, чтобы получить ключ для работы с API. Открываем файл App.vue. Нас интересует блок:
<script>
export default {
data() {
return {
};
},
computed: {
weatherClass() {
}
},
methods: {
weatherSearch() {
},
resetSearchQuery() {
}
}
};
</script>
В нем прописываем основную структуру кода.
- В data будем управлять полученными данными.
- В computed пропишем изменения фона приложений, в зависимости от типа погоды.
- В methods подключим API и изменения в работе приложения.
Чтобы передавать приложению погодные характеристики из API, нужно открыть доступ к изменению данных. Для этого используем интерполяцию. В div указываем названия необходимых значений, а в script добавляем функцию, которая возвращает их в объект данных, data().
<div class="weather-info__text">
<p class="card">{{ location }}</p>
<p class="card">{{ temperature }}°C</p>
<p class="card">{{ description }}</p>
</div>
data() {
return {
location: '',
temperature: 0,
description: '',
};
}
Указываем в элементах интерфейса input и button связь функций. В директиву v-model добавляем переменную searchQuery.
<input type="text" class="weather-form__input" v-model="searchQuery" @keyup.enter="weatherSearch" placeholder="Enter city">
<button class="weather-form__btn" @click="weatherSearch">Search</button>
data() {
return {
location: '',
temperature: 0,
description: '',
searchQuery: '',
};
}
Подключаем API для сбора данных о погоде и добавляем два условия. Сначала необходимо обработать поисковый запрос, затем — сбросить введенную фразу из .
methods: {
weatherSearch() {
this.loading = true;
this.error = false;
fetch(`https://api.weatherapi.com/v1/current.json?key=69ed7b91ab4b4d4f9f282327242604&q=${this.searchQuery}`)
.then(response => response.json())
.then(data => {
this.loading = false;
this.location = data.location.name;
this.temperature = data.current.temp_c;
this.description = data.current.condition.text;
this.resetSearchQuery();
})
.catch(error => {
this.loading = false;
this.error = true;
console.error(error);
});
},
resetSearchQuery() {
this.searchQuery = '';
}
}
Поскольку в catch указаны значения loading и error, их также необходимо добавить в ветку data(). Это позволит отображать строку загрузки и планировать вывод ошибок на случай, если API перестанет обрабатывать данные через поиск.
data() {
return {
location: '',
temperature: 0,
description: '',
searchQuery: '',
loading: false,
error: false,
};
}
<div class="card weather-load" v-if="loading">...</div>
<div class="weather-info" v-show="!error && location && temperature !== 0 && description">
<div class="card" v-if="error">...</div>
<div class="weather-info__text">
...
</div>
</div>
Для изменения фона добавим :class в div, куда установлен класс weather.
<div class="weather" :class="weatherClass">
Далее пишем функцию, после которой появятся дополнительные классы напротив div. И это только в том случае, если в {{ description }} отображается соответствующее описание.
Напомню, что мы описывали только три состояния погоды, но таких значений может быть больше. Не забудьте подготовить фоны для каждого нового состояния.
computed: {
weatherClass() {
if (this.description.includes('Sunny')) {
return 'sunny';
} else if (this.description.includes('Overcast')) {
return 'overcast';
} else if (this.description.includes('Partly cloudy')) {
return 'partly-cloudy';
} else {
return '';
}
}
},
Финальный шаг — создаем приложение:
npm run build
Готово, мы сделали простое приложение по прогнозу погоды с нуля!
Деплой проекта
Сейчас готовый результат не увидит никто, кроме нас. Чтобы это исправить, перенесем его на облачный сервер Selectel.
В файле vite.config.js добавляем строку base: './' внутри export default defineConfig({ … }). Это позволит в build проекта отразить верные пути файлов.
Вводим в терминале команду npm run build, после которой появится папка dist. В ней будут формироваться итоговые данные проекта. Файлы оттуда выгрузим на облачный сервер.
В панели управления нужно создать аккаунт или авторизоваться, если у вас уже есть учетная запись. Внутри выбираем Облачная платформа и нажимаем Создать сервер.
В настройках указываем имя проекта и устанавливаем минимальную конфигурацию. Для работы нашего приложения много мощностей не нужно.
После установки конфигурации спускаемся вниз и нажимаем Создать.
Возвращаемся в панель управления и переходим в консоль созданного сервера. Для входа нужен логин (root) и пароль. Нажимаем ЛКМ по иконке Горячие клавиши и добавляем сгенерированный пароль.
В терминале вводим набор команд, чтобы загружать файлы на облачный сервер по FTP-протоколу.
1. Устанавливаем FTP-сервер (vsftpd):
sudo apt update
sudo apt install vsftpd
2. Настраиваем доступ. Для этого редактируем файл конфигурации FTP-сервера:
sudo nano /etc/vsftpd.conf
3. Проверяем параметры конфигурации. Они должны быть установлены или раскомментированы и иметь указанные значения:
anonymous_enable=NO
local_enable=YES
write_enable=YES
chroot_local_user=YES
4. Перезапускаем сервис:
sudo systemctl restart vsftpd
5. Открываем порты в файрволе, если необходимо:
sudo ufw allow ftp
6. Проверяем статус сервиса:
sudo systemctl status vsftpd
С помощью IP-адреса, имени пользователя и пароля вы можете войти в FTP-клиент, чтобы подключить приложение к FTP-серверу. Остается только перенести туда готовые файлы из папки dist.
Готово! Сайт доступен по адресу 46.148.229.29:
Заключение
В инструкции наглядно показали, как создать простое приложение для мониторинга погоды. Не бойтесь с ним экспериментировать — добавлять новые функции и улучшать дизайн. Это поможет прокачать навыки и пополнить портфолио новыми проектами.
А чтобы снизить затраты на обслуживание приложения, советуем присмотреться к виртуальной машине из линейки Shared Line. Оплата производится только за потребленные мощности по модели pay-as-you-go.
Автор: Анна Блок, автор YouTube-канала.
majorius
Ума не приложу откуда 12 плюсиков у статьи про корявое hello world приложение на vue2 (или на vue3 с options api), которое еще и деплоится по фтп.
gmtd
Это корпоративный блог
Корпорации все любят и плюсуют
Astroscope
Hidden text
majorius
возможно это реклама для джунов - "приходите к нам, пока уровень нашей технической экспертизы соответствует вашим знаниям" :)
egribanov
Не возможно, а точно это реклама серверов селектел
Doctor_IT
Добрый день.
Около 20% нашей аудитории — Junior специалисты.
В своих медиа мы выпускаем разный контент, ориентированный на разработчиков и сисадминов разного уровня. В нашем блоге на Хабре вы также можете найти как средний по сложности, так и хардовый контент.
Так что welcome, если есть пожелания по темам, на которые вы хотели бы прочитать статьи, напишите нам.)
venanen
Первое, о чем я подумал, увидев на главной эту статью - будет какой-то подвох, шутка или интересные моменты. А тут буквально туториал, который даже джуну уже полезен не будет. Нет комментариев вдобавок.
@moderator - может немножко бы уже надо бы как накруточку то проверять, а то 21 лайк, 21 сохранение (каково совпадение то, а?), 1 корневой коммент, эта статья уровня яслей детского сада 3 (третья!!!) на главной. У вас же есть вся бигдата, посчитайте распределение по просмотрам, лайкам и комментариям, уже давно же все придумано
( @Boomburum - тегаю чтобы кто-то из администрации увидел, т.к. вдруг у вас опять вызывалки отвалились)
UPD:
Заодно неплохо бы разобраться, кто эта половинка (полуторка?) человека?
Boomburum
Посмотрим, но, наверное, только второго числа вдумчиво получится. Но сходу вижу, что плюсики есть и от старожилов с весом голоса +3 )
Насчёт дробного голоса — это баг, надеюсь, скоро поправим.