Использование Vue.js для создания интерактивной панели мониторинга погоды с помощью API
Использование Vue.js для создания интерактивной панели мониторинга погоды с помощью API
Опубликовано: 2022-03-10
Краткий обзор ↬ Создание панели мониторинга с данными API часто представляет собой сложную задачу. Выбор технологического стека, интеграция API, выбор правильных диаграмм и украшение с помощью стилей CSS может стать сложной задачей. Это руководство представляет собой пошаговое руководство о том, как помочь вам создать панель мониторинга погоды в Vue.js с использованием данных API.
(Это спонсируемая статья.) В этом руководстве вы создадите простую панель мониторинга погоды с нуля. Это будет клиентское приложение, которое не является примером «Hello World» и не слишком устрашающе по размеру и сложности.
Весь проект будет разрабатываться с использованием инструментов экосистемы Node.js + npm. В частности, мы будем в значительной степени полагаться на Dark Sky API для данных, Vue.js для всей тяжелой работы и FusionCharts для визуализации данных.
Предпосылки
Мы ожидаем, что вы знакомы со следующим:
HTML5 и CSS3 (мы также будем использовать основные функции, предоставляемые Bootstrap;
JavaScript (особенно способ использования языка ES6);
Node.js и npm (основы окружения и управления пакетами вполне подойдут).
Помимо упомянутых выше, было бы здорово, если бы вы были знакомы с Vue.js или любым другим подобным фреймворком JavaScript. Мы не ожидаем, что вы будете знать о FusionCharts — им так легко пользоваться, что вы будете изучать его на лету!
Ожидаемые уроки
Ваши основные выводы из этого проекта:
Как спланировать внедрение хорошей информационной панели
Как разрабатывать приложения с помощью Vue.js
Как создавать приложения, управляемые данными
Как визуализировать данные с помощью FusionCharts
В частности, каждый из разделов приближает вас на шаг к целям обучения:
Введение в панель управления погодой В этой главе дается обзор различных аспектов предприятия.
Создать проект В этом разделе вы узнаете о создании проекта с нуля с помощью инструмента командной строки Vue.
Настройте структуру проекта по умолчанию Скаффолда проекта по умолчанию, который вы получили в предыдущем разделе, недостаточно; здесь вы узнаете дополнительные материалы, необходимые для проекта со структурной точки зрения.
Сбор и обработка данных Этот раздел является основной частью проекта; весь критический код для получения и обработки данных из API демонстрируется здесь. Рассчитывайте потратить максимум времени на этот раздел.
Визуализация данных с помощью FusionCharts Как только мы стабилизируем все данные и другие движущиеся части проекта, этот раздел посвящен визуализации данных с использованием FusionCharts и небольшого количества CSS.
1. Рабочий процесс панели инструментов
Прежде чем мы углубимся в реализацию, важно прояснить наш план. Мы разбиваем наш план на четыре отдельных аспекта:
Требования
Каковы наши требования к этому проекту? Другими словами, что мы хотим показать на нашей панели прогноза погоды? Имея в виду, что наша целевая аудитория, вероятно, простые смертные с простыми вкусами, мы хотели бы показать им следующее:
Подробная информация о месте, для которого они хотят видеть погоду, а также некоторые основные сведения о погоде. Поскольку жестких требований нет, со скучными подробностями разберемся позже. Однако на этом этапе важно отметить, что нам нужно будет предоставить аудитории окно поиска, чтобы они могли указать интересующее их местоположение.
Графическая информация о погоде в интересующем их месте, например:
Изменение температуры в день запроса
Отличительные черты сегодняшней погоды:
Скорость и направление ветра
Видимость
УФ-индекс
Примечание. Данные, полученные от API, предоставляют информацию о многих других аспектах погоды.Мы решили не использовать их все, чтобы свести код к минимуму.
Структура
Основываясь на требованиях, мы можем структурировать нашу панель инструментов, как показано ниже:
(Большой превью)
Данные
Наша информационная панель так же хороша, как и данные, которые мы получаем, потому что без надлежащих данных не будет красивых визуализаций. Существует множество общедоступных API, которые предоставляют данные о погоде — некоторые из них бесплатны, а некоторые — нет. Для нашего проекта мы будем собирать данные из Dark Sky API. Однако мы не сможем напрямую опросить конечную точку API со стороны клиента. Не волнуйтесь, у нас есть обходной путь, который будет раскрыт в нужное время! Как только мы получим данные для искомого местоположения, мы выполним некоторую обработку и форматирование данных — вы знаете, тип технических деталей, которые помогают нам оплачивать счета.
Визуализация
Как только мы получим чистые и отформатированные данные, мы вставим их в FusionCharts. В мире очень мало библиотек JavaScript с такими возможностями, как FusionCharts. Из огромного количества предложений от FusionCharts мы будем использовать только несколько — все они написаны на JavaScript, но без проблем работают при интеграции с оболочкой Vue для FusionCharts.
Вооружившись более широкой картиной, давайте запачкаем руки — пришло время сделать вещи конкретными! В следующем разделе вы создадите базовый проект Vue, поверх которого мы будем строить дальше.
2. Создание проекта
Для создания проекта выполните следующие шаги:
Установите Node.js + npm ( Если на вашем компьютере установлен Node.js, пропустите этот шаг. ) Node.js поставляется вместе с npm, поэтому вам не нужно устанавливать npm отдельно. В зависимости от операционной системы загрузите и установите Node.js в соответствии с приведенными здесь инструкциями.
После установки, вероятно, неплохо проверить, правильно ли работает программное обеспечение и каковы его версии. Чтобы проверить это, откройте командную строку/терминал и выполните следующие команды:
node --version npm --version
Установить пакеты с помощью npm После того, как вы запустите npm, выполните следующую команду, чтобы установить основные пакеты, необходимые для нашего проекта.
npm install -g vue@2 vue-cli@2
Инициализировать строительные леса проекта с помощью vue-cli Предполагая, что предыдущий шаг прошел успешно, следующим шагом будет использование vue-cli — инструмента командной строки из Vue.js для инициализации проекта. Для этого выполните следующее:
Инициализируйте скаффолдинг с помощью шаблона webpack-simple.
vue init webpack-simple vue_weather_dashboard
Вам будет задано множество вопросов — принять значения по умолчанию для всех вопросов, кроме последнего, будет достаточно для этого проекта; ответ N для последнего. (Большой превью) Имейте в виду, что, хотя webpack-simple отлично подходит для быстрого прототипирования и легких приложений, таких как наше, он не особенно подходит для серьезных приложений или производственного развертывания. Если вы хотите использовать любой другой шаблон (хотя мы бы не советовали его использовать, если вы новичок) или хотели бы назвать свой проект как-то иначе, синтаксис такой:
vue init [template-name] [project-name]
Перейдите в каталог, созданный vue-cli для проекта.
cd vue_weather_dashboard
Установите все пакеты, упомянутые в package.json , который был создан инструментом vue-cli cli для шаблона webpack-simple .
npm install
Запустите сервер разработки и посмотрите, как ваш проект Vue по умолчанию работает в браузере!
npm run dev
Если вы новичок в Vue.js, найдите минутку, чтобы насладиться своим последним достижением — вы создали небольшое приложение Vue и запустили его на локальном хосте: 8080!
(Большой превью)
Краткое объяснение структуры проекта по умолчанию
Пришло время взглянуть на структуру внутри каталога vue_weather_dashboard , чтобы у вас было понимание основ, прежде чем мы начнем его модифицировать.
Хотя может возникнуть соблазн пропустить знакомство с файлами и каталогами по умолчанию, если вы новичок в Vue, мы настоятельно рекомендуем хотя бы взглянуть на содержимое файлов. Это может быть хорошим учебным занятием и вызвать вопросы, которые вы должны задать самостоятельно, особенно следующие файлы:
package.json и просто взгляд на его двоюродного брата package-lock.json
webpack.config.js
index.html
src/main.js
src/App.vue
Краткое объяснение каждого из файлов и каталогов, показанных на древовидной диаграмме, приведено ниже:
README.md Никаких наград за догадки — в первую очередь люди должны прочитать и понять шаги, необходимые для создания строительных лесов проекта.
node_modules/ Это каталог, в который npm загружает пакеты, необходимые для запуска проекта. Информация о необходимых пакетах доступна в файле package.json .
пакет.json Этот файл создается инструментом vue-cli на основе требований шаблона webpack-simple и содержит информацию о пакетах npm (включая их версии и другие сведения), которые необходимо установить. Внимательно посмотрите на содержимое этого файла — это то место, где вы должны посетить и, возможно, отредактировать, чтобы добавить/удалить пакеты, необходимые для проекта, а затем запустить npm install. Подробнее о package.json читайте здесь.
пакет-lock.json Этот файл создается самим npm и в первую очередь предназначен для ведения журнала вещей, загруженных и установленных npm.
webpack.config.js Это файл JavaScript, который содержит конфигурацию веб-пакета — инструмента, который объединяет различные аспекты нашего проекта (код, статические ресурсы, конфигурация, среды, режим использования и т. д.) и минимизирует его перед предоставлением пользователю. Преимущество заключается в том, что все вещи автоматически связаны друг с другом, а пользовательский опыт значительно улучшается из-за повышения производительности приложения (страницы обслуживаются быстрее и быстрее загружаются в браузере). Как вы можете столкнуться позже, это файл, который необходимо проверять, когда что-то в системе сборки работает не так, как задумано. Кроме того, когда вы хотите развернуть приложение, это один из ключевых файлов, который необходимо отредактировать (подробнее здесь).
index.html Этот HTML-файл служит матрицей (или, можно сказать, шаблоном), в которую данные и код должны быть встроены динамически (это то, что в основном делает Vue), а затем предоставлены пользователю.
источник/main.js Этот файл JavaScript содержит код, который в основном управляет зависимостями верхнего уровня/уровня проекта и определяет компонент Vue самого верхнего уровня. Короче говоря, он управляет JavaScript для всего проекта и служит точкой входа приложения. Отредактируйте этот файл, если вам нужно объявить зависимости для всего проекта от определенных модулей узла или вы хотите что-то изменить в самом верхнем компоненте Vue в проекте.
источник/App.vue В предыдущем пункте, когда мы говорили о «самом верхнем компоненте Vue», мы, по сути, говорили об этом файле. Каждый файл .vue в проекте является компонентом, а компоненты связаны иерархически. В начале у нас есть только один файл .vue , то есть App.vue , как наш единственный компонент. Но вскоре мы добавим в наш проект дополнительные компоненты (в первую очередь в соответствии со структурой панели инструментов) и свяжем их в соответствии с желаемой иерархией, причем App.vue является прародителем всех компонентов. Эти файлы .vue будут содержать код в формате, который Vue хочет, чтобы мы написали. Не волнуйтесь, это код JavaScript, написанный с сохранением структуры, которая помогает нам оставаться в здравом уме и организованности. Вы были предупреждены — к концу этого проекта, если вы новичок в Vue, вы можете пристраститься к template — script — styletemplate — script — styletemplate — script — style способ организации кода!
Теперь, когда мы создали основу, пришло время:
Измените шаблоны и немного подправьте файлы конфигурации, чтобы проект вел себя именно так, как мы хотим.
Создайте новые файлы .vue и реализуйте структуру панели мониторинга с помощью кода Vue.
Мы изучим их в следующем разделе, который будет немного длинным и потребует некоторого внимания. Если вам нужен кофеин или вода, или вы хотите разрядиться — сейчас самое время!
3. Настройка структуры проекта по умолчанию
Пришло время повозиться с фундаментом, который дал нам проект строительных лесов. Прежде чем начать, убедитесь, что сервер разработки, предоставляемый webpack , работает. Преимущество непрерывной работы этого сервера заключается в том, что любые изменения, которые вы вносите в исходный код — если вы сохраняете его и обновляете веб-страницу — немедленно отражаются в браузере.
Если вы хотите запустить сервер разработки, просто выполните следующую команду из терминала (при условии, что ваш текущий каталог является каталогом проекта):
npm run dev
В следующих разделах мы изменим некоторые из существующих файлов и добавим несколько новых файлов. За ним последуют краткие объяснения содержимого этих файлов, чтобы вы имели представление о том, для чего предназначены эти изменения.
Изменить существующие файлы
index.html
Наше приложение — это буквально одностраничное приложение, потому что в браузере отображается только одна веб-страница. Мы поговорим об этом позже, но сначала давайте просто внесем наше первое изменение — изменим текст внутри <title> .
С этой небольшой доработкой файл HTML выглядит следующим образом:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <!-- Modify the text of the title tag below --> <title>Vue Weather Dashboard</title> </head> <body> <div></div> <script src="/dist/build.js"></script> </body> </html>
Найдите минутку, чтобы обновить веб-страницу по адресу localhost:8080 и увидеть изменения, отраженные в строке заголовка вкладки в браузере — там должно быть написано «Vue Weather Dashboard». Однако это было сделано только для того, чтобы продемонстрировать вам процесс внесения изменений и проверки его работоспособности. У нас есть еще дела!
На этой простой HTML-странице не хватает многих вещей, которые нам нужны в нашем проекте, особенно следующего:
Некоторая метаинформация
Ссылки CDN на Bootstrap (CSS-фреймворк)
ссылка на пользовательскую таблицу стилей (еще не добавленную в проект)
Указатели на Google Maps Geolocation API из <script>
После добавления этих вещей окончательный index.html будет иметь следующее содержимое:
Сохраните файл и обновите веб-страницу. Возможно, вы заметили небольшой толчок во время загрузки страницы — в первую очередь это связано с тем, что стиль страницы теперь контролируется Bootstrap, а элементы стиля, такие как шрифты, интервалы и т. д., отличаются от того, что было по умолчанию. раньше (если не уверены, откатитесь на дефолт и посмотрите разницу).
(Большой превью)
Примечание . Перед тем, как мы двинемся дальше, необходимо сделать одну важную вещь: URL-адрес Google Maps API содержит ключ, являющийся собственностью FusionCharts.На данный момент вы можете использовать этот ключ для создания проекта, так как мы не хотим, чтобы вы увязли в этих мелких деталях (которые могут отвлекать, пока вы новичок).Тем не менее, мы настоятельно рекомендуем вам сгенерировать и использовать свой собственный ключ API Карт Google, как только вы добьетесь определенного прогресса и почувствуете себя комфортно, обращая внимание на эти мелкие детали.
пакет.json
На момент написания этого мы использовали определенные версии пакетов npm для нашего проекта, и мы точно знаем, что эти вещи работают вместе. Однако к тому времени, когда вы выполняете проект, весьма вероятно, что последние стабильные версии пакетов, которые npm загружает для вас, не будут такими же, как мы использовали, и это может привести к поломке кода (или сделать что-то, что выходит за рамки наш контроль). Таким образом, очень важно иметь тот же самый файл package.json , который использовался для сборки этого проекта, чтобы наш код/объяснения и полученные вами результаты были согласованы.
Мы рекомендуем вам просмотреть новый package.json и выяснить, каковы функции различных объектов в json. Вы можете предпочесть изменить значение ключа « author » на свое имя. Кроме того, пакеты, упомянутые в зависимостях, в нужный момент проявятся в коде. Пока достаточно знать, что:
Пакеты, связанные с babel , предназначены для правильной обработки браузером кода стиля ES6;
axios работает с HTTP-запросами на основе Promise;
moment и момент-часовой пояс предназначены для манипулирования датой/временем;
fusioncharts и vue-fusioncharts отвечают за отрисовку диаграмм:
vue по понятным причинам.
webpack.config.js
Как и в случае с package.json , мы рекомендуем вам поддерживать файл webpack.config.js , соответствующий тому, который мы использовали для сборки проекта. Однако, прежде чем вносить какие-либо изменения, мы рекомендуем вам тщательно сравнить код по умолчанию в webpack.config.js и код, который мы предоставили ниже. Вы заметите довольно много отличий — погуглите их и получите общее представление о том, что они означают. Поскольку подробное объяснение конфигураций веб-пакетов выходит за рамки этой статьи, в этом отношении вы предоставлены сами себе.
Настроенный файл webpack.config.js выглядит следующим образом:
После внесения изменений в файл проекта webpack.config.js необходимо остановить работающий сервер разработки ( Ctrl + C ) и перезапустить его с помощью следующей команды, выполняемой из каталога проекта после установки всех пакетов, упомянутых в package.json . package.json файл:
npm install npm run dev
На этом испытания по настройке конфигураций и обеспечению наличия нужных пакетов заканчиваются. Тем не менее, это также знаменует собой путешествие по модификации и написанию кода, которое немного долго, но также очень полезно!
источник/main.js
Этот файл является ключом к оркестровке проекта на высшем уровне — именно здесь мы определяем:
Каковы зависимости верхнего уровня (где взять наиболее важные необходимые пакеты npm);
Как разрешить зависимости, а также инструкции для Vue по использованию плагинов/оболочек, если таковые имеются;
Экземпляр Vue, который управляет самым верхним компонентом в проекте: src/App.vue (узловой файл .vue ).
В соответствии с нашими целями для файла src/main.js код должен быть:
// Import the dependencies and necessary modules import Vue from 'vue'; import App from './App.vue'; import FusionCharts from 'fusioncharts'; import Charts from 'fusioncharts/fusioncharts.charts'; import Widgets from 'fusioncharts/fusioncharts.widgets'; import PowerCharts from 'fusioncharts/fusioncharts.powercharts'; import FusionTheme from 'fusioncharts/themes/fusioncharts.theme.fusion'; import VueFusionCharts from 'vue-fusioncharts'; // Resolve the dependencies Charts(FusionCharts); PowerCharts(FusionCharts); Widgets(FusionCharts); FusionTheme(FusionCharts); // Globally register the components for project-wide use Vue.use(VueFusionCharts, FusionCharts); // Instantiate the Vue instance that controls the application new Vue({ el: '#app', render: h => h(App) })
источник/App.vue
Это один из самых важных файлов во всем проекте, представляющий самый верхний компонент в иерархии — само приложение в целом. Для нашего проекта этот компонент выполнит всю тяжелую работу, которую мы рассмотрим позже. А пока мы хотим избавиться от стандартного шаблона и поставить что-то свое.
Если вы новичок в способе организации кода Vue, было бы лучше получить представление об общей структуре файлов .vue . Файлы .vue состоят из трех разделов:
Шаблон Здесь определяется шаблон HTML для страницы. Помимо статического HTML, этот раздел также содержит способ Vue для встраивания динамического контента с использованием двойных фигурных скобок {{ }} .
Скрипт JavaScript управляет этим разделом и отвечает за создание динамического контента, который размещается внутри шаблона HTML в соответствующих местах. Этот раздел в первую очередь представляет собой объект, который экспортируется, и состоит из:
Данные Это сама функция, и обычно она возвращает желаемые данные, инкапсулированные в красивую структуру данных.
Методы Объект, состоящий из одной или нескольких функций/методов, каждый из которых обычно так или иначе манипулирует данными, а также управляет динамическим содержимым шаблона HTML.
Вычислено Подобно объекту-методу, рассмотренному выше, с одним важным отличием: хотя все функции в объекте-методе выполняются всякий раз, когда вызывается любая из них, функции в вычисляемом объекте ведут себя гораздо более разумно и выполняются тогда и только тогда, когда они были вызваны. называется.
Стиль Этот раздел предназначен для стилей CSS, которые применяются к HTML-коду страницы (написанному в шаблоне) — поместите сюда старый добрый CSS, чтобы сделать ваши страницы красивыми!
Помня об изложенной выше парадигме, давайте минимально настроим код в App.vue :
Помните, что приведенный выше фрагмент кода предназначен просто для проверки того, что App.vue работает с нашим собственным кодом. Позже он претерпит множество изменений, но сначала сохраните файл и обновите страницу в браузере.
(Большой превью)
На этом этапе, вероятно, будет хорошей идеей получить некоторую помощь в инструментах. Ознакомьтесь с инструментами разработки Vue для Chrome, и если у вас нет особых проблем с использованием Google Chrome в качестве браузера по умолчанию для разработки, установите этот инструмент и немного поэкспериментируйте с ним. Это будет очень удобно для дальнейшей разработки и отладки, когда все станет сложнее.
Дополнительные каталоги и файлы
Следующим шагом будет добавление дополнительных файлов, чтобы структура нашего проекта стала полной. Мы бы добавили следующие каталоги и файлы:
Примечание . Сохраните файлы .svg с гиперссылками в своем проекте.
Создайте каталоги и файлы, упомянутые выше. Окончательная структура проекта должна выглядеть примерно так (не забудьте удалить папки и файлы из структуры по умолчанию, которые теперь не нужны):
В корневой папке проектамогут быть и другие файлы, такие как.babelrc , .gitignore , .editorconfig и т. д. Вы можете безопасно игнорировать их на данный момент.
В следующем разделе мы добавим минимальное содержимое во вновь добавленные файлы и проверим, правильно ли они работают.
источник/css/style.css
Хотя сразу это не очень поможет, скопируйте в файл следующий код:
В этом каталоге загрузите и сохраните файлы .svg , указанные ниже:
calendar.svg
location.svg
search.svg
winddirection.svg
windspeed.svg
источник/компоненты/Content.vue
Это то, что мы называем «тупым компонентом» (то есть заполнителем), который существует только для поддержания иерархии и, по сути, передает данные своим дочерним компонентам.
Помните, что нет технических ограничений для написания всего нашего кода в файле App.vue , но мы используем подход разделения кода путем вложения компонентов по двум причинам:
Писать чистый код, который способствует удобочитаемости и ремонтопригодности;
Воспроизвести ту же структуру, которую мы увидим на экране, т. е. иерархию.
Прежде чем мы вложим компонент, определенный в Content.vue , в корневой компонент App.vue , давайте напишем некоторый игрушечный (но обучающий) код для Content.vue :
В коде внимательно наблюдайте и понимайте следующее:
Внутри <script> (где мы, очевидно, пишем код JavaScript) мы определяем объект, который экспортируется (делается доступным для других файлов) по умолчанию. Этот объект содержит функцию data() , которая возвращает объект массива с именем childComponents , элементы которого являются именами файлов компонентов, которые должны быть вложены дальше.
Внутри <template> (где мы пишем HTML-шаблон) интерес представляет <ul> .
В неупорядоченном списке каждый элемент списка должен быть именами предполагаемых дочерних компонентов, как определено в объекте массива childComponents . Более того, список должен автоматически расширяться до последнего элемента массива. Похоже, мы должны написать цикл for , не так ли? Мы делаем это с помощью директивы v-for , предоставляемой Vue.js. Директива v-for :
Действует как атрибут <li> , перебирает массив, отображает имена дочерних компонентов, где итератор упоминается в скобках {{ }} (где мы пишем текст для элементов списка).
Код и приведенное выше объяснение составляют основу вашего последующего понимания того, как взаимосвязаны сценарий и шаблон и как мы можем использовать директивы, предоставляемые Vue.js.
Мы узнали довольно много, но даже после всего этого нам осталось узнать кое-что о плавном соединении компонентов в иерархии — передаче данных от родительского компонента к его дочерним. На данный момент нам нужно научиться передавать некоторые данные из src/App.vue в src/components/Content.vue , чтобы мы могли использовать те же методы для вложения остальных компонентов в этом проекте.
Передача данных от родительских компонентов к дочерним может показаться простой задачей, но дьявол кроется в деталях! Как кратко объяснено ниже, есть несколько шагов, необходимых для того, чтобы заставить его работать:
Определение и данные На данный момент нам нужны некоторые статические данные для игры — объект, содержащий жестко запрограммированные значения о различных аспектах погоды, будет вполне уместным! Мы создаем объект с именем weather_data и возвращаем его из функции data() в App.vue . Объект weather_data представлен во фрагменте ниже:
Передача данных от родителя Чтобы передать данные, нам нужен пункт назначения, куда мы хотим отправить данные! В этом случае пунктом назначения является компонент Content.vue , и способ его реализации заключается в следующем:
Назначьте объект weather_dataпользовательскому атрибуту<Content>
Свяжите атрибут с данными с помощью директивы v-bind :, предоставляемой Vue.js, которая делает значение атрибута динамическим (реагирующим на изменения, внесенные в исходные данные).
Когда данные определены и переданы из источника (родительского компонента), теперь ответственность за получение данных и их надлежащее отображение лежит на дочернем компоненте, как объясняется в следующих двух шагах.
Получение данных ребенком Дочерний компонент, в данном случае Content.vue , должен получить объект weather_data , отправленный ему родительским компонентом App.vue . Vue.js предоставляет для этого механизм — все, что вам нужно, — это объект массива, называемый props , определенный в объекте по умолчанию, экспортируемом Content.vue . Каждый элемент props массива — это имя объектов данных, которые он хочет получить от своего родителя. На данный момент единственный объект данных, который он должен получать, — это weather_data из App.vue. Таким образом, массив props выглядит так:
<template> // HTML template code here </template> <script> export default { props: ["weather_data"], data () { return { // data here } }, } </script> <style> // component specific CSS here </style>
Отображение данных на странице Теперь, когда мы обеспечили получение данных, последняя задача, которую нам нужно выполнить, — отобразить данные. В этом примере мы выгрузим полученные данные непосредственно на веб-страницу, просто чтобы проиллюстрировать технику. Однако в реальных приложениях (таких как то, которое мы собираемся создать) данные обычно проходят многократную обработку, и только соответствующие их части отображаются так, как это соответствует цели. Например, в этом проекте мы в конечном итоге получим необработанные данные из API погоды, очистим и отформатируем их, загрузим данные в структуры данных, необходимые для графиков, а затем визуализируем их. В любом случае, чтобы отобразить дамп необработанных данных, мы просто будем использовать скобки {{ }} , которые понимает Vue, как показано во фрагменте ниже:
<template> <div> // other template code here {{ weather_data }} </div> </template>
Пришло время ассимилировать все кусочки. Код для Content.vue — в его текущем состоянии — приведен ниже:
После внесения описанных выше изменений обновите веб-страницу в браузере и посмотрите, как она выглядит. Потратьте минутку, чтобы оценить сложность, с которой справляется Vue — если вы измените объект weather_data в App.vue , он будет незаметно передан в Content.vue и, в конечном итоге, в браузер, отображающий веб-страницу! Попробуйте изменить значение местоположения ключа.
Хотя мы узнали о свойствах и привязке данных с помощью статических данных, мы будем использовать в приложении динамические данные, собранные с помощью веб-API, и соответствующим образом изменим код .
Резюме
Прежде чем мы перейдем к остальным файлам .vue , давайте обобщим то, что мы узнали, когда писали код для App.vue и components/Content.vue :
Файл App.vue — это то, что мы называем корневым компонентом — тот, который находится на вершине иерархии компонентов. Остальные файлы .vue представляют компоненты, которые являются его непосредственными дочерними, внучатыми и т. д.
Файл Content.vue является фиктивным компонентом — его обязанностью является передача данных на уровни ниже и поддержание структурной иерархии, чтобы наш код соответствовал философии «*что мы видим, то и реализуем*».
Родительско-дочерние отношения компонента не возникают из ничего — вы должны зарегистрировать компонент (глобально или локально, в зависимости от предполагаемого использования компонента), а затем вложить его с помощью пользовательских тегов HTML (чье написание является точным совпадает с именами, под которыми зарегистрированы компоненты).
После регистрации и вложения данные передаются от родительских компонентов к дочерним, и поток никогда не бывает обратным (плохие вещи могут произойти, если архитектура проекта допускает обратный поток). Родительский компонент является относительным источником данных и передает соответствующие данные своим дочерним элементам с помощью директивы v-bind для атрибутов пользовательских элементов HTML. Ребенок получает предназначенные ему данные с помощью реквизита, а затем самостоятельно решает, что с данными делать.
Для остальных компонентов мы не будем вдаваться в подробные объяснения — мы просто напишем код на основе выводов из приведенного выше резюме. Код будет очевиден, и если вы запутались в иерархии, обратитесь к схеме ниже:
(Большой превью)
На диаграмме показано, что TempVarChart.vue и Highlights.vue являются прямым потомком Content.vue . Таким образом, было бы неплохо подготовить Content.vue для отправки данных этим компонентам, что мы и делаем, используя приведенный ниже код:
Как только вы сохраните этот код, вы получите ошибки — не волнуйтесь, это ожидаемо. Это будет исправлено, как только у вас будут готовы остальные файлы компонентов. Если вас беспокоит отсутствие возможности увидеть вывод, закомментируйте строки, содержащие теги пользовательских элементов <temp-var-chart> и <today-highlights> .
Для этого раздела это окончательный код Content.vue . В оставшейся части этого раздела мы будем ссылаться на этот код , а не на предыдущие, которые мы написали для обучения.
источник/компоненты/TempVarChart.vue
Поскольку его родительский компонент Content.vue передает данные, TempVarChart.vue должен быть настроен для получения и отображения данных, как показано в приведенном ниже коде:
Этот компонент также будет получать данные от App.vue — своего родительского компонента. После этого его нужно слинковать со своими дочерними компонентами и передать им соответствующие данные.
Давайте сначала посмотрим код для получения данных от родителя:
На данный момент веб-страница выглядит так, как показано на рисунке ниже:
(Большой превью)
Теперь нам нужно изменить код Highlights.vue , чтобы зарегистрировать и вложить его дочерние компоненты, а затем передать данные дочерним элементам. Код для него следующий:
Как только вы сохраните код и увидите веб-страницу, вы должны увидеть ошибки в инструменте консоли разработчика, предоставляемом браузером; они появляются потому, что хотя Highlights.vue отправляет данные, никто их не получает. Нам еще предстоит написать код для потомковHighlights.vue .
Обратите внимание, что мы не выполнили большую часть обработки данных, т. е. мы не извлекли отдельные факторы данных о погоде, которые находятся в разделе «Основные моменты» панели инструментов. Мы могли бы сделать это в функции data() , но мы предпочли оставить Highlights.vue тупым компонентом, который просто передает весь дамп данных, который он получает, каждому из дочерних элементов, которые затем владеют своими собственными извлечениями того, что им необходимо. . Тем не менее, мы рекомендуем вам попробовать извлечь данные в Highlights.vue и отправить соответствующие данные каждому дочернему компоненту — тем не менее, это хорошая практика!
источник/компоненты/UVIndex.vue
Код для этого компонента получает дамп данных основных моментов из Highlights.vue , извлекает данные для UV Index и отображает их на странице.
Код для этого компонента получает дамп данных об основных моментах из Highlights.vue , извлекает данные для состояния ветра (скорость и направление) и отображает их на странице.
После добавления кода для всех компонентов взгляните на веб-страницу в браузере.
(Большой превью)
Чтобы не унывать, но все эти труды были только для того, чтобы связать компоненты в иерархию и проверить, происходит ли поток данных между ними или нет! В следующем разделе мы отбросим большую часть кода, который мы написали до сих пор , и добавим гораздо больше, относящееся к реальному проекту. Однако мы обязательно сохраним структуру и вложенность компонентов; выводы из этого раздела позволят нам создать достойную информационную панель с помощью Vue.js.
4. Сбор и обработка данных
Помните объект weather_data в App.vue ? В нем были некоторые жестко закодированные данные, которые мы использовали для проверки правильности работы всех компонентов, а также для того, чтобы помочь вам изучить некоторые основные аспекты приложения Vue, не увязая в деталях реальных данных. Однако пришло время сбросить нашу оболочку и выйти в реальный мир, где данные из API будут доминировать в большей части нашего кода.
Подготовка дочерних компонентов для получения и обработки реальных данных
В этом разделе вы получите дамп кода для всех компонентов, кроме App.vue . Код будет обрабатывать получение реальных данных из App.vue (в отличие от кода, который мы написали в предыдущем разделе для получения и рендеринга фиктивных данных).
Мы настоятельно рекомендуем внимательно прочитать код каждого компонента, чтобы у вас сложилось представление о том, какие данные каждый из этих компонентов ожидает и в конечном итоге будет использовать в визуализации.
Часть кода и общая структура будут похожи на те, что вы видели в предыдущей структуре, так что вы не столкнетесь с чем-то кардинально другим. Однако дьявол кроется в деталях! Поэтому внимательно изучите код и, когда вы достаточно хорошо его поймете, скопируйте код в соответствующие файлы компонентов в вашем проекте.
Примечание . Все компоненты в этом разделе находятся в каталоге src/components/ .Таким образом, каждый раз путь упоминаться не будет — для идентификации компонента будет указано только имя файла .vue .
По сравнению с предыдущим кодом были внесены следующие изменения:
В <template> текст и данные в пределах {{ }} были удалены, так как теперь мы просто получаем данные и передаем их дочерним элементам без рендеринга этого компонента.
В export default {} :
props были изменены, чтобы соответствовать объектам данных, которые будут отправлены родителем: App.vue . Причина изменения реквизита заключается в том, что App.vue сам будет отображать часть данных, которые он получает от API погоды и других онлайн-ресурсов, исходя из поискового запроса пользователя, и передавать остальные данные. В фиктивном коде, который мы написали ранее, App.vue передавал весь фиктивный дамп данных без какой-либо дискриминации, и реквизиты Content.vue были настроены соответствующим образом.
Функция data() теперь ничего не возвращает, так как мы не выполняем никаких манипуляций с данными в этом компоненте.
TempVarChart.vue
Этот компонент должен получать подробные прогнозы температуры на оставшуюся часть текущего дня и в конечном итоге отображать их с помощью FusionCharts. Но пока мы будем отображать их только в виде текста на веб-странице.
В <template> текст и данные внутри {{ }} были удалены, потому что это тупой компонент, как и Content.vue , единственной задачей которого является передача данных дочерним элементам при сохранении структурной иерархии. Помните, что простые компоненты, такие как Highlights.vue и Content.vue , существуют для поддержания паритета между визуальной структурой панели инструментов и кодом, который мы пишем.
UVIndex.vue
Изменения, внесенные в предыдущий код, заключаются в следующем:
В <template> и <style>div id был изменен на uvIndex , что более читабельно.
В export default {} функция data() теперь возвращает строковый объект uvIndex , значение которого извлекается из объекта хайлайтов, полученного компонентом с помощью props . Этот uvIndex теперь временно используется для отображения значения в виде текста внутри <template> . Позже мы вставим это значение в структуру данных, подходящую для отображения диаграммы.
Единственное изменение, сделанное в этом файле (по сравнению с его предыдущим кодом), заключается в том, что определение объекта visibility , возвращаемого функцией data() , теперь содержит toString() в конце, поскольку значение, полученное от родителя, будет плавающим. номер точки, который необходимо преобразовать в строку.
WindStatus.vue
<template> <div> <p>Wind Speed — {{ windSpeed }}</p> <p>Wind Direction — {{ derivedWindDirection }}, or {{ windDirection }} degree clockwise with respect to true N as 0 degree.</p> </div> </template> <script> export default { props: ["highlights"], data () { return { windSpeed: this.highlights.windStatus.windSpeed, derivedWindDirection: this.highlights.windStatus.derivedWindDirection, windDirection: this.highlights.windStatus.windDirection } }, methods: { }, computed: { }, } </script> <style> </style>
Изменения, внесенные в предыдущий код, заключаются в следующем:
Во всем файле windstatus был переименован в windStatus , чтобы повысить удобочитаемость, а также для синхронизации с объектом основных моментов, который App.vue предоставляет с фактическими данными.
Аналогичные изменения в именах были внесены для скорости и направления — новыми стали windSpeed и windDirection .
В игру вступил новый объект, derivedWindDirection (также предоставленный App.vue в пакете основных моментов).
На данный момент полученные данные отображаются в виде текста; позже он будет подключен к структуре данных, необходимой для визуализации.
Тестирование с фиктивными данными
Повторное обращение к фиктивным данным может вас немного разочаровать, но для этого есть несколько веских причин:
Мы внесли множество изменений в код каждого компонента, и было бы неплохо проверить, не нарушают ли эти изменения код. Другими словами, мы должны проверить, не поврежден ли поток данных, теперь, когда мы собираемся перейти к более сложным частям проекта.
Реальные данные из онлайн-API погоды потребуют большого количества обработки, и вам может быть сложно переключаться между кодом для сбора и обработки данных и кодом для плавного потока данных по компонентам. Идея состоит в том, чтобы держать квант сложности под контролем, чтобы лучше понимать ошибки, с которыми мы можем столкнуться.
В этом разделе мы, по сути, жестко закодируем некоторые данные json в App.vue , которые, очевидно, будут заменены живыми данными в ближайшем будущем. Есть много общего между фиктивной структурой json и структурой json, которую мы будем использовать для реальных данных. Таким образом, это также дает вам приблизительное представление о том, чего ожидать от реальных данных, как только мы с ними столкнемся.
Однако мы признаем, что это далеко не идеальный подход, который можно было бы использовать при создании такого проекта с нуля. В реальном мире вы часто начинаете с реального источника данных, немного играете с ним, чтобы понять, что можно и нужно сделать, чтобы его укротить, а затем думаете о подходящей структуре данных json для сбора соответствующей информации. Мы намеренно оградили вас от всей этой грязной работы, так как она уводит вас дальше от цели — изучения того, как использовать Vue.js и FusionCharts для построения дашборда.
Давайте теперь перейдем к новому коду для App.vue:
Изменения, внесенные в код по отношению к его предыдущей версии, заключаются в следующем:
Имя дочернего компонента было изменено на «dashboard-content», и, соответственно, изменен пользовательский элемент HTML в <template> . Обратите внимание, что теперь у нас есть два атрибута — highlights и tempVar — вместо одного атрибута, который мы использовали ранее с пользовательским элементом. Соответственно, данные, связанные с этими атрибутами, также изменились. Что здесь интересно, так это то, что мы можем использовать директиву v-bind: или ее сокращение : (как мы сделали здесь) с несколькими атрибутами пользовательского элемента HTML!
Функция data() теперь возвращает объект filename (существовавший ранее) вместе с двумя новыми объектами (вместо старого weather_data ): tempVar и highlights . Структура json соответствует коду, который мы написали в дочерних компонентах, чтобы они могли извлекать нужные им фрагменты данных из дампов. Структуры говорят сами за себя, и вы можете ожидать, что они будут очень похожи, когда мы имеем дело с оперативными данными. Однако существенным изменением, с которым вы столкнетесь, является отсутствие жесткого кодирования (очевидно, не так ли) — мы оставим значения пустыми в качестве состояния по умолчанию и напишем код для их динамического обновления на основе значений, которые мы получим от API погоды.
Вы написали много кода в этом разделе, не видя фактического результата. Прежде чем продолжить, загляните в браузер (при необходимости перезапустите сервер с помощью npm run dev ) и насладитесь славой своего достижения. Веб-страница, которую вы должны увидеть на этом этапе, выглядит так, как показано на изображении ниже:
(Большой превью)
Код для сбора и обработки данных
Этот раздел станет основной частью проекта, и весь код будет написан в App.vue для следующего:
Ввод местоположения от пользователя — достаточно поля ввода и кнопки призыва к действию;
Вспомогательные функции для различных задач; эти функции будут вызываться позже в различных частях кода компонента;
Получение подробных геолокационных данных из Google Maps API для JavaScript;
Получение подробных данных о погоде из Dark Sky API;
Форматирование и обработка данных о геолокации и погоде, которые будут передаваться дочерним компонентам.
Следующие подразделы иллюстрируют, как мы можем реализовать задачи, поставленные перед нами в предыдущих пунктах. За некоторыми исключениями, большинство из них будут следовать последовательности.
Вход от пользователя
Совершенно очевидно, что действие начинается, когда пользователь указывает название места, для которого необходимо отобразить данные о погоде. Для этого нам необходимо реализовать следующее:
Поле ввода для ввода локации;
Кнопка отправки, которая сообщает нашему приложению, что пользователь ввел местоположение и пришло время сделать все остальное. Мы также реализуем поведение, когда обработка начинается после нажатия Enter .
Код, который мы показываем ниже, будет ограничен частью HTML-шаблона App.vue . Мы просто упомянем имя метода, связанного с событиями щелчка, и определим их позже в объекте методов <script> в App.vue.
Разместить приведенный выше фрагмент в нужном месте тривиально — мы оставляем это на ваше усмотрение. Тем не менее, интересные части фрагмента:
@keyup.enter="organizeAllDetails"
@click="organizeAllDetails"
Как вы знаете из предыдущих разделов, @ — это сокращение Vue для директивы v-on :, которая связана с некоторым событием. Новой вещью является « organizeAllDetails » — это не что иное, как метод, который сработает, как только произойдут события (нажатие Enter или нажатие кнопки). Нам еще предстоит определить метод, и головоломка будет завершена к концу этого раздела.
Отображение текстовой информации, управляемое App.vue
Как только пользовательский ввод инициирует действие и большое количество данных получено из API, мы сталкиваемся с неизбежным вопросом: «Что делать со всеми этими данными?». Очевидно, что требуется некоторая обработка данных, но это не дает полного ответа на наш вопрос! Нам нужно решить, каково конечное использование данных, или, точнее, какие объекты получают разные фрагменты полученных и обработанных данных?
Дочерние компоненты App.vue , в зависимости от их иерархии и назначения, являются главными претендентами на большую часть данных. Однако у нас также будут некоторые данные, которые не принадлежат ни к одному из этих дочерних компонентов, но являются достаточно информативными и делают панель инструментов полной. Мы можем с пользой использовать их, если будем отображать их в виде текстовой информации, непосредственно контролируемой App.vue , в то время как остальные данные передаются дочернему элементу для отображения в конечном итоге в виде красивых диаграмм.
Имея в виду этот контекст, давайте сосредоточимся на коде для установки этапа использования текстовых данных. На данный момент это простой HTML-шаблон, на котором в конечном итоге будут храниться данные.
В приведенном выше фрагменте вы должны понимать следующее:
Вещи внутри {{ }} — это способ Vue вставки динамических данных в HTML-шаблон до того, как он отобразится в браузере. Вы сталкивались с ними раньше, и в этом нет ничего нового или удивительного. Просто имейте в виду, что эти объекты данных происходят из метода data() в объекте export default()App.vue . У них есть значения по умолчанию, которые мы установим в соответствии с нашими требованиями, а затем напишем определенные методы для заполнения объектов реальными данными API.
Не беспокойтесь о том, что вы не видите изменений в браузере — данные еще не определены, и для Vue естественно не отображать то, чего он не знает. Однако, как только данные будут установлены (а на данный момент вы даже можете проверить их, жестко запрограммировав данные), текстовые данные будут контролироваться App.vue .
Метод data()
Метод data() — это специальная конструкция в файлах .vue — она содержит и возвращает объекты данных, которые так важны для приложения. Вспомните общую структуру части <script> в любом файле .vue — примерно она содержит следующее:
<script> // import statements here export default { // name, components, props, etc. data() { return { // the data that is so crucial for the application is defined here. // the data objects will have certain default values chosen by us. // The methods that we define below will manipulate the data. // Since the data is bounded to various attributes and directives, they // will update as and when the values of the data objects change. } }, methods: { // methods (objects whose values are functions) here. // bulk of dynamic stuff (the black magic part) is controlled from here. }, computed: { // computed properties here }, // other objects, as necessary } </script>
До сих пор вы встречались с именами некоторых объектов данных, но их гораздо больше. Большинство из них относятся к дочерним компонентам, каждый из которых обрабатывает отдельный аспект дампа информации о погоде. Ниже приведен весь метод data() , который нам понадобится для этого проекта — у вас будет четкое представление о том, какие данные мы ожидаем от API, и как мы распространяем данные на основе номенклатуры объектов.
data() { return { weatherDetails: false, location: '', // raw location from input lat: '', // raw latitude from google maps api response long: '', // raw longitude from google maps api response completeWeatherApi: '', // weather api string with lat and long rawWeatherData: '', // raw response from weather api currentWeather: { full_location: '', // for full address formatted_lat: '', // for N/S formatted_long: '', // for E/W time: '', temp: '', todayHighLow: { todayTempHigh: '', todayTempHighTime: '', todayTempLow: '', todayTempLowTime: '' }, summary: '', possibility: '' }, tempVar: { tempToday: [ // gets added dynamically by this.getSetHourlyTempInfoToday() ], }, highlights: { uvIndex: '', visibility: '', windStatus: { windSpeed: '', windDirection: '', derivedWindDirection: '' }, } }; },
Как видите, в большинстве случаев значение по умолчанию пусто, потому что на данном этапе этого достаточно. Будут написаны методы для манипулирования данными и заполнения их соответствующими значениями до того, как они будут отображены или переданы дочерним компонентам.
Методы в App.vue
Для файлов .vue методы обычно записываются как значения ключей, вложенных в объект methods { } . Их основная роль заключается в управлении объектами данных компонента. Мы напишем методы в App.vue придерживаясь той же философии. Однако в зависимости от их назначения мы можем разделить методы App.vue на следующие категории:
Вспомогательные методы
Методы, ориентированные на действие/событие
Методы сбора данных
Методы обработки данных
Методы клея высокого уровня
Важно, чтобы вы это понимали — мы представляем вам методы на блюдечке, потому что мы уже выяснили, как работают API, какие данные они дают и как мы должны использовать эти данные в нашем проекте. Дело не в том, что мы взяли методы из воздуха и написали какой-то загадочный код для работы с данными. В целях обучения хорошим упражнением будет внимательно прочитать и понять код методов и данных. Однако, когда вы сталкиваетесь с новым проектом, который вам нужно создавать с нуля, вы должны делать всю грязную работу самостоятельно, а это означает много экспериментировать с API — их программным доступом и их структурой данных, прежде чем бесшовно склеить их с данными. структура, которую требует ваш проект. Вас не будут держать за руку, и будут разочаровывающие моменты, но это все часть взросления как разработчика.
В следующих подразделах мы объясним каждый из типов методов, а также покажем реализацию методов, принадлежащих к этой категории. Имена методов говорят сами за себя в отношении их назначения, как и их реализация, которую, как мы полагаем, вы найдете достаточно простой. Однако перед этим вспомним общую схему написания методов в .vue файлах:
<script> // import statements here export default { // name, components, props, etc. data() { return { // the data that is so crucial for the application is defined here. } }, methods: { // methods (objects whose values are functions) here. // bulk of dynamic stuff (the black magic part) is controlled from here. method_1: function(arg_1) { }, method_2: function(arg_1, arg_2) { }, method_3: function(arg_1) { }, ……. }, computed: { // computed properties here }, // other objects, as necessary } </script>
Вспомогательные методы
Вспомогательные методы, как следует из названия, — это методы, написанные в первую очередь для модуляризации повторяющегося кода, используемого для второстепенных задач. При необходимости они вызываются другими методами. Ниже приведены служебные методы для App.vue :
convertToTitleCase: function(str) { str = str.toLowerCase().split(' '); for (var i = 0; i < str.length; i++) { str[i] = str[i].charAt(0).toUpperCase() + str[i].slice(1); } return str.join(' '); },
// To format the “possibility” (of weather) string obtained from the weather API formatPossibility: function(str) { str = str.toLowerCase().split('-'); for (var i = 0; i < str.length; i++) { str[i] = str[i].charAt(0).toUpperCase() + str[i].slice(1); } return str.join(' '); },
// To convert Unix timestamps according to our convenience unixToHuman: function(timezone, timestamp) { /* READ THIS BEFORE JUDGING & DEBUGGING For any location beyond the arctic circle and the antarctic circle, the goddamn weather api does not return certain keys/values in each of this.rawWeatherData.daily.data[some_array_index]. Due to this, console throws up an error. The code is correct, the problem is with the API. May be later on I will add some padding to tackle missing values. */ var moment = require('moment-timezone'); // for handling date & time var decipher = new Date(timestamp * 1000); var human = moment(decipher) .tz(timezone) .format('llll'); var timeArray = human.split(' '); var timeNumeral = timeArray[4]; var timeSuffix = timeArray[5]; var justTime = timeNumeral + ' ' + timeSuffix; var monthDateArray = human.split(','); var monthDate = monthDateArray[1].trim(); return { fullTime: human, onlyTime: justTime, onlyMonthDate: monthDate }; },
// To convert temperature from fahrenheit to celcius fahToCel: function(tempInFahrenheit) { var tempInCelcius = Math.round((5 / 9) * (tempInFahrenheit — 32)); return tempInCelcius; },
// To convert the air pressure reading from millibar to kilopascal milibarToKiloPascal: function(pressureInMilibar) { var pressureInKPA = pressureInMilibar * 0.1; return Math.round(pressureInKPA); },
// To convert distance readings from miles to kilometers mileToKilometer: function(miles) { var kilometer = miles * 1.60934; return Math.round(kilometer); },
Хотя мы не реализовали это, вы можете вынуть методы утилиты из файла .vue и поместить их в отдельный файл JavaScript. Все, что вам нужно сделать, это импортировать файл .js в начале части скрипта в файл .vue , и все готово. Такой подход работает очень хорошо и поддерживает чистоту кода, особенно в больших приложениях, где вы можете использовать множество методов, которые лучше сгруппированы в зависимости от их назначения. Вы можете применить этот подход ко всем группам методов, перечисленным в этой статье, и увидеть сам эффект. Тем не менее, мы предлагаем вам выполнить это упражнение после того, как вы прошли курс, представленный здесь, чтобы у вас было общее представление о всех частях, работающих в полной синхронизации, а также у вас была работающая часть программного обеспечения, к которой вы можете обратиться, как только что-то ломается во время эксперимента.
Методы, ориентированные на действие/событие
Эти методы обычно выполняются, когда нам нужно выполнить действие, соответствующее событию. В зависимости от случая событие может быть вызвано взаимодействием с пользователем или программно. В файле App.vue эти методы находятся ниже служебных методов.
detectEnterKeyPress: function() { var input = this.$refs.input; input.addEventListener('keyup', function(event) { event.preventDefault(); var enterKeyCode = 13; if (event.keyCode === enterKeyCode) { this.setHitEnterKeyTrue(); } }); },
locationEntered: function() { var input = this.$refs.input; if (input.value === '') { this.location = "New York"; } else { this.location = this.convertToTitleCase(input.value); } this.makeInputEmpty(); this.makeTempVarTodayEmpty(); },
Одна интересная вещь в некоторых из приведенных выше фрагментов кода — использование $ref . Проще говоря, это способ Vue связать оператор кода, содержащий его, с конструкцией HTML, на которую он должен воздействовать (для получения дополнительной информации прочитайте официальное руководство). Например, методы makeInputEmpty() и detectEnterKeyPress() воздействуют на поле ввода, потому что в HTML поля ввода мы упомянули значение атрибута ref в качестве input .
Методы сбора данных
В нашем проекте мы используем следующие два API:
API геокодера Карт Google Этот API предназначен для получения координат местоположения, которое ищет пользователь. Вам понадобится ключ API для себя, который вы можете получить, следуя документации по данной ссылке. На данный момент вы можете использовать API-ключ, используемый FusionCharts, но мы просим вас не злоупотреблять им и получить свой собственный ключ. Мы ссылаемся на JavaScript API из index.html этого проекта и будем использовать предоставленные им конструкторы для нашего кода в файле App.vue .
Погодный API темного неба Этот API предназначен для получения данных о погоде, соответствующих координатам. Однако мы не будем использовать его напрямую; мы поместим его в URL-адрес, который перенаправляет через один из серверов FusionCharts. Причина в том, что если вы отправляете запрос GET к API из полностью клиентского приложения, такого как наше, это приводит к досадной ошибке CORS (дополнительная информация здесь и здесь).
Важное примечание . Поскольку мы использовали API Google Maps и Dark Sky, оба этих API имеют свои собственные ключи API, которыми мы поделились с вами в этой статье.Это поможет вам сосредоточиться на разработках на стороне клиента, а не на головной боли реализации серверной части.Тем не менее, мы рекомендуем вам создать свои собственные ключи , потому что наши ключи API будут иметь ограничения, и если эти ограничения превысят, вы не сможете попробовать приложение самостоятельно.
Для Карт Google перейдите к этой статье, чтобы получить ключ API. Для Dark Sky API посетите https://darksky.net/dev, чтобы создать ключ API и соответствующие конечные точки.
Имея в виду контекст, давайте посмотрим на реализацию методов сбора данных для нашего проекта.
getCoordinates: function() { this.locationEntered(); var loc = this.location; var coords; var geocoder = new google.maps.Geocoder(); return new Promise(function(resolve, reject) { geocoder.geocode({ address: loc }, function(results, status) { if (status == google.maps.GeocoderStatus.OK) { this.lat = results[0].geometry.location.lat(); this.long = results[0].geometry.location.lng(); this.full_location = results[0].formatted_address; coords = { lat: this.lat, long: this.long, full_location: this.full_location }; resolve(coords); } else { alert("Oops! Couldn't get data for the location"); } }); }); },
/* The coordinates that Google Maps Geocoder API returns are way too accurate for our requirements. We need to bring it into shape before passing the coordinates on to the weather API. Although this is a data processing method in its own right, we can't help mentioning it right now, because the data acquisition method for the weather API has dependency on the output of this method. */ setFormatCoordinates: async function() { var coordinates = await this.getCoordinates(); this.lat = coordinates.lat; this.long = coordinates.long; this.currentWeather.full_location = coordinates.full_location; // Remember to beautify lat for N/S if (coordinates.lat > 0) { this.currentWeather.formatted_lat = (Math.round(coordinates.lat * 10000) / 10000).toString() + '°N'; } else if (coordinates.lat < 0) { this.currentWeather.formatted_lat = (-1 * (Math.round(coordinates.lat * 10000) / 10000)).toString() + '°S'; } else { this.currentWeather.formatted_lat = ( Math.round(coordinates.lat * 10000) / 10000 ).toString(); } // Remember to beautify long for N/S if (coordinates.long > 0) { this.currentWeather.formatted_long = (Math.round(coordinates.long * 10000) / 10000).toString() + '°E'; } else if (coordinates.long < 0) { this.currentWeather.formatted_long = (-1 * (Math.round(coordinates.long * 10000) / 10000)).toString() + '°W'; } else { this.currentWeather.formatted_long = ( Math.round(coordinates.long * 10000) / 10000 ).toString(); } },
/* This method dynamically creates the the correct weather API query URL, based on the formatted latitude and longitude. The complete URL is then fed to the method querying for weather data. Notice that the base URL used in this method (without the coordinates) points towards a FusionCharts server — we must redirect our GET request to the weather API through a server to avoid the CORS error. */ fixWeatherApi: async function() { await this.setFormatCoordinates(); var weatherApi = 'https://csm.fusioncharts.com/files/assets/wb/wb-data.php?src=darksky&lat=' + this.lat + '&long=' + this.long; this.completeWeatherApi = weatherApi; },
fetchWeatherData: async function() { await this.fixWeatherApi(); var axios = require('axios'); // for handling weather api promise var weatherApiResponse = await axios.get(this.completeWeatherApi); if (weatherApiResponse.status === 200) { this.rawWeatherData = weatherApiResponse.data; } else { alert('Hmm... Seems like our weather experts are busy!'); } },
Through these methods, we have introduced the concept of async-await in our code. If you have been a JavaScript developer for some time now, you must be familiar with the callback hell, which is a direct consequence of the asynchronous way JavaScript is written. ES6 allows us to bypass the cumbersome nested callbacks, and our code becomes much cleaner if we write JavaScript in a synchronous way, using the async-await technique. However, there is a downside. It takes away the speed that asynchronous code gives us, especially for the portions of the code that deals with data being exchanged over the internet. Since this is not a mission-critical application with low latency requirements, and our primary aim is to learn stuff, the clean code is much more preferable over the slightly fast code.
Data Processing Methods
Now that we have the methods that will bring the data to us, we need to prepare the ground for properly receiving and processing the data. Safety nets must be cast, and there should be no spills — data is the new gold (OK, that might be an exaggeration in our context)! Enough with the fuss, let's get to the point.
Technically, the methods we implement in this section are aimed at getting the data out of the acquisition methods and the data objects in App.vue , and sometimes setting the data objects to certain values that suits the purpose.
getSetHourlyTempInfoToday: function() { var unixTime = this.rawWeatherData.currently.time; var timezone = this.getTimezone(); var todayMonthDate = this.unixToHuman(timezone, unixTime).onlyMonthDate; var hourlyData = this.getHourlyInfoToday(); for (var i = 0; i < hourlyData.length; i++) { var hourlyTimeAllTypes = this.unixToHuman(timezone, hourlyData[i].time); var hourlyOnlyTime = hourlyTimeAllTypes.onlyTime; var hourlyMonthDate = hourlyTimeAllTypes.onlyMonthDate; if (todayMonthDate === hourlyMonthDate) { var hourlyObject = { hour: '', temp: '' }; hourlyObject.hour = hourlyOnlyTime; hourlyObject.temp = this.fahToCel(hourlyData[i].temperature).toString(); this.tempVar.tempToday.push(hourlyObject); /* Since we are using array.push(), we are just adding elements at the end of the array. Thus, the array is not getting emptied first when a new location is entered. to solve this problem, a method this.makeTempVarTodayEmpty() has been created, and called from this.locationEntered(). */ } } /* To cover the edge case where the local time is between 10 — 12 PM, and therefore there are only two elements in the array this.tempVar.tempToday. We need to add the points for minimum temperature and maximum temperature so that the chart gets generated with atleast four points. */ if (this.tempVar.tempToday.length <= 2) { var minTempObject = { hour: this.currentWeather.todayHighLow.todayTempHighTime, temp: this.currentWeather.todayHighLow.todayTempHigh }; var maxTempObject = { hour: this.currentWeather.todayHighLow.todayTempLowTime, temp: this.currentWeather.todayHighLow.todayTempLow }; /* Typically, lowest temp are at dawn, highest temp is around mid day. Thus we can safely arrange like min, max, temp after 10 PM. */ // array.unshift() adds stuff at the beginning of the array. // the order will be: min, max, 10 PM, 11 PM. this.tempVar.tempToday.unshift(maxTempObject, minTempObject); } },
После того, как мы убрали с пути утилиту, методы получения и обработки, нам осталась задача организовать все это целиком. Мы делаем это, создавая связующие методы высокого уровня, которые, по сути, вызывают описанные выше методы в определенной последовательности, так что вся операция выполняется без проблем.
// Top level for info section // Data in this.currentWeather organizeCurrentWeatherInfo: function() { // data in this.currentWeather /* Coordinates and location is covered (get & set) in: — this.getCoordinates() — this.setFormatCoordinates() There are lots of async-await involved there. So it's better to keep them there. */ this.getSetCurrentTime(); this.getSetCurrentTemp(); this.getSetTodayTempHighLowWithTime(); this.getSetSummary(); this.getSetPossibility(); },
// Top level for highlights organizeTodayHighlights: function() { // top level for highlights this.getSetUVIndex(); this.getSetVisibility(); this.getSetWindStatus(); },
// Top level organization and rendering organizeAllDetails: async function() { // top level organization await this.fetchWeatherData(); this.organizeCurrentWeatherInfo(); this.organizeTodayHighlights(); this.getSetHourlyTempInfoToday(); },
смонтированный
Vue предоставляет хуки жизненного цикла экземпляра — свойства, которые по сути являются методами и срабатывают, когда жизненный цикл экземпляра достигает этой стадии. Например, create, Mount, beforeUpdate и т. д. — все это очень полезные хуки жизненного цикла, которые позволяют программисту управлять экземпляром на гораздо более детальном уровне, чем это было бы возможно в противном случае.
В коде компонента Vue эти хуки жизненного цикла реализованы так же, как и для любого другого prop . Например:
<template> </template> <script> // import statements export default { data() { return { // data objects here } }, methods: { // methods here }, mounted: function(){ // function body here }, } </script> <style> </style>
Вооружившись этим новым пониманием, взгляните на приведенный ниже код mounted опоры App.vue :
В этом разделе мы рассмотрели много вопросов, а последние несколько разделов дали вам информацию по частям. Однако важно, чтобы у вас был полный собранный код для App.vue (с учетом дальнейших изменений в последующих разделах). Вот оно:
<template> <div> <div class="container-fluid"> <div class="row"> <div class="col-md-3 col-sm-4 col-xs-12 sidebar"> <div> <input type="text" ref="input" placeholder="Location?" @keyup.enter="organizeAllDetails" > <button @click="organizeAllDetails"> <img src="./assets/Search.svg" width="24" height="24"> </button> </div> <div> <div class="wrapper-left"> <div> {{ currentWeather.temp }} <span>°C</span> </div> <div>{{ currentWeather.summary }}</div> <div class="temp-max-min"> <div class="max-desc"> <div> <i>▲</i> {{ currentWeather.todayHighLow.todayTempHigh }} <span>°C</span> </div> <div>at {{ currentWeather.todayHighLow.todayTempHighTime }}</div> </div> <div class="min-desc"> <div> <i>▼</i> {{ currentWeather.todayHighLow.todayTempLow }} <span>°C</span> </div> <div>at {{ currentWeather.todayHighLow.todayTempLowTime }}</div> </div> </div> </div> <div class="wrapper-right"> <div class="date-time-info"> <div> <img src="./assets/calendar.svg" width="20" height="20"> {{ currentWeather.time }} </div> </div> <div class="location-info"> <div> <img src="./assets/location.svg" width="10.83" height="15.83" > {{ currentWeather.full_location }} <div class="mt-1"> Lat: {{ currentWeather.formatted_lat }} <br> Long: {{ currentWeather.formatted_long }} </div> </div> </div> </div> </div> </div> <dashboard-content class="col-md-9 col-sm-8 col-xs-12 content" :highlights="highlights" :tempVar="tempVar" ></dashboard-content> </div> </div> </div> </template> <script> import Content from './components/Content.vue'; export default { name: 'app', props: [], components: { 'dashboard-content': Content }, data() { return { weatherDetails: false, location: '', // raw location from input lat: '', // raw latitude from google maps api response long: '', // raw longitude from google maps api response completeWeatherApi: '', // weather api string with lat and long rawWeatherData: '', // raw response from weather api currentWeather: { full_location: '', // for full address formatted_lat: '', // for N/S formatted_long: '', // for E/W time: '', temp: '', todayHighLow: { todayTempHigh: '', todayTempHighTime: '', todayTempLow: '', todayTempLowTime: '' }, summary: '', possibility: '' }, tempVar: { tempToday: [ // gets added dynamically by this.getSetHourlyTempInfoToday() ], }, highlights: { uvIndex: '', visibility: '', windStatus: { windSpeed: '', windDirection: '', derivedWindDirection: '' }, } }; }, methods: { // Some utility functions convertToTitleCase: function(str) { str = str.toLowerCase().split(' '); for (var i = 0; i < str.length; i++) { str[i] = str[i].charAt(0).toUpperCase() + str[i].slice(1); } return str.join(' '); }, formatPossibility: function(str) { str = str.toLowerCase().split('-'); for (var i = 0; i < str.length; i++) { str[i] = str[i].charAt(0).toUpperCase() + str[i].slice(1); } return str.join(' '); }, unixToHuman: function(timezone, timestamp) { /* READ THIS BEFORE JUDGING & DEBUGGING For any location beyond the arctic circle and the antarctic circle, the goddamn weather api does not return certain keys/values in each of this.rawWeatherData.daily.data[some_array_index]. Due to this, console throws up an error. The code is correct, the problem is with the API. May be later on I will add some padding to tackle missing values. */ var moment = require('moment-timezone'); // for handling date & time var decipher = new Date(timestamp * 1000); var human = moment(decipher) .tz(timezone) .format('llll'); var timeArray = human.split(' '); var timeNumeral = timeArray[4]; var timeSuffix = timeArray[5]; var justTime = timeNumeral + ' ' + timeSuffix; var monthDateArray = human.split(','); var monthDate = monthDateArray[1].trim(); return { fullTime: human, onlyTime: justTime, onlyMonthDate: monthDate }; }, fahToCel: function(tempInFahrenheit) { var tempInCelcius = Math.round((5 / 9) * (tempInFahrenheit — 32)); return tempInCelcius; }, milibarToKiloPascal: function(pressureInMilibar) { var pressureInKPA = pressureInMilibar * 0.1; return Math.round(pressureInKPA); }, mileToKilometer: function(miles) { var kilometer = miles * 1.60934; return Math.round(kilometer); }, deriveWindDir: function(windDir) { var wind_directions_array = [ { minVal: 0, maxVal: 30, direction: 'N' }, { minVal: 31, maxVal: 45, direction: 'NNE' }, { minVal: 46, maxVal: 75, direction: 'NE' }, { minVal: 76, maxVal: 90, direction: 'ENE' }, { minVal: 91, maxVal: 120, direction: 'E' }, { minVal: 121, maxVal: 135, direction: 'ESE' }, { minVal: 136, maxVal: 165, direction: 'SE' }, { minVal: 166, maxVal: 180, direction: 'SSE' }, { minVal: 181, maxVal: 210, direction: 'S' }, { minVal: 211, maxVal: 225, direction: 'SSW' }, { minVal: 226, maxVal: 255, direction: 'SW' }, { minVal: 256, maxVal: 270, direction: 'WSW' }, { minVal: 271, maxVal: 300, direction: 'W' }, { minVal: 301, maxVal: 315, direction: 'WNW' }, { minVal: 316, maxVal: 345, direction: 'NW' }, { minVal: 346, maxVal: 360, direction: 'NNW' } ]; var wind_direction = ''; for (var i = 0; i < wind_directions_array.length; i++) { if ( windDir >= wind_directions_array[i].minVal && windDir <= wind_directions_array[i].maxVal ) { wind_direction = wind_directions_array[i].direction; } } return wind_direction; }, // Some basic action oriented functions makeInputEmpty: function() { this.$refs.input.value = ''; }, makeTempVarTodayEmpty: function() { this.tempVar.tempToday = []; }, detectEnterKeyPress: function() { var input = this.$refs.input; input.addEventListener('keyup', function(event) { event.preventDefault(); var enterKeyCode = 13; if (event.keyCode === enterKeyCode) { this.setHitEnterKeyTrue(); } }); }, locationEntered: function() { var input = this.$refs.input; if (input.value === '') { this.location = "New York"; } else { this.location = this.convertToTitleCase(input.value); } this.makeInputEmpty(); this.makeTempVarTodayEmpty(); }, getCoordinates: function() { this.locationEntered(); var loc = this.location; var coords; var geocoder = new google.maps.Geocoder(); return new Promise(function(resolve, reject) { geocoder.geocode({ address: loc }, function(results, status) { if (status == google.maps.GeocoderStatus.OK) { this.lat = results[0].geometry.location.lat(); this.long = results[0].geometry.location.lng(); this.full_location = results[0].formatted_address; coords = { lat: this.lat, long: this.long, full_location: this.full_location }; resolve(coords); } else { alert("Oops! Couldn't get data for the location"); } }); }); }, // Some basic asynchronous functions setFormatCoordinates: async function() { var coordinates = await this.getCoordinates(); this.lat = coordinates.lat; this.long = coordinates.long; this.currentWeather.full_location = coordinates.full_location; // Remember to beautify lat for N/S if (coordinates.lat > 0) { this.currentWeather.formatted_lat = (Math.round(coordinates.lat * 10000) / 10000).toString() + '°N'; } else if (coordinates.lat < 0) { this.currentWeather.formatted_lat = (-1 * (Math.round(coordinates.lat * 10000) / 10000)).toString() + '°S'; } else { this.currentWeather.formatted_lat = ( Math.round(coordinates.lat * 10000) / 10000 ).toString(); } // Remember to beautify long for N/S if (coordinates.long > 0) { this.currentWeather.formatted_long = (Math.round(coordinates.long * 10000) / 10000).toString() + '°E'; } else if (coordinates.long < 0) { this.currentWeather.formatted_long = (-1 * (Math.round(coordinates.long * 10000) / 10000)).toString() + '°W'; } else { this.currentWeather.formatted_long = ( Math.round(coordinates.long * 10000) / 10000 ).toString(); } }, fixWeatherApi: async function() { await this.setFormatCoordinates(); var weatherApi = 'https://csm.fusioncharts.com/files/assets/wb/wb-data.php?src=darksky&lat=' + this.lat + '&long=' + this.long; this.completeWeatherApi = weatherApi; }, fetchWeatherData: async function() { await this.fixWeatherApi(); var axios = require('axios'); // for handling weather api promise var weatherApiResponse = await axios.get(this.completeWeatherApi); if (weatherApiResponse.status === 200) { this.rawWeatherData = weatherApiResponse.data; } else { alert('Hmm... Seems like our weather experts are busy!'); } }, // Get and set functions; often combined, because they are short // For basic info — left panel/sidebar getTimezone: function() { return this.rawWeatherData.timezone; }, getSetCurrentTime: function() { var currentTime = this.rawWeatherData.currently.time; var timezone = this.getTimezone(); this.currentWeather.time = this.unixToHuman( timezone, currentTime ).fullTime; }, getSetSummary: function() { var currentSummary = this.convertToTitleCase( this.rawWeatherData.currently.summary ); if (currentSummary.includes(' And')) { currentSummary = currentSummary.replace(' And', ','); } this.currentWeather.summary = currentSummary; }, getSetPossibility: function() { var possible = this.formatPossibility(this.rawWeatherData.daily.icon); if (possible.includes(' And')) { possible = possible.replace(' And', ','); } this.currentWeather.possibility = possible; }, getSetCurrentTemp: function() { var currentTemp = this.rawWeatherData.currently.temperature; this.currentWeather.temp = this.fahToCel(currentTemp); }, getTodayDetails: function() { return this.rawWeatherData.daily.data[0]; }, getSetTodayTempHighLowWithTime: function() { var timezone = this.getTimezone(); var todayDetails = this.getTodayDetails(); this.currentWeather.todayHighLow.todayTempHigh = this.fahToCel( todayDetails.temperatureMax ); this.currentWeather.todayHighLow.todayTempHighTime = this.unixToHuman( timezone, todayDetails.temperatureMaxTime ).onlyTime; this.currentWeather.todayHighLow.todayTempLow = this.fahToCel( todayDetails.temperatureMin ); this.currentWeather.todayHighLow.todayTempLowTime = this.unixToHuman( timezone, todayDetails.temperatureMinTime ).onlyTime; }, getHourlyInfoToday: function() { return this.rawWeatherData.hourly.data; }, getSetHourlyTempInfoToday: function() { var unixTime = this.rawWeatherData.currently.time; var timezone = this.getTimezone(); var todayMonthDate = this.unixToHuman(timezone, unixTime).onlyMonthDate; var hourlyData = this.getHourlyInfoToday(); for (var i = 0; i < hourlyData.length; i++) { var hourlyTimeAllTypes = this.unixToHuman(timezone, hourlyData[i].time); var hourlyOnlyTime = hourlyTimeAllTypes.onlyTime; var hourlyMonthDate = hourlyTimeAllTypes.onlyMonthDate; if (todayMonthDate === hourlyMonthDate) { var hourlyObject = { hour: '', temp: '' }; hourlyObject.hour = hourlyOnlyTime; hourlyObject.temp = this.fahToCel(hourlyData[i].temperature).toString(); this.tempVar.tempToday.push(hourlyObject); /* Since we are using array.push(), we are just adding elements at the end of the array. Thus, the array is not getting emptied first when a new location is entered. to solve this problem, a method this.makeTempVarTodayEmpty() has been created, and called from this.locationEntered(). */ } } /* To cover the edge case where the local time is between 10 — 12 PM, and therefore there are only two elements in the array this.tempVar.tempToday. We need to add the points for minimum temperature and maximum temperature so that the chart gets generated with atleast four points. */ if (this.tempVar.tempToday.length <= 2) { var minTempObject = { hour: this.currentWeather.todayHighLow.todayTempHighTime, temp: this.currentWeather.todayHighLow.todayTempHigh }; var maxTempObject = { hour: this.currentWeather.todayHighLow.todayTempLowTime, temp: this.currentWeather.todayHighLow.todayTempLow }; /* Typically, lowest temp are at dawn, highest temp is around mid day. Thus we can safely arrange like min, max, temp after 10 PM. */ // array.unshift() adds stuff at the beginning of the array. // the order will be: min, max, 10 PM, 11 PM. this.tempVar.tempToday.unshift(maxTempObject, minTempObject); } }, // For Today Highlights getSetUVIndex: function() { var uvIndex = this.rawWeatherData.currently.uvIndex; this.highlights.uvIndex = uvIndex; }, getSetVisibility: function() { var visibilityInMiles = this.rawWeatherData.currently.visibility; this.highlights.visibility = this.mileToKilometer(visibilityInMiles); }, getSetWindStatus: function() { var windSpeedInMiles = this.rawWeatherData.currently.windSpeed; this.highlights.windStatus.windSpeed = this.mileToKilometer( windSpeedInMiles ); var absoluteWindDir = this.rawWeatherData.currently.windBearing; this.highlights.windStatus.windDirection = absoluteWindDir; this.highlights.windStatus.derivedWindDirection = this.deriveWindDir( absoluteWindDir ); }, // top level for info section organizeCurrentWeatherInfo: function() { // data in this.currentWeather /* Coordinates and location is covered (get & set) in: — this.getCoordinates() — this.setFormatCoordinates() There are lots of async-await involved there. So it's better to keep them there. */ this.getSetCurrentTime(); this.getSetCurrentTemp(); this.getSetTodayTempHighLowWithTime(); this.getSetSummary(); this.getSetPossibility(); }, organizeTodayHighlights: function() { // top level for highlights this.getSetUVIndex(); this.getSetVisibility(); this.getSetWindStatus(); }, // topmost level orchestration organizeAllDetails: async function() { // top level organization await this.fetchWeatherData(); this.organizeCurrentWeatherInfo(); this.organizeTodayHighlights(); this.getSetHourlyTempInfoToday(); }, }, mounted: async function() { this.location = "New York"; await this.organizeAllDetails(); } }; </script>
И, наконец, после такого большого терпения и тяжелой работы вы можете увидеть поток данных с его необузданной мощью! Откройте приложение в браузере, обновите страницу, найдите местоположение в окне поиска приложения и нажмите Enter !
(Большой превью)
Теперь, когда мы закончили со всей тяжелой работой, сделайте перерыв. Последующие разделы посвящены использованию данных для создания красивых и информативных диаграмм, после чего нашему уродливо выглядящему приложению будет проведен заслуженный сеанс груминга с использованием CSS.
5. Визуализация данных с помощью FusionCharts
Фундаментальные соображения о диаграммах
Для конечного пользователя суть информационной панели заключается в следующем: набор отфильтрованной и тщательно отобранной информации по определенной теме, передаваемой с помощью визуальных/графических инструментов для быстрого восприятия. Их не волнуют тонкости проектирования вашего конвейера данных или эстетичность вашего кода — все, что им нужно, — это высокоуровневое представление за 3 секунды. Поэтому наше сырое приложение, отображающее текстовые данные, для них ничего не значит, и давно пора реализовать механизмы для обертывания данных диаграммами.
Однако, прежде чем мы углубимся в реализацию диаграмм, давайте рассмотрим некоторые уместные вопросы и возможные ответы с нашей точки зрения:
Какие типы диаграмм подходят для типа данных, с которыми мы имеем дело? Что ж, ответ имеет два аспекта — контекст и цель. Под контекстом мы подразумеваем тип данных, и в целом они вписываются в схему более крупных вещей, ограниченных масштабом и аудиторией проекта. А под назначением мы по сути подразумеваем «на чем мы хотим сделать акцент?». Например, мы можем представить сегодняшнюю температуру в разное время дня с помощью гистограммы (вертикальные столбцы одинаковой ширины с высотой, пропорциональной значению, которое представляет столбец). Однако нас редко интересуют отдельные значения, а скорее общая вариация и тенденция во всех данных. Для достижения этой цели в наших интересах использовать линейную диаграмму, и мы сделаем это в ближайшее время.
Что следует иметь в виду, прежде чем выбрать библиотеку графиков? Поскольку мы делаем проект преимущественно с использованием технологий, основанных на JavaScript, несложно понять, что любая библиотека построения диаграмм, которую мы выбираем для нашего проекта, должна быть родной для мира JavaScript. Имея в виду эту основную предпосылку, мы должны рассмотреть следующее, прежде чем остановиться на какой-либо конкретной библиотеке:
Поддержка выбранных нами фреймворков , в данном случае это Vue.js. Проект может быть разработан в других популярных средах JavaScript, таких как React или Angular — проверьте поддержку библиотеки диаграмм для вашей любимой среды. Кроме того, необходимо учитывать поддержку других популярных языков программирования, таких как Python, Java, C++, .Net (AS и VB), особенно когда проект включает в себя серьезные внутренние компоненты.
Наличие типов и характеристик диаграмм , так как практически невозможно заранее знать, каков будет окончательный вид и назначение данных в проекте (особенно если требования регулируются вашими клиентами в профессиональной среде). В этом случае вам следует раскинуть свою сеть пошире и выбрать библиотеку диаграмм, в которой имеется самая обширная коллекция диаграмм. Что еще более важно, чтобы отличать ваш проект от других, в библиотеке должно быть достаточно функций в виде настраиваемых атрибутов диаграмм, чтобы вы могли точно настраивать и настраивать большинство аспектов диаграмм и нужный уровень детализации. Кроме того, конфигурации диаграмм по умолчанию должны быть разумными, а документация библиотеки должна быть на высшем уровне по причинам, очевидным для профессиональных разработчиков.
Также необходимо учитывать кривую обучения, сообщество поддержки и баланс , особенно если вы новичок в визуализации данных. На одном конце спектра у вас есть проприетарные инструменты, такие как Tableau и Qlickview, которые стоят бомбы, имеют плавную кривую обучения, но также имеют множество ограничений с точки зрения настраиваемости, интеграции и развертывания. С другой стороны, есть d3.js — обширный, бесплатный (с открытым исходным кодом) и настраиваемый до мозга костей, но вы должны заплатить цену очень крутой кривой обучения, чтобы иметь возможность делать что-либо продуктивное с библиотекой.
Что вам нужно, так это «золотая середина» — правильный баланс между производительностью, охватом, настраиваемостью, кривой обучения и, конечно же, стоимостью.Мы предлагаем вам взглянуть на FusionCharts — самую полную в мире и готовую к работе библиотеку диаграмм JavaScript для Интернета и мобильных устройств, которую мы будем использовать в этом проекте для создания диаграмм.
Введение в FusionCharts
FusionCharts используется во всем мире как библиотека для построения диаграмм JavaScript миллионами разработчиков в сотнях стран по всему миру. Технически он настолько загружаемый и настраиваемый, насколько это возможно, с поддержкой интеграции практически с любым популярным техническим стеком, используемым для веб-проектов. Для коммерческого использования FusionCharts требуется лицензия, и вы должны заплатить за лицензию в зависимости от вашего варианта использования (пожалуйста, свяжитесь с отделом продаж, если вам интересно). Тем не менее, мы используем FusionCharts в этих проектах только для того, чтобы попробовать несколько вещей, и, следовательно, нелицензионную версию (поставляется с небольшим водяным знаком на ваших диаграммах и некоторыми другими ограничениями). Использование нелицензионной версии совершенно нормально, когда вы пробуете графики и используете их в своих некоммерческих или личных проектах. Если вы планируете развернуть приложение на коммерческой основе, убедитесь, что у вас есть лицензия от FusionCharts.
Поскольку это проект с участием Vue.js, нам понадобятся два модуля, которые необходимо установить, если это не было сделано ранее:
Модуль fusioncharts , поскольку он содержит все необходимое для создания диаграмм.
Модуль vue-fusioncharts , который по сути является оболочкой для fusioncharts, поэтому его можно использовать в проекте Vue.js.
Если вы не установили их ранее (как указано в третьем разделе), установите их, выполнив следующую команду из корневого каталога проекта:
npm install fusioncharts vue-fusioncharts --save
Затем убедитесь, что файл проекта src/main.js имеет следующий код (также упомянутый в разделе 3):
import Vue from 'vue'; import App from './App.vue'; import FusionCharts from 'fusioncharts'; import Charts from 'fusioncharts/fusioncharts.charts'; import Widgets from 'fusioncharts/fusioncharts.widgets'; import PowerCharts from 'fusioncharts/fusioncharts.powercharts'; import FusionTheme from 'fusioncharts/themes/fusioncharts.theme.fusion'; import VueFusionCharts from 'vue-fusioncharts'; Charts(FusionCharts); PowerCharts(FusionCharts); Widgets(FusionCharts); FusionTheme(FusionCharts); Vue.use(VueFusionCharts, FusionCharts); new Vue({ el: '#app', render: h => h(App) })
Возможно, наиболее важной строкой в приведенном выше фрагменте является следующая:
Vue.use(VueFusionCharts, FusionCharts)
Он указывает Vue использовать модуль vue-fusioncharts для понимания многих вещей в проекте, которые явно не определены нами, но определены в самом модуле. Кроме того, этот тип оператора подразумевает глобальное объявление, под которым мы подразумеваем, что везде, где Vue встречает что-то странное в коде нашего проекта (вещи, которые мы явно не определили в отношении использования FusionCharts), он по крайней мере один раз заглянет в vue-fusioncharts. и модули узлов fusioncharts для их определений, прежде чем выдавать ошибки. Если бы мы использовали FusionCharts в изолированной части нашего проекта (не используя его почти во всех файлах компонентов), то, возможно, локальное объявление имело бы больше смысла.
После этого все готово для использования FusionCharts в проекте. Мы будем использовать довольно много различных карт, выбор которых зависит от аспекта данных о погоде, которые мы хотим визуализировать. Кроме того, мы увидим взаимодействие привязки данных, пользовательских компонентов и наблюдателей в действии.
Общая схема использования Fusioncharts в .vue
В этом разделе мы объясним общую идею использования FusionCharts для создания различных диаграмм в файлах .vue . Но сначала давайте посмотрим на псевдокод, который схематически иллюстрирует основную идею.
Давайте разберемся с различными частями приведенного выше псевдокода:
В <template> , на верхнем уровне <div> (это практически обязательно для HTML-кода шаблона каждого компонента), у нас есть пользовательский компонент <fusioncharts> . У нас есть определение компонента, содержащегося в модуле Node vue-fusioncharts , который мы установили для этого проекта. Внутри vue-fusioncharts использует модуль fusioncharts , который также был установлен. Мы импортировали необходимые модули и разрешили их зависимости, проинструктировали Vue использовать оболочку глобально (во всем проекте) в файле src/main.js , и поэтому нет недостатка в определении используемого нами пользовательского компонента <fusioncharts> здесь. Кроме того, пользовательский компонент имеет настраиваемые атрибуты, и каждый из настраиваемых атрибутов привязан к объекту данных (и, в свою очередь, к их значениям) с помощью директивы v-bind , сокращенным обозначением которой является символ двоеточия ( : ). Мы узнаем об атрибутах и связанных с ними объектах данных более подробно, когда будем обсуждать некоторые из конкретных диаграмм, используемых в этом проекте.
В <script> сначала вы объявляете свойства, которые должен получать компонент, а затем переходите к определению объектов данных, которые привязаны к атрибутам <fusioncharts> . Значения, присвоенные объектам данных, — это значения, которые извлекаются атрибутами <fusioncharts> , и диаграммы создаются на основе этих извлеченных значений. Помимо этого, наиболее интересной частью кода является объект watch { } . Это очень особенный объект в схеме вещей Vue — он, по сути, инструктирует Vue следить за любыми изменениями, происходящими с определенными данными, а затем выполнять действия, основанные на том, как была определена функция handler для этих данных. Например, мы хотим, чтобы Vue следил за полученной prop , т. е. data_prop_received_by_the_component в псевдокоде. prop становится ключом в объекте watch { } , а значением ключа является другой объект — метод-обработчик, описывающий, что нужно делать при prop изменении свойства. Благодаря таким элегантным механизмам обработки изменений приложение сохраняет свою реактивность. deep: true представляет логический флаг, который вы можете ассоциировать с наблюдателями, чтобы отслеживаемый объект отслеживался довольно глубоко, т. е. отслеживались даже изменения, сделанные на вложенных уровнях объекта. ( Для получения дополнительной информации о наблюдателях обратитесь к официальной документации ).
Теперь, когда у вас есть понимание общей схемы вещей, давайте углубимся в конкретные реализации диаграмм в файлах компонентов .vue . Код будет довольно понятным, и вы должны попытаться понять, как особенности вписываются в общую схему вещей, описанную выше.
Реализация диаграмм в файлах .vue
Хотя сама специфика реализации варьируется от одной диаграммы к другой, следующее объяснение применимо ко всем из них:
<template> Как объяснялось ранее, пользовательский компонент <fusioncharts> имеет несколько атрибутов, каждый из которых привязан к соответствующему объекту данных, определенному в функции data() с помощью директивы v-bind :. Имена атрибутов говорят сами за себя, что они означают, и определение соответствующих объектов данных также тривиально.
<script> В функции data() объекты данных и их значения — это то, что заставляет диаграммы работать из-за привязки, выполняемой директивами v-bind ( : ), используемыми для атрибутов <fusioncharts> . Прежде чем мы углубимся в отдельные объекты данных, стоит упомянуть некоторые общие характеристики:
Объекты данных, значения которых равны 0 или 1 , по своей природе являются булевыми, где 0 представляет что-то недоступное/выключенное, а 1 представляет доступное/включенное состояние. Однако будьте осторожны, так как небулевы объекты данных могут также иметь значения 0 или 1 , помимо других возможных значений — это зависит от контекста. Например, containerbackgroundopacity со значением по умолчанию, равным 0 , является логическим значением, тогда как lowerLimit со значением по умолчанию, равным 0 , просто означает, что число ноль является его буквальным значением.
Некоторые объекты данных имеют дело со свойствами CSS, такими как поля, отступы, размер шрифта и т. д. — значение имеет подразумеваемую единицу измерения «px» или пиксель. Точно так же другие объекты данных могут иметь неявные единицы, связанные с их значениями. Подробную информацию см. на соответствующей странице атрибутов диаграммы в Центре разработчиков FusionCharts.
В функции data() , пожалуй, самым интересным и неочевидным объектом является dataSource. Этот объект имеет три основных объекта, вложенных в него:
chart : этот объект инкапсулирует множество атрибутов диаграммы, связанных с конфигурацией и внешним видом диаграммы. Это почти обязательная конструкция, которую вы найдете во всех диаграммах, которые будете создавать для этого проекта.
colorrange : этот объект несколько специфичен для рассматриваемой диаграммы и в основном присутствует в диаграммах, которые имеют дело с несколькими цветами/оттенками, чтобы разграничить различные поддиапазоны шкалы, используемой в диаграмме.
значение: этот объект, опять же, присутствует в диаграммах, имеет определенное значение, которое необходимо выделить в диапазоне шкалы.
Объект watch { } , пожалуй, самая важная вещь, благодаря которой эта диаграмма и другие диаграммы, используемые в этом проекте, оживают. Реактивность диаграмм, т. е. самообновление диаграмм на основе новых значений, полученных в результате нового пользовательского запроса, контролируется наблюдателями, определенными в этом объекте. Например, мы определили наблюдателя для highlights свойств, полученных компонентом, а затем определили функцию-обработчик, чтобы проинструктировать Vue о необходимых действиях, которые он должен предпринять, когда что-либо изменяется в отслеживаемом объекте во всем проекте. Это означает, что всякий раз, когда App.vue новое значение для любого объекта в highlights , информация просачивается вниз к этому компоненту, и новое значение обновляется в объектах данных этого компонента. Диаграмма, привязанная к значениям, также обновляется в результате этого механизма.
Приведенные выше объяснения являются довольно общими, чтобы помочь нам развить интуитивное понимание более широкой картины. Как только вы интуитивно поймете концепции, вы всегда сможете обратиться к документации Vue.js и FusionCharts, когда вам что-то непонятно из самого кода. Мы оставляем это упражнение на вас, и, начиная со следующего подраздела, мы не будем объяснять то, что рассмотрели в этом подразделе.
Этот компонент содержит чрезвычайно полезную диаграмму — Angular Gauge.
(Большой превью)
Код компонента приведен ниже. Подробную информацию об атрибутах диаграммы Angular Gauge см. на странице Центра разработки FusionCharts для Angular Gauge.
В этом компоненте мы используем Horizontal Linear Gauge для представления видимости, как показано на изображении ниже:
(Большой превью)
Код компонента приведен ниже. Для более глубокого понимания различных атрибутов этого типа диаграммы, пожалуйста, обратитесь к странице Центра разработчиков FusionCharts для горизонтальной линейной шкалы.
Этот компонент отображает скорость и направление ветра (скорость ветра, если вы разбираетесь в физике), и представить вектор с помощью диаграммы очень сложно. В таких случаях мы предлагаем представить их с помощью красивых изображений и текстовых значений. Поскольку представление, о котором мы думали, полностью зависит от CSS, мы реализуем его в следующем разделе, посвященном CSS. Однако взгляните на то, что мы стремимся создать:
Напомним, что мы уже реализовали код с помощью CSS для всех компонентов — кроме Content.vue и Highlights.vue . Поскольку Content.vue — это тупой компонент, который просто передает данные, минимальный необходимый стиль уже рассмотрен. Кроме того, мы уже написали соответствующий код для оформления боковой панели и карточек с диаграммами. Поэтому все, что нам осталось сделать, это добавить некоторые стилистические элементы в Highlights.vue , что в основном связано с использованием классов CSS:
С диаграммами и стилем в порядке, мы закончили! Найдите минутку, чтобы оценить красоту вашего творения.
(Большой превью)
Пришло время развернуть ваше приложение и поделиться им с коллегами. Если вы не очень хорошо разбираетесь в развертывании и ожидаете, что мы вам поможем, ознакомьтесь с нашим подходом к идеям развертывания. Связанная статья также содержит предложения о том, как удалить водяной знак FusionCharts в левом нижнем углу каждой диаграммы.
Если вы где-то ошиблись и вам нужна точка отсчета, исходный код доступен на Github.