В статье рассмотрим, как сделать простое приложение погоды без дизайн-макета‎. Поработаем с 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-канала.

Комментарии (8)


  1. majorius
    30.04.2024 10:50
    +9

    Ума не приложу откуда 12 плюсиков у статьи про корявое hello world приложение на vue2 (или на vue3 с options api), которое еще и деплоится по фтп.


    1. gmtd
      30.04.2024 10:50
      +1

      Это корпоративный блог

      Корпорации все любят и плюсуют


      1. Astroscope
        30.04.2024 10:50

        Корпорации все любят и плюсуют

        Hidden text


      1. majorius
        30.04.2024 10:50
        +1

        возможно это реклама для джунов - "приходите к нам, пока уровень нашей технической экспертизы соответствует вашим знаниям" :)


        1. egribanov
          30.04.2024 10:50

          Не возможно, а точно это реклама серверов селектел


    1. Doctor_IT
      30.04.2024 10:50
      +1

      Добрый день.

      Около 20% нашей аудитории — Junior специалисты.

      В своих медиа мы выпускаем разный контент, ориентированный на разработчиков и сисадминов разного уровня. В нашем блоге на Хабре вы также можете найти как средний по сложности, так и хардовый контент.

      Так что welcome, если есть пожелания по темам, на которые вы хотели бы прочитать статьи, напишите нам.)


    1. venanen
      30.04.2024 10:50

      Первое, о чем я подумал, увидев на главной эту статью - будет какой-то подвох, шутка или интересные моменты. А тут буквально туториал, который даже джуну уже полезен не будет. Нет комментариев вдобавок.

      @moderator - может немножко бы уже надо бы как накруточку то проверять, а то 21 лайк, 21 сохранение (каково совпадение то, а?), 1 корневой коммент, эта статья уровня яслей детского сада 3 (третья!!!) на главной. У вас же есть вся бигдата, посчитайте распределение по просмотрам, лайкам и комментариям, уже давно же все придумано
      ( @Boomburum - тегаю чтобы кто-то из администрации увидел, т.к. вдруг у вас опять вызывалки отвалились)

      UPD:

      Заодно неплохо бы разобраться, кто эта половинка (полуторка?) человека?


      1. Boomburum
        30.04.2024 10:50

        Посмотрим, но, наверное, только второго числа вдумчиво получится. Но сходу вижу, что плюсики есть и от старожилов с весом голоса +3 )
        Насчёт дробного голоса — это баг, надеюсь, скоро поправим.