Verwenden von Vue.js zum Erstellen eines interaktiven Wetter-Dashboards mit APIs

Veröffentlicht: 2022-03-10
Kurze Zusammenfassung ↬ Das Erstellen eines Dashboards mit API-Daten ist oft eine komplexe Angelegenheit. Die Auswahl Ihres Tech-Stacks, die Integration von APIs, die Auswahl der richtigen Diagramme und die Verschönerung mit CSS-Stilen können schwierig werden. Dieses Tutorial ist eine Schritt-für-Schritt-Anleitung, die Ihnen hilft, ein Wetter-Dashboard in Vue.js mithilfe von API-Daten zu erstellen.

(Dies ist ein gesponserter Artikel.) In diesem Tutorial erstellen Sie ein einfaches Wetter-Dashboard von Grund auf neu. Es wird eine clientseitige Anwendung sein, die weder ein „Hello World“-Beispiel noch zu einschüchternd in ihrer Größe und Komplexität ist.

Das gesamte Projekt wird mit Tools aus dem Ökosystem Node.js + npm entwickelt. Insbesondere werden wir uns für die Daten stark auf die Dark Sky API verlassen, Vue.js für all das schwere Heben und FusionCharts für die Datenvisualisierung.

Voraussetzungen

Wir gehen davon aus, dass Sie mit Folgendem vertraut sind:

  • HTML5 und CSS3 (wir werden auch die grundlegenden Funktionen von Bootstrap verwenden;
  • JavaScript (insbesondere ES6 Art, die Sprache zu verwenden);
  • Node.js und npm (die Grundlagen der Umgebung und der Paketverwaltung sind in Ordnung).

Abgesehen von den oben genannten wäre es großartig, wenn Sie mit Vue.js oder einem anderen ähnlichen JavaScript-Framework vertraut wären. Wir erwarten nicht, dass Sie FusionCharts kennen – es ist so einfach zu bedienen, dass Sie es im Handumdrehen lernen werden!

Erwartete Erkenntnisse

Ihre wichtigsten Erkenntnisse aus diesem Projekt sind:

  1. So planen Sie die Implementierung eines guten Dashboards
  2. So entwickeln Sie Anwendungen mit Vue.js
  3. So erstellen Sie datengesteuerte Anwendungen
  4. So visualisieren Sie Daten mit FusionCharts

Insbesondere bringt Sie jeder der Abschnitte den Lernzielen einen Schritt näher:

  1. Eine Einführung in das Wetter-Dashboard
    Dieses Kapitel gibt Ihnen einen Überblick über verschiedene Aspekte des Vorhabens.
  2. Erstellen Sie das Projekt
    In diesem Abschnitt erfahren Sie, wie Sie mit dem Vue-Befehlszeilentool ein Projekt von Grund auf neu erstellen.
  3. Passen Sie die Standardprojektstruktur an
    Das standardmäßige Projektgerüst, das Sie im vorherigen Abschnitt erhalten, reicht nicht aus; Hier lernen Sie die zusätzlichen Dinge, die für das Projekt aus struktureller Sicht erforderlich sind.
  4. Datenerfassung und -verarbeitung
    Dieser Abschnitt ist das Kernstück des Projekts; Der gesamte kritische Code zum Erfassen und Verarbeiten von Daten aus der API wird hier gezeigt. Erwarten Sie, dass Sie die maximale Zeit für diesen Abschnitt aufwenden.
  5. Datenvisualisierung mit FusionCharts
    Sobald wir alle Daten und andere bewegliche Teile des Projekts stabilisiert haben, widmet sich dieser Abschnitt der Visualisierung der Daten mit FusionCharts und etwas CSS.

1. Der Dashboard-Workflow

Bevor wir uns mit der Implementierung befassen, ist es wichtig, sich über unseren Plan im Klaren zu sein. Wir unterteilen unseren Plan in vier verschiedene Aspekte:

Anforderungen

Was sind unsere Anforderungen an dieses Projekt? Mit anderen Worten, was sind die Dinge, die wir über unser Wetter-Dashboard präsentieren möchten? In Anbetracht dessen, dass unser beabsichtigtes Publikum wahrscheinlich nur Sterbliche mit einfachen Vorlieben sind, möchten wir ihnen Folgendes zeigen:

  • Details des Ortes, für den sie das Wetter sehen möchten, zusammen mit einigen primären Informationen über das Wetter. Da es keine strengen Anforderungen gibt, werden wir die langweiligen Details später herausfinden. Zu diesem Zeitpunkt ist es jedoch wichtig zu beachten, dass wir dem Publikum ein Suchfeld bereitstellen müssen, damit es Eingaben für den Ort seines Interesses machen kann.
  • Grafische Informationen über das Wetter am Ort von Interesse, wie z. B.:
    • Temperaturverlauf für den Tag der Abfrage
    • Highlights des heutigen Wetters:
      • Windgeschwindigkeit und -richtung
      • Sichtweite
      • UV-Index

Hinweis : Die von der API erhaltenen Daten liefern Informationen zu vielen anderen Aspekten des Wetters. Wir haben uns entschieden, nicht alle zu verwenden, um den Code auf ein Minimum zu beschränken.

Struktur

Basierend auf den Anforderungen können wir unser Dashboard wie folgt strukturieren:

Dashboard-Struktur
(Große Vorschau)

Daten

Unser Dashboard ist so gut wie die Daten, die wir bekommen, denn ohne richtige Daten gibt es keine schönen Visualisierungen. Es gibt viele öffentliche APIs, die Wetterdaten bereitstellen – einige davon sind kostenlos, andere nicht. Für unser Projekt werden wir Daten von der Dark Sky API sammeln. Wir können den API-Endpunkt jedoch nicht direkt vom Client-Ende aus abrufen. Keine Sorge, wir haben eine Problemumgehung, die genau zum richtigen Zeitpunkt bekannt gegeben wird! Sobald wir die Daten für den gesuchten Ort erhalten haben, werden wir einige Daten verarbeiten und formatieren – Sie wissen schon, die Art von technischen Details, die uns helfen, die Rechnungen zu bezahlen.

Visualisierung

Sobald wir saubere und formatierte Daten erhalten, schließen wir sie an FusionCharts an. Es gibt weltweit nur sehr wenige JavaScript-Bibliotheken, die so leistungsfähig sind wie FusionCharts. Aus der großen Anzahl von Angeboten von FusionCharts werden wir nur wenige verwenden – alle sind in JavaScript geschrieben, funktionieren aber nahtlos, wenn sie mit dem Vue-Wrapper für FusionCharts integriert sind.

Bewaffnet mit dem großen Ganzen, lasst uns unsere Hände schmutzig machen – es ist Zeit, die Dinge konkret zu machen! Im nächsten Abschnitt erstellen Sie das grundlegende Vue-Projekt, auf dem wir weiter aufbauen.

2. Erstellen des Projekts

Um das Projekt zu erstellen, führen Sie die folgenden Schritte aus:

  1. Installieren Sie Node.js + npm
    ( Wenn Sie Node.js auf Ihrem Computer installiert haben, überspringen Sie diesen Schritt. )
    Node.js wird mit npm geliefert, sodass Sie npm nicht separat installieren müssen. Laden Sie je nach Betriebssystem Node.js gemäß den hier angegebenen Anweisungen herunter und installieren Sie es.

    Nach der Installation ist es wahrscheinlich eine gute Idee zu überprüfen, ob die Software richtig funktioniert und welche Versionen sie hat. Öffnen Sie zum Testen die Befehlszeile/das Terminal und führen Sie die folgenden Befehle aus:
     node --version npm --version
  2. Pakete mit npm installieren
    Sobald Sie npm eingerichtet und ausgeführt haben, führen Sie den folgenden Befehl aus, um die für unser Projekt erforderlichen Basispakete zu installieren.
     npm install -g vue@2 vue-cli@2
  3. Initialisieren Sie das Projektgerüst mit vue-cli
    Unter der Annahme, dass der vorherige Schritt gut verlaufen ist, besteht der nächste Schritt darin, vue-cli – ein Befehlszeilentool von Vue.js – zu verwenden, um das Projekt zu initialisieren. Führen Sie dazu Folgendes aus:
    • Initialisieren Sie das Gerüst mit der webpack-simple-Vorlage.
       vue init webpack-simple vue_weather_dashboard
      Ihnen werden eine Reihe von Fragen gestellt – die Standardeinstellungen für alle zu akzeptieren, aber die letzte Frage wird für dieses Projekt gut genug sein; Antwort N für die letzte.
      Ein Screenshot der Befehlszeile/des Terminals
      (Große Vorschau)
      Denken Sie daran, dass sich webpack-simple zwar hervorragend für schnelles Prototyping und leichte Anwendungen wie unsere eignet, aber nicht besonders für ernsthafte Anwendungen oder den Produktionseinsatz geeignet ist. Wenn Sie eine andere Vorlage verwenden möchten (wobei wir davon abraten würden, wenn Sie ein Neuling sind) oder Ihr Projekt anders benennen möchten, lautet die Syntax:
       vue init [template-name] [project-name]
    • Navigieren Sie zu dem Verzeichnis, das von vue-cli für das Projekt erstellt wurde.
       cd vue_weather_dashboard
    • Installieren Sie alle Pakete, die in der package.json erwähnt werden, die vom Tool vue-cli für die Vorlage webpack-simple erstellt wurde.
       npm install
    • Starten Sie den Entwicklungsserver und sehen Sie, wie Ihr Standard-Vue-Projekt im Browser funktioniert!
       npm run dev

Wenn Sie neu bei Vue.js sind, nehmen Sie sich einen Moment Zeit, um Ihre neueste Errungenschaft zu genießen – Sie haben eine kleine Vue-Anwendung erstellt und sie läuft unter localhost:8080!

Ein Screenshot der Vue.js-Website
(Große Vorschau)

Kurze Erläuterung der Standardprojektstruktur

Es ist an der Zeit, einen Blick auf die Struktur im Verzeichnis vue_weather_dashboard zu werfen, damit Sie die Grundlagen verstehen, bevor wir mit der Änderung beginnen.

Die Struktur sieht in etwa so aus:

 vue_weather_dashboard |--- README.md |--- node_modules/ | |--- ... | |--- ... | |--- [many npm packages we installed] | |--- ... | |--- ... |--- package.json |--- package-lock.json |--- webpack.config.js |--- index.html |--- src | |--- App.vue | |--- assets | | |--- logo.png | |--- main.js

Obwohl es verlockend sein mag, sich nicht mit den Standarddateien und -verzeichnissen vertraut zu machen, empfehlen wir Ihnen dringend, zumindest einen Blick auf den Inhalt der Dateien zu werfen, wenn Sie neu bei Vue sind. Es kann eine gute Aufklärungssitzung sein und Fragen aufwerfen, denen Sie selbst nachgehen sollten, insbesondere die folgenden Dateien:

  • package.json , und nur ein Blick auf seinen Cousin package-lock.json
  • webpack.config.js
  • index.html
  • src/main.js
  • src/App.vue

Nachfolgend finden Sie eine kurze Erläuterung der einzelnen Dateien und Verzeichnisse, die im Baumdiagramm angezeigt werden:

  • README.md
    Kein Preis für Raten – es ist in erster Linie Sache des Menschen, die Schritte zu lesen und zu verstehen, die zum Erstellen des Projektgerüsts erforderlich sind.
  • node_modules/
    Dies ist das Verzeichnis, in das npm die Pakete herunterlädt, die für den Kickstart des Projekts erforderlich sind. Die Informationen zu den erforderlichen Paketen sind in der Datei package.json verfügbar.
  • Paket.json
    Diese Datei wird vom Tool vue-cli basierend auf den Anforderungen der Vorlage webpack-simple und enthält Informationen zu den npm-Paketen (einschließlich ihrer Versionen und anderer Details), die installiert werden müssen. Sehen Sie sich den Inhalt dieser Datei genau an – hier sollten Sie nachsehen und vielleicht bearbeiten, um für das Projekt erforderliche Pakete hinzuzufügen/zu löschen, und dann npm install ausführen. Lesen Sie hier mehr über package.json .
  • Paketsperre.json
    Diese Datei wird von npm selbst erstellt und dient in erster Linie dazu, ein Protokoll der Dinge zu führen, die npm heruntergeladen und installiert hat.
  • webpack.config.js
    Dies ist eine JavaScript-Datei, die die Konfiguration von Webpack enthält – ein Tool, das verschiedene Aspekte unseres Projekts bündelt (Code, statische Assets, Konfiguration, Umgebungen, Verwendungsmodus usw.) und minimiert, bevor es dem Benutzer bereitgestellt wird. Der Vorteil besteht darin, dass alle Dinge automatisch miteinander verknüpft werden und die Benutzererfahrung aufgrund der Verbesserung der Anwendungsleistung erheblich verbessert wird (Seiten werden schnell bereitgestellt und im Browser schneller geladen). Wie Sie später feststellen werden, ist dies die Datei, die überprüft werden muss, wenn etwas im Build-System nicht so funktioniert, wie es beabsichtigt ist. Wenn Sie die Anwendung bereitstellen möchten, ist dies auch eine der Schlüsseldateien, die bearbeitet werden muss (lesen Sie hier mehr).
  • index.html
    Diese HTML-Datei dient als Matrix (oder Sie können sagen, Vorlage), in die Daten und Code dynamisch eingebettet werden (das ist es, was Vue hauptsächlich tut) und dann dem Benutzer bereitgestellt werden.
  • src/main.js
    Diese JavaScript-Datei enthält Code, der hauptsächlich Abhängigkeiten auf oberster/Projektebene verwaltet und die Vue-Komponente der obersten Ebene definiert. Kurz gesagt orchestriert es das JavaScript für das gesamte Projekt und dient als Einstiegspunkt der Anwendung. Bearbeiten Sie diese Datei, wenn Sie projektweite Abhängigkeiten von bestimmten Knotenmodulen deklarieren müssen oder etwas an der obersten Vue-Komponente im Projekt geändert werden soll.
  • src/App.vue
    Als wir im vorherigen Punkt über die „oberste Vue-Komponente“ gesprochen haben, haben wir im Wesentlichen über diese Datei gesprochen. Jede .vue-Datei im Projekt ist eine Komponente, und Komponenten sind hierarchisch miteinander verbunden. Am Anfang haben wir nur eine .vue -Datei, nämlich App.vue , als unsere einzige Komponente. Aber in Kürze werden wir unserem Projekt weitere Komponenten hinzufügen (in erster Linie der Struktur des Dashboards folgend) und sie gemäß unserer gewünschten Hierarchie verknüpfen, wobei App.vue der Vorfahre von allen ist. Diese .vue Dateien enthalten Code in einem Format, das Vue von uns schreiben lassen möchte. Keine Sorge, sie sind JavaScript-Code, der geschrieben wurde, um eine Struktur beizubehalten, die uns bei Verstand und Ordnung halten kann. Sie wurden gewarnt – am Ende dieses Projekts könnten Sie, wenn Sie neu bei Vue sind, süchtig nach der template — script — style template — script — style template — script — style , Code zu organisieren!

Nachdem wir die Grundlage geschaffen haben, ist es an der Zeit:

  • Ändern Sie die Vorlagen und optimieren Sie die Konfigurationsdateien ein wenig, damit sich das Projekt so verhält, wie wir es möchten.
  • Erstellen Sie neue .vue Dateien und implementieren Sie die Dashboard-Struktur mit Vue-Code.

Wir werden sie im nächsten Abschnitt lernen, der etwas lang wird und einige Aufmerksamkeit erfordert. Wenn Sie Koffein oder Wasser brauchen oder sich entladen möchten – jetzt ist der richtige Zeitpunkt!

3. Anpassen der Standardprojektstruktur

Es ist an der Zeit, an der Grundlage zu basteln, die uns das Gerüstprojekt gegeben hat. Bevor Sie beginnen, stellen Sie sicher, dass der von webpack bereitgestellte Entwicklungsserver ausgeführt wird. Der Vorteil, diesen Server kontinuierlich auszuführen, besteht darin, dass alle Änderungen, die Sie am Quellcode vornehmen – wenn Sie ihn speichern und die Webseite aktualisieren – sofort im Browser angezeigt werden.

Wenn Sie den Entwicklungsserver starten möchten, führen Sie einfach den folgenden Befehl vom Terminal aus (vorausgesetzt, Ihr aktuelles Verzeichnis ist das Projektverzeichnis):

 npm run dev

In den folgenden Abschnitten werden wir einige der vorhandenen Dateien ändern und einige neue Dateien hinzufügen. Es folgen kurze Erläuterungen zum Inhalt dieser Dateien, damit Sie eine Vorstellung davon bekommen, was diese Änderungen bewirken sollen.

Vorhandene Dateien ändern

index.html

Unsere Anwendung ist buchstäblich eine Single-Page-Anwendung, da es nur eine Webseite gibt, die im Browser angezeigt wird. Wir werden später darüber sprechen, aber lassen Sie uns zuerst unsere erste Änderung vornehmen – den Text innerhalb des <title> -Tags ändern.

Mit dieser kleinen Überarbeitung sieht die HTML-Datei wie folgt aus:

 <!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>

Nehmen Sie sich einen Moment Zeit, um die Webseite unter localhost:8080 zu aktualisieren, und sehen Sie, wie sich die Änderung in der Titelleiste der Registerkarte im Browser widerspiegelt – es sollte „Vue Weather Dashboard“ heißen. Dies diente jedoch nur dazu, Ihnen den Prozess des Vornehmens von Änderungen und des Überprüfens, ob es funktioniert, zu demonstrieren. Wir haben noch mehr zu tun!

Dieser einfachen HTML-Seite fehlen viele Dinge, die wir in unserem Projekt wollen, insbesondere die folgenden:

  • Einige Metainformationen
  • CDN-Links zu Bootstrap (CSS-Framework)
  • Link zu benutzerdefiniertem Stylesheet (muss noch im Projekt hinzugefügt werden)
  • Verweise auf die Google Maps Geolocation API vom <script> -Tag

Nach dem Hinzufügen dieser Dinge hat die endgültige index.html den folgenden Inhalt:

 <!DOCTYPE html> <html lang="en"> <head> <meta http-equiv="Content-Type" content="text/html;charset=utf-8" /> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"> <link rel="stylesheet" href="//maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css"> <link rel="stylesheet" type="text/css" href="src/css/style.css"> <title>Weather Dashboard</title> <script src="https://maps.googleapis.com/maps/api/js?key=AIzaSyC-lCjpg1xbw-nsCc11Si8Ldg2LKYizqI4&libraries=places"></script> </head> <body> <div></div> <script src="/dist/build.js"></script> </body> </html>

Speichern Sie die Datei und aktualisieren Sie die Webseite. Möglicherweise haben Sie beim Laden der Seite eine leichte Beule bemerkt – dies liegt hauptsächlich daran, dass der Seitenstil jetzt von Bootstrap gesteuert wird und die Stilelemente wie Schriftarten, Abstände usw. von der Standardeinstellung abweichen früher (wenn Sie sich nicht sicher sind, kehren Sie zur Standardeinstellung zurück und sehen Sie sich den Unterschied an).

Ein Screenshot, wenn Sie die Webseite mit localhost:8080 aktualisieren
(Große Vorschau)

Hinweis : Eine wichtige Sache, bevor wir fortfahren – die URL für die Google Maps-API enthält einen Schlüssel, der eine Eigenschaft von FusionCharts ist. Im Moment können Sie diesen Schlüssel verwenden, um das Projekt zu erstellen, da wir nicht möchten, dass Sie sich durch diese Art von winzigen Details verzetteln (die Sie ablenken können, wenn Sie neu sind). Wir empfehlen Ihnen jedoch dringend, Ihren eigenen Google Maps-API-Schlüssel zu generieren und zu verwenden, sobald Sie einige Fortschritte gemacht haben und sich sicher fühlen, auf diese kleinen Details zu achten.

Paket.json

Als wir dies schrieben, haben wir bestimmte Versionen der npm-Pakete für unser Projekt verwendet, und wir wissen mit Sicherheit, dass diese Dinge zusammenarbeiten. Bis Sie das Projekt ausführen, ist es jedoch sehr wahrscheinlich, dass die neuesten stabilen Versionen der Pakete, die npm für Sie herunterlädt, nicht die gleichen sind, die wir verwendet haben, und dies könnte den Code beschädigen (oder Dinge tun, die darüber hinausgehen unsere Kontrolle). Daher ist es sehr wichtig, genau dieselbe package.json -Datei zu haben, die zum Erstellen dieses Projekts verwendet wurde, damit unser Code/Erklärungen und die Ergebnisse, die Sie erhalten, konsistent sind.

Der Inhalt der Datei package.json sollte sein:

 { "name": "vue_weather_dashboard", "description": "A Vue.js project", "version": "1.0.0", "author": "FusionCharts", "license": "MIT", "private": true, "scripts": { "dev": "cross-env NODE_ENV=development webpack-dev-server --open --hot", "build": "cross-env NODE_ENV=production webpack --progress --hide-modules" }, "dependencies": { "axios": "^0.18.0", "babel": "^6.23.0", "babel-cli": "^6.26.0", "babel-polyfill": "^6.26.0", "fusioncharts": "^3.13.3", "moment": "^2.22.2", "moment-timezone": "^0.5.21", "vue": "^2.5.11", "vue-fusioncharts": "^2.0.4" }, "browserslist": [ "> 1%", "last 2 versions", "not ie <= 8" ], "devDependencies": { "babel-core": "^6.26.0", "babel-loader": "^7.1.2", "babel-preset-env": "^1.6.0", "babel-preset-stage-3": "^6.24.1", "cross-env": "^5.0.5", "css-loader": "^0.28.7", "file-loader": "^1.1.4", "vue-loader": "^13.0.5", "vue-template-compiler": "^2.4.4", "webpack": "^3.6.0", "webpack-dev-server": "^2.9.1" } }

Wir empfehlen Ihnen, die neue package.json und herauszufinden, welche Funktionen die verschiedenen Objekte in „json“ haben. Möglicherweise möchten Sie den Wert des Schlüssels „ author “ auf Ihren Namen ändern. Außerdem zeigen sich die in den Abhängigkeiten erwähnten Pakete zur richtigen Zeit im Code. Vorerst genügt es zu wissen:

  • babel -bezogene Pakete dienen der korrekten Handhabung des Codes im ES6-Stil durch den Browser;
  • axios beschäftigt sich mit Promise-basierten HTTP-Anfragen;
  • moment und moment-timezone dienen der Datums-/Zeitmanipulation;
  • fusioncharts und vue-fusioncharts sind für das Rendern von Diagrammen verantwortlich:
  • vue , aus offensichtlichen Gründen.

webpack.config.js

Wie bei package.json wir Ihnen, eine webpack.config.js -Datei zu verwalten, die mit der konsistent ist, die wir zum Erstellen des Projekts verwendet haben. Bevor Sie jedoch Änderungen vornehmen, empfehlen wir Ihnen, den Standardcode in webpack.config.js und den unten bereitgestellten Code sorgfältig zu vergleichen. Sie werden einige Unterschiede bemerken – googeln Sie sie und haben Sie eine grundlegende Vorstellung davon, was sie bedeuten. Da es den Rahmen dieses Artikels sprengen würde, Webpack-Konfigurationen ausführlich zu erläutern, sind Sie in dieser Hinsicht auf sich allein gestellt.

Die angepasste webpack.config.js -Datei sieht wie folgt aus:

 var path = require('path') var webpack = require('webpack') module.exports = { entry: ['babel-polyfill', './src/main.js'], output: { path: path.resolve(__dirname, './dist'), publicPath: '/dist/', filename: 'build.js' }, module: { rules: [ { test: /\.css$/, use: [ 'vue-style-loader', 'css-loader' ], }, { test: /\.vue$/, loader: 'vue-loader', options: { loaders: { } // other vue-loader options go here } }, { test: /\.js$/, loader: 'babel-loader', exclude: /node_modules/ }, { test: /\.(png|jpg|gif|svg)$/, loader: 'file-loader', options: { name: '[name].[ext]?[hash]' } } ] }, resolve: { alias: { 'vue$': 'vue/dist/vue.esm.js' }, extensions: ['*', '.js', '.vue', '.json'] }, devServer: { historyApiFallback: true, noInfo: true, overlay: true, host: '0.0.0.0', port: 8080 }, performance: { hints: false }, devtool: '#eval-source-map' } if (process.env.NODE_ENV === 'production') { module.exports.devtool = '#source-map' // https://vue-loader.vuejs.org/en/workflow/production.html module.exports.plugins = (module.exports.plugins || []).concat([ new webpack.DefinePlugin({ 'process.env': { NODE_ENV: '"production"' } }), new webpack.optimize.UglifyJsPlugin({ sourceMap: true, compress: { warnings: false } }), new webpack.LoaderOptionsPlugin({ minimize: true }) ]) }

Bei Änderungen an webpack.config.js des Projekts ist es zwingend erforderlich, dass Sie den laufenden Entwicklungsserver stoppen ( Strg + C ) und ihn mit dem folgenden Befehl neu starten, der aus dem Verzeichnis des Projekts ausgeführt wird, nachdem Sie alle im package.json erwähnten Pakete installiert haben package.json -Datei:

 npm install npm run dev

Damit endet die Tortur, die Konfigurationen zu optimieren und sicherzustellen, dass die richtigen Pakete vorhanden sind. Dies markiert jedoch auch die Reise des Modifizierens und Schreibens von Code, die ein bisschen lang, aber auch sehr lohnend ist!

src/main.js

Diese Datei ist der Schlüssel zur Orchestrierung des Projekts auf höchster Ebene – hier definieren wir:

  • Was die Abhängigkeiten der obersten Ebene sind (woher man die wichtigsten notwendigen npm-Pakete bekommt);
  • Wie man die Abhängigkeiten auflöst, zusammen mit Anweisungen an Vue zur Verwendung von Plugins/Wrappern, falls vorhanden;
  • Eine Vue-Instanz, die die oberste Komponente im Projekt verwaltet: src/App.vue (die .vue- .vue ).

In Übereinstimmung mit unseren Zielen für die Datei src/main.js sollte der Code wie folgt aussehen:

 // 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) })

src/App.vue

Dies ist eine der wichtigsten Dateien im gesamten Projekt und stellt die oberste Komponente in der Hierarchie dar – die gesamte Anwendung selbst als Ganzes. Für unser Projekt übernimmt diese Komponente die ganze schwere Arbeit, die wir später untersuchen werden. Fürs Erste wollen wir die Standard-Boilerplate loswerden und etwas Eigenes hinzufügen.

Wenn Sie mit der Organisation von Code in Vue noch nicht vertraut sind, ist es besser, sich ein Bild von der allgemeinen Struktur innerhalb der .vue Dateien zu machen. Die .vue Dateien bestehen aus drei Abschnitten:

  • Vorlage
    Hier wird das HTML-Template für die Seite definiert. Neben dem statischen HTML enthält dieser Abschnitt auch Vues Methode zum Einbetten dynamischer Inhalte mithilfe der doppelten geschweiften Klammern {{ }} .
  • Skript
    JavaScript regiert diesen Abschnitt und ist für die Generierung dynamischer Inhalte verantwortlich, die an geeigneten Stellen in die HTML-Vorlage eingefügt werden. Dieser Abschnitt ist in erster Linie ein Objekt, das exportiert wird, und besteht aus:
    • Daten
      Dies ist selbst eine Funktion und gibt normalerweise einige gewünschte Daten zurück, die in einer schönen Datenstruktur gekapselt sind.
    • Methoden
      Ein Objekt, das aus einer oder mehreren Funktionen/Methoden besteht, von denen jede normalerweise Daten auf die eine oder andere Weise manipuliert und auch den dynamischen Inhalt der HTML-Vorlage steuert.
    • Berechnet
      Ähnlich wie das oben besprochene Methodenobjekt mit einem wichtigen Unterschied – während alle Funktionen innerhalb des Methodenobjekts ausgeführt werden, wenn eine von ihnen aufgerufen wird, verhalten sich die Funktionen innerhalb des berechneten Objekts viel vernünftiger und werden ausgeführt, wenn und nur wenn dies der Fall war namens.
  • Stil
    Dieser Abschnitt ist für das CSS-Styling, das sich auf den HTML-Code der Seite bezieht (innerhalb der Vorlage geschrieben) – fügen Sie das gute alte CSS hier ein, um Ihre Seiten schön zu machen!

Unter Berücksichtigung des obigen Paradigmas passen wir den Code in App.vue minimal an:

 <template> <div> <p>This component's code is in {{ filename }}</p> </div> </template> <script> export default { data() { return { filename: 'App.vue' } }, methods: { }, computed: { }, } </script> <style> </style>

Denken Sie daran, dass das obige Code-Snippet lediglich zum Testen dient, dass App.vue mit unserem eigenen Code darin arbeitet. Es wird später viele Änderungen durchlaufen, aber speichern Sie zuerst die Datei und aktualisieren Sie die Seite im Browser.

Ein Screenshot des Browsers mit der Meldung „Dieser Komponentencode befindet sich in App.vue“
(Große Vorschau)

An diesem Punkt ist es wahrscheinlich eine gute Idee, sich Hilfe bei der Werkzeugausstattung zu holen. Schauen Sie sich die Vue-Entwicklungstools für Chrome an, und wenn Sie keine großen Probleme haben, Google Chrome als Standardbrowser für die Entwicklung zu verwenden, installieren Sie das Tool und spielen Sie ein wenig damit herum. Es wird sich als äußerst nützlich für die weitere Entwicklung und das Debuggen erweisen, wenn die Dinge komplizierter werden.

Zusätzliche Verzeichnisse und Dateien

Der nächste Schritt wäre, weitere Dateien hinzuzufügen, damit die Struktur unseres Projekts vollständig wird. Wir würden die folgenden Verzeichnisse und Dateien hinzufügen:

  • src/css/style.css
  • src/assets/calendar.svgvlocation.svgsearch.svgwinddirection.svgwindspeed.svg
  • src/components/Content.vueHighlights.vueTempVarChart.vueUVIndex.vueVisibility.vueWindStatus.vue

Hinweis : Speichern Sie die .svg Dateien in Ihrem Projekt.

Erstellen Sie die oben genannten Verzeichnisse und Dateien. Die endgültige Projektstruktur sollte wie folgt aussehen (denken Sie daran, Ordner und Dateien aus der Standardstruktur zu löschen, die jetzt unnötig sind):

 vue_weather_dashboard/ |--- README.md |--- node_modules/ | |--- ... | |--- ... | |--- [many npm packages we installed] | |--- ... | |--- ... |--- package.json |--- package-lock.json |--- webpack.config.js |--- index.html |--- src/ | |--- App.vue | |--- css/ | | |--- style.css | |--- assets/ | | |--- calendar.svg | | |--- location.svg | | |--- location.svg | | |--- winddirection.svg | | |--- windspeed.svg | |--- main.js | |--- components/ | | |--- Content.vue | | |--- Highlights.vue | | |--- TempVarChart.vue | | |--- UVIndex.vue | | |--- Visibility.vue | | |--- WindStatus.vue

Möglicherweise befinden sich einige andere Dateien wie .babelrc , .gitignore , .editorconfig usw. im Stammordner des Projekts. Sie können sie vorerst getrost ignorieren.

Im folgenden Abschnitt werden wir den neu hinzugefügten Dateien minimalen Inhalt hinzufügen und testen, ob sie ordnungsgemäß funktionieren.

src/css/style.css

Obwohl es nicht sofort von großem Nutzen sein wird, kopieren Sie den folgenden Code in die Datei:

 @import url("https://fonts.googleapis.com/css?family=Roboto:300,400,500"); :root { font-size: 62.5%; } body { font-family: Roboto; font-weight: 400; width: 100%; margin: 0; font-size: 1.6rem; } #sidebar { position: relative; display: flex; flex-direction: column; background-image: linear-gradient(-180deg, #80b6db 0%, #7da7e2 100%); } #search { text-align: center; height: 20vh; position: relative; } #location-input { height: 42px; width: 100%; opacity: 1; border: 0; border-radius: 2px; background-color: rgba(255, 255, 255, 0.2); margin-top: 16px; padding-left: 16px; color: #ffffff; font-size: 1.8rem; line-height: 21px; } #location-input:focus { outline: none; } ::placeholder { color: #FFFFFF; opacity: 0.6; } #current-weather { color: #ffffff; font-size: 8rem; line-height: 106px; position: relative; } #current-weather>span { color: #ffffff; font-size: 3.6rem; line-height: 42px; vertical-align: super; opacity: 0.8; top: 15px; position: absolute; } #weather-desc { font-size: 2.0rem; color: #ffffff; font-weight: 500; line-height: 24px; } #possibility { color: #ffffff; font-size: 16px; font-weight: 500; line-height: 19px; } #max-detail, #min-detail { color: #ffffff; font-size: 2.0rem; font-weight: 500; line-height: 24px; } #max-detail>i, #min-detail>i { font-style: normal; height: 13.27px; width: 16.5px; opacity: 0.4; } #max-detail>span, #min-detail>span { color: #ffffff; font-family: Roboto; font-size: 1.2rem; line-height: 10px; vertical-align: super; } #max-summary, #min-summary { opacity: 0.9; color: #ffffff; font-size: 1.4rem; line-height: 16px; margin-top: 2px; opacity: 0.7; } #search-btn { position: absolute; right: 0; top: 16px; padding: 2px; z-index: 999; height: 42px; width: 45px; background-color: rgba(255, 255, 255, 0.2); border: none; } #dashboard-content { text-align: center; height: 100vh; } #date-desc, #location-desc { color: #ffffff; font-size: 1.6rem; font-weight: 500; line-height: 19px; margin-bottom: 15px; } #date-desc>img { top: -3px; position: relative; margin-right: 10px; } #location-desc>img { top: -3px; position: relative; margin-left: 5px; margin-right: 15px; } #location-detail { opacity: 0.7; color: #ffffff; font-size: 1.4rem; line-height: 20px; margin-left: 35px; } .centered { position: fixed; top: 45%; left: 50%; transform: translate(-50%, -50%); } .max-desc { width: 80px; float: left; margin-right: 28px; } .temp-max-min { margin-top: 40px } #dashboard-content { background-color: #F7F7F7; } .custom-card { background-color: #FFFFFF !important; border: 0 !important; margin-top: 16px !important; margin-bottom: 20px !important; } .custom-content-card { background-color: #FFFFFF !important; border: 0 !important; margin-top: 16px !important; margin-bottom: 0px !important; } .header-card { height: 50vh; } .content-card { height: 43vh; } .card-divider { margin-top: 0; } .content-header { color: #8786A4; font-size: 1.4rem; line-height: 16px; font-weight: 500; padding: 15px 10px 5px 15px; } .highlights-item { min-height: 37vh; max-height: 38vh; background-color: #FFFFFF; } .card-heading { color: rgb(33, 34, 68); font-size: 1.8rem; font-weight: 500; line-height: 21px; text-align: center; } .card-sub-heading { color: #73748C; font-size: 1.6rem; line-height: 19px; } .card-value { color: #000000; font-size: 1.8rem; line-height: 21px; } span text { font-weight: 500 !important; } hr { padding-top: 1.5px; padding-bottom: 1px; margin-bottom: 0; margin-top: 0; line-height: 0.5px; } @media only screen and (min-width: 768px) { #sidebar { height: 100vh; } #info { position: fixed; bottom: 50px; width: 100%; padding-left: 15px; } .wrapper-right { margin-top: 80px; } } @media only screen and (min-width:1440px) { #sidebar { width: 350px; max-width: 350px; flex: auto; } #dashboard-content { width: calc(100% — 350px); max-width: calc(100% — 350px); flex: auto; } }

quelle/assets/

Laden Sie in diesem Verzeichnis die unten genannten .svg Dateien herunter und speichern Sie sie:

  • calendar.svg
  • location.svg
  • search.svg
  • winddirection.svg
  • windspeed.svg

src/components/Content.vue

Dies nennen wir eine „dumme Komponente“ (dh einen Platzhalter), die nur dazu da ist, die Hierarchie aufrechtzuerhalten, und im Wesentlichen Daten an ihre untergeordneten Komponenten weitergibt.

Denken Sie daran, dass es kein technisches Hindernis gibt, unseren gesamten Code in die App.vue -Datei zu schreiben, aber wir wählen aus zwei Gründen den Ansatz, den Code aufzuteilen, indem wir die Komponenten verschachteln:

  • Sauberen Code zu schreiben, der die Lesbarkeit und Wartbarkeit unterstützt;
  • Um die gleiche Struktur zu replizieren, die wir auf dem Bildschirm sehen werden, dh die Hierarchie.

Bevor wir die in Content.vue definierte Komponente in der Stammkomponente App.vue , schreiben wir etwas Spielzeugcode (aber lehrreich) für Content.vue :

 <template> <div> <p>This child components of Content.vue are:</p> <ul> <li v-for="child in childComponents">{{ child }}</li> </ul> </div> </template> <script> export default { data () { return { childComponents: ['TempVarChart.vue', 'Highlights.vue'] } }, methods: { }, computed: { }, } </script> <style> </style>

Beachten und verstehen Sie im Code Folgendes sorgfältig:

  • Innerhalb des <script> -Tags (wo wir offensichtlich etwas JavaScript-Code schreiben) definieren wir ein Objekt, das standardmäßig exportiert (für andere Dateien verfügbar gemacht) wird. Dieses Objekt enthält eine Funktion data() , die ein Array-Objekt namens childComponents , dessen Elemente Namen der Komponentendateien sind, die weiter verschachtelt werden sollen.
  • Innerhalb des <template> -Tags (wo wir eine HTML-Vorlage schreiben) ist das <ul> von Interesse.
    • Innerhalb der ungeordneten Liste sollte jedes Listenelement ein Name der beabsichtigten untergeordneten Komponenten sein, wie im Array-Objekt childComponents definiert. Außerdem sollte die Liste automatisch bis zum letzten Element des Arrays erweitert werden. Scheint, als sollten wir eine for -Schleife schreiben, nicht wahr? Dazu verwenden wir die von Vue.js bereitgestellte v-for Direktive. Die v-for Direktive:
      • Fungiert als Attribut des <li> -Tags, iteriert durch das Array, gibt die Namen der untergeordneten Komponenten wieder, wobei der Iterator in den {{ }} -Klammern erwähnt wird (wo wir den Text für die Listenelemente schreiben).

Der Code und die obige Erklärung bilden die Grundlage für Ihr späteres Verständnis, wie das Skript und die Vorlage zusammenhängen und wie wir die von Vue.js bereitgestellten Anweisungen verwenden können.

Wir haben ziemlich viel gelernt, aber selbst nach all dem müssen wir noch eines über die nahtlose Verbindung von Komponenten in der Hierarchie lernen – die Weitergabe von Daten von der übergeordneten Komponente an ihre untergeordneten Komponenten. Im Moment müssen wir lernen, wie einige Daten von src/App.vue an src/components/Content.vue werden, damit wir die gleichen Techniken für den Rest der Komponentenverschachtelung in diesem Projekt verwenden können.

Daten, die von den Eltern- zu den Kindkomponenten heruntersickern, mögen einfach klingen, aber der Teufel steckt im Detail! Wie unten kurz erläutert, sind mehrere Schritte erforderlich, damit es funktioniert:

  • Definieren und die Daten
    Fürs Erste wollen wir mit ein paar statischen Daten spielen – ein Objekt, das fest codierte Werte zu verschiedenen Aspekten des Wetters enthält, reicht vollkommen aus! Wir erstellen ein Objekt namens weather_data und geben es von der Funktion data() von App.vue . Das Objekt weather_data ist im folgenden Snippet angegeben:
 weather_data: { location: "California", temperature: { current: "35 C", }, highlights: { uvindex: "3", windstatus: { speed: "20 km/h", direction: "NE", }, visibility: "12 km", }, },
  • Weitergabe der Daten vom Elternteil
    Um die Daten weiterzugeben, brauchen wir ein Ziel, wohin wir die Daten senden wollen! In diesem Fall ist das Ziel die Content.vue Komponente, und die Implementierung erfolgt wie folgt:
    • Weisen Sie das Objekt weather_data einem benutzerdefinierten Attribut des <Content> -Tags zu
    • Binden Sie das Attribut mithilfe der von Vue.js bereitgestellten v-bind : -Direktive an die Daten, wodurch der Attributwert dynamisch wird (reagiert auf Änderungen an den Originaldaten).
       <Content v-bind:weather_data=“weather_data”></Content>

Das Definieren und Übergeben der Daten erfolgt auf der Quellseite des Handshakes, in unserem Fall die App.vue -Datei.

Der Code für die App.vue -Datei in ihrem aktuellen Status ist unten angegeben:

 <template> <div> <p>This component's code is in {{ filename }}</p> <Content v-bind:weather_data="weather_data"></Content> </div> </template> <script> import Content from './components/Content.vue' export default { name: 'app', components: { 'Content': Content }, data () { return { filename: 'App.vue', weather_data: { location: "California", temperature: { current: "35 C", }, highlights: { uvindex: "3", windstatus: { speed: "20 km/h", direction: "NE", }, visibility: "12 km", }, }, } }, methods: { }, computed: { }, } </script> <style> </style> 
Ein Screenshot des Browsers mit der Meldung „Der Code dieser Komponente befindet sich in App.vue. Diese untergeordneten Komponenten von Content.vue sind: TempVarChart.vue, Highlights.vue.
(Große Vorschau)

Nachdem die Daten definiert und von der Quelle (übergeordnete Komponente) übergeben wurden, liegt es nun in der Verantwortung des untergeordneten Elements, die Daten zu empfangen und entsprechend darzustellen, wie in den nächsten beiden Schritten erläutert.

  • Erhalt der Daten durch das Kind
    Die untergeordnete Komponente, in diesem Fall Content.vue , muss das Objekt weather_data empfangen, das von der übergeordneten Komponente App.vue an sie gesendet wird. Vue.js bietet dafür einen Mechanismus – alles, was Sie brauchen, ist ein Array-Objekt namens props , das in dem von Content.vue exportierten Standardobjekt definiert ist. Jedes Element der Array- props ist ein Name der Datenobjekte, die es von seinem Elternelement erhalten möchte. Im Moment ist das einzige Datenobjekt, das es empfangen soll, weather_data von App.vue. Somit sieht das props -Array folgendermaßen aus:
 <template> // HTML template code here </template> <script> export default { props: ["weather_data"], data () { return { // data here } }, } </script> <style> // component specific CSS here </style>
  • Rendern der Daten auf der Seite
    Nachdem wir nun sichergestellt haben, dass wir die Daten erhalten, ist die letzte Aufgabe, die wir erledigen müssen, das Rendern der Daten. Für dieses Beispiel werden wir die empfangenen Daten direkt auf der Webseite ausgeben, nur um die Technik zu veranschaulichen. In realen Anwendungen (wie der, die wir gerade erstellen) werden Daten jedoch normalerweise stark verarbeitet, und nur die relevanten Teile davon werden auf eine Weise angezeigt, die dem Zweck entspricht. In diesem Projekt werden wir beispielsweise Rohdaten von der Wetter-API erhalten, sie bereinigen und formatieren, die Daten in die für die Diagramme erforderlichen Datenstrukturen einspeisen und sie dann visualisieren. Wie auch immer, um den Rohdaten-Dump anzuzeigen, verwenden wir einfach die Klammern {{ }} , die Vue versteht, wie im folgenden Snippet gezeigt:
 <template> <div> // other template code here {{ weather_data }} </div> </template>

Es ist jetzt an der Zeit, alle Bits und Stücke zu assimilieren. Der Code für Content.vue – in seinem aktuellen Status – ist unten angegeben:

 <template> <div> <p>This child components of Content.vue are:</p> <ul> <li v-for="child in childComponents">{{ child }}</li> </ul> {{ weather_data }} </div> </template> <script> export default { props: ["weather_data"], data () { return { childComponents: ['TempVarChart.vue', 'Highlights.vue'] } }, methods: { }, computed: { }, } </script> <style> #pagecontent { border: 1px solid black; padding: 2px; } </style> 
Ein Screenshot des Browsers mit dem Ergebnis des bereitgestellten Codes
(Große Vorschau)

Nachdem Sie die oben beschriebenen Änderungen vorgenommen haben, aktualisieren Sie die Webseite im Browser und sehen Sie, wie sie aussieht. Nehmen Sie sich einen Moment Zeit, um die Komplexität zu verstehen, die Vue handhabt – wenn Sie das Objekt weather_data in App.vue , wird es stillschweigend an Content.vue und schließlich an den Browser übermittelt, der die Webseite anzeigt! Versuchen Sie es, indem Sie den Wert für die Schlüsselposition ändern.

Obwohl wir etwas über Requisiten und Datenbindung mit statischen Daten gelernt haben, werden wir dynamische Daten verwenden, die mit Web-APIs in der Anwendung gesammelt wurden, und den Code entsprechend ändern .

Zusammenfassung

Bevor wir zu den restlichen .vue -Dateien übergehen, fassen wir zusammen, was wir beim Schreiben des Codes für App.vue und components/Content.vue :

  • Die App.vue -Datei ist das, was wir die Stammkomponente nennen – diejenige, die sich an der Spitze der Komponentenhierarchie befindet. Der Rest der .vue Dateien stellt Komponenten dar, die ihre direkten untergeordneten, untergeordneten Elemente usw. sind.
  • Die Content.vue -Datei ist eine Dummy-Komponente – ihre Aufgabe ist es, die Daten an die darunter liegenden Ebenen weiterzugeben und die strukturelle Hierarchie aufrechtzuerhalten, damit unser Code mit der Philosophie „*was wir sehen, ist was wir implementieren*“ konsistent bleibt.
  • Die Eltern-Kind-Beziehung von Komponenten entsteht nicht aus dem Nichts – Sie müssen eine Komponente registrieren (entweder global oder lokal, abhängig von der beabsichtigten Verwendung der Komponente) und sie dann mit benutzerdefinierten HTML-Tags verschachteln (deren Schreibweise die exakte ist identisch mit dem der Namen, unter denen die Komponenten registriert wurden).
  • Einmal registriert und verschachtelt, werden Daten von übergeordneten zu untergeordneten Komponenten weitergegeben, und der Fluss ist nie umgekehrt (es passieren schlimme Dinge, wenn die Projektarchitektur einen Rückfluss zulässt). Die übergeordnete Komponente ist die relative Quelle der Daten und gibt relevante Daten an ihre untergeordneten Komponenten weiter, indem sie die v-bind Direktive für die Attribute der benutzerdefinierten HTML-Elemente verwendet. Das Kind erhält die für es bestimmten Daten mittels Requisiten und entscheidet dann selbst, was es mit den Daten macht.

Für die restlichen Komponenten verzichten wir auf eine detaillierte Erklärung – wir schreiben einfach den Code basierend auf den Erkenntnissen aus der obigen Zusammenfassung. Der Code ist selbsterklärend, und wenn Sie in Bezug auf die Hierarchie verwirrt sind, sehen Sie sich das folgende Diagramm an:

Ein Diagramm, das die Hierarchie des Codes erläutert
(Große Vorschau)

Das Diagramm besagt, dass TempVarChart.vue und Highlights.vue die direkten untergeordneten Elemente von Content.vue sind. Daher könnte es eine gute Idee sein, Content.vue für das Senden von Daten an diese Komponenten vorzubereiten, was wir mit dem folgenden Code tun:

 <template> <div> <p>This child components of Content.vue are:</p> <ul> <li v-for="child in childComponents">{{ child }}</li> </ul> {{ weather_data }} <temp-var-chart :tempVar="tempVar"></temp-var-chart> <today-highlights :highlights="highlights"></today-highlights> </div> </template> <script> import TempVarChart from './TempVarChart.vue' import Highlights from './Highlights.vue' export default { props: ["weather_data"], components: { 'temp-var-chart': TempVarChart, 'today-highlights': Highlights }, data () { return { childComponents: ['TempVarChart.vue', 'Highlights.vue'], tempVar: this.weather_data.temperature, highlights: this.weather_data.highlights, } }, methods: { }, computed: { }, } </script> <style> </style>

Sobald Sie diesen Code speichern, erhalten Sie Fehler — keine Sorge, es wird erwartet. Es wird behoben, sobald Sie die restlichen Komponentendateien bereit haben. Wenn es Sie stört, die Ausgabe nicht sehen zu können, kommentieren Sie die Zeilen mit den benutzerdefinierten Element-Tags <temp-var-chart> und <today-highlights> aus.

Für diesen Abschnitt ist dies der endgültige Code von Content.vue . Für den Rest dieses Abschnitts beziehen wir uns auf diesen Code und nicht auf die vorherigen, die wir zum Lernen geschrieben haben.

src/components/TempVarChart.vue

Da die übergeordnete Komponente Content.vue die Daten weitergibt, muss TempVarChart.vue eingerichtet werden, um die Daten zu empfangen und zu rendern, wie im folgenden Code gezeigt:

 <template> <div> <p>Temperature Information:</p> {{ tempVar }} </div> </template> <script> export default { props: ["tempVar"], data () { return { } }, methods: { }, computed: { }, } </script> <style> </style>

src/components/Highlights.vue

Diese Komponente erhält auch Daten von App.vue – ihrer übergeordneten Komponente. Danach sollte es mit seinen untergeordneten Komponenten verknüpft und relevante Daten an diese weitergegeben werden.

Sehen wir uns zuerst den Code zum Empfangen von Daten vom übergeordneten Element an:

 <template> <div> <p>Weather Highlights:</p> {{ highlights }} </div> </template> <script> export default { props: ["highlights"], data () { return { } }, methods: { }, computed: { }, } </script> <style> </style>

Zu diesem Zeitpunkt sieht die Webseite wie im folgenden Bild aus:

Ergebnis des im Browser angezeigten Codes
(Große Vorschau)

Jetzt müssen wir den Code von Highlights.vue ändern, um seine untergeordneten Komponenten zu registrieren und zu verschachteln, gefolgt von der Weitergabe der Daten an untergeordnete Elemente. Der Code dafür lautet wie folgt:

 <template> <div> <p>Weather Highlights:</p> {{ highlights }} <uv-index :highlights="highlights"></uv-index> <visibility :highlights="highlights"></visibility> <wind-status :highlights="highlights"></wind-status> </div> </template> <script> import UVIndex from './UVIndex.vue'; import Visibility from './Visibility.vue'; import WindStatus from './WindStatus.vue'; export default { props: ["highlights"], components: { 'uv-index': UVIndex, 'visibility': Visibility, 'wind-status': WindStatus, }, data () { return { } }, methods: { }, computed: { }, } </script> <style> </style>

Sobald Sie den Code gespeichert und die Webseite angezeigt haben, werden voraussichtlich Fehler im Entwicklerkonsolen-Tool angezeigt, das vom Browser bereitgestellt wird. Sie erscheinen, weil Highlights.vue zwar Daten sendet, aber niemand sie empfängt. Wir müssen noch den Code für die Kinder von Highlights.vue schreiben.

Beachten Sie, dass wir nicht viel von der Datenverarbeitung durchgeführt haben, dh wir haben die einzelnen Faktoren der Wetterdaten, die in den Abschnitt „Highlights“ des Dashboards eingehen, nicht extrahiert. Wir hätten das in der data() -Funktion machen können, aber wir haben es vorgezogen, Highlights.vue als dumme Komponente zu belassen, die einfach den gesamten Daten-Dump, den sie erhält, an jedes der Kinder weitergibt, die dann ihre eigenen Extrakte besitzen, was für sie notwendig ist . Wir empfehlen Ihnen jedoch, das Extrahieren von Daten in Highlights.vue auszuprobieren und relevante Daten an jede untergeordnete Komponente zu senden – es ist dennoch eine bewährte Übung!

src/components/UVIndex.vue

Der Code für diese Komponente empfängt den Daten-Dump der Highlights von Highlights.vue , extrahiert die Daten für den UV-Index und rendert sie auf der Seite.

 <template> <div> <p>UV Index: {{ uvindex }}</p> </div> </template> <script> export default { props: ["highlights"], data () { return { uvindex: this.highlights.uvindex } }, methods: { }, computed: { }, } </script> <style> </style>

src/components/Visibility.vue

Der Code für diese Komponente empfängt den Daten-Dump der Highlights von Highlights.vue , extrahiert die Daten für die Sichtbarkeit und rendert sie auf der Seite.

 <template> <div> <p>Visibility: {{ visibility }}</p> </div> </template> <script> export default { props: ["highlights"], data () { return { visibility: this.highlights.visibility, } }, methods: { }, computed: { }, } </script> <style> </style>

src/components/WindStatus.vue

Der Code für diese Komponente empfängt den Daten-Dump der Highlights von Highlights.vue , extrahiert die Daten für den Windstatus (Geschwindigkeit und Richtung) und rendert sie auf der Seite.

 <template> <div> <p>Wind Status:</p> <p>Speed — {{ speed }}; Direction — {{ direction }}</p> </div> </template> <script> export default { props: ["highlights"], data () { return { speed: this.highlights.windstatus.speed, direction: this.highlights.windstatus.direction } }, methods: { }, computed: { }, } </script> <style> </style>

Nachdem Sie den Code für alle Komponenten hinzugefügt haben, sehen Sie sich die Webseite im Browser an.

Ergebnis des im Browser angezeigten Codes
(Große Vorschau)

Nicht zu entmutigen, aber all diese Mühen bestanden nur darin, die Komponenten hierarchisch zu verknüpfen und zu testen, ob ein Datenfluss zwischen ihnen stattfindet oder nicht! Im nächsten Abschnitt werden wir den größten Teil des Codes, den wir bisher geschrieben haben, wegwerfen und viel mehr hinzufügen, das sich auf das eigentliche Projekt bezieht. Die Struktur und Verschachtelung der Bauteile werden wir aber auf jeden Fall beibehalten; Die Erkenntnisse aus diesem Abschnitt werden es uns ermöglichen, ein anständiges Dashboard mit Vue.js zu erstellen.

4. Datenerfassung und -verarbeitung

Erinnern Sie sich an das Objekt weather_data in App.vue ? Es hatte einige hartcodierte Daten, die wir verwendet haben, um zu testen, ob alle Komponenten richtig funktionieren, und um Ihnen auch dabei zu helfen, einige grundlegende Aspekte der Vue-Anwendung zu lernen, ohne sich in den Details realer Daten zu verzetteln. Es ist jedoch jetzt an der Zeit, dass wir unsere Hülle ablegen und in die reale Welt hinaustreten, wo Daten von der API den größten Teil unseres Codes dominieren werden.

Vorbereiten von untergeordneten Komponenten zum Empfangen und Verarbeiten echter Daten

In diesem Abschnitt erhalten Sie einen Code-Dump für alle Komponenten außer App.vue . Der Code behandelt den Empfang echter Daten von App.vue (im Gegensatz zu dem Code, den wir im vorherigen Abschnitt geschrieben haben, um Dummy-Daten zu empfangen und zu rendern).

Wir empfehlen dringend, den Code jeder Komponente sorgfältig zu lesen, damit Sie sich eine Vorstellung davon machen, welche Daten jede dieser Komponenten erwartet und schließlich in der Visualisierung verwenden wird.

Ein Teil des Codes und die Gesamtstruktur ähneln denen, die Sie in der vorherigen Struktur gesehen haben – Sie werden also nicht mit etwas drastisch Unterschiedlichem konfrontiert. Allerdings steckt der Teufel im Detail! Untersuchen Sie den Code also sorgfältig, und wenn Sie ihn einigermaßen gut verstanden haben, kopieren Sie den Code in die entsprechenden Komponentendateien in Ihrem Projekt.

Hinweis : Alle Komponenten in diesem Abschnitt befinden sich im Verzeichnis src/components/ . Daher wird der Pfad nicht jedes Mal erwähnt – nur der Name der .vue -Datei wird erwähnt, um die Komponente zu identifizieren.

Inhalt.vue

 <template> <div> <temp-var-chart :tempVar="tempVar"></temp-var-chart> <today-highlights :highlights="highlights"></today-highlights> </div> </template> <script> import TempVarChart from './TempVarChart.vue'; import Highlights from './Highlights.vue'; export default { props: ['highlights', 'tempVar'], components: { 'temp-var-chart': TempVarChart, 'today-highlights': Highlights }, } </script>

Die folgenden Änderungen wurden gegenüber dem vorherigen Code vorgenommen:

  • In <template> wurden Text und Daten innerhalb von {{ }} entfernt, da wir jetzt nur Daten empfangen und an die untergeordneten Elemente weitergeben, ohne diese Komponente spezifisch zu rendern.
  • Im export default {} :
    • Die props wurden geändert, um den Datenobjekten zu entsprechen, die vom übergeordneten Element gesendet werden: App.vue . Der Grund für die Änderung der Props ist, dass App.vue selbst einen Teil der Daten, die es von der Wetter-API und anderen Online-Ressourcen erhält, basierend auf der Suchanfrage des Benutzers anzeigen und den Rest der Daten weitergeben wird. In dem zuvor geschriebenen Dummy-Code hat App.vue den gesamten Dummy-Datendump diskriminierungsfrei weitergegeben, und die Props von Content.vue wurden entsprechend eingerichtet.
    • Die Funktion data() gibt jetzt nichts zurück, da wir in dieser Komponente keine Datenmanipulation vornehmen.

TempVarChart.vue

Diese Komponente soll detaillierte Temperaturprognosen für den Rest des aktuellen Tages erhalten und schließlich mit FusionCharts anzeigen. Aber vorerst werden wir sie nur als Text auf der Webseite anzeigen.

 <template> <div> {{ tempVar.tempToday }} </div> </template> <script> export default { props: ["tempVar"], components: {}, data() { return { }; }, methods: { }, }; </script> <style> </style>

Höhepunkte.vue

 <template> <div> <uv-index :highlights="highlights"></uv-index> <visibility :highlights="highlights"></visibility> <wind-status :highlights="highlights"></wind-status> </div> </template> <script> import UVIndex from './UVIndex.vue'; import Visibility from './Visibility.vue'; import WindStatus from './WindStatus.vue'; export default { props: ["highlights"], components: { 'uv-index': UVIndex, 'visibility': Visibility, 'wind-status': WindStatus, }, data () { return { } }, methods: { }, computed: { }, } </script> <style> </style>

Die Änderungen gegenüber dem vorherigen Code sind:

  • In der <template> wurde der Text und die Daten innerhalb von {{ }} entfernt, da dies eine dumme Komponente ist, genau wie Content.vue , deren einzige Aufgabe es ist, die Daten unter Beibehaltung der strukturellen Hierarchie an Kinder weiterzugeben. Denken Sie daran, dass dumme Komponenten wie Highlights.vue und Content.vue existieren, um die Parität zwischen der visuellen Struktur des Dashboards und dem von uns geschriebenen Code aufrechtzuerhalten.

UVIndex.vue

Die am vorherigen Code vorgenommenen Änderungen lauten wie folgt:

  • In <template> und <style> wurde die div id in uvIndex geändert, was besser lesbar ist.
  • Im export default {} liefert die data() Funktion nun ein String-Objekt uvIndex , dessen Wert aus dem von der Komponente empfangenen Highlights-Objekt mittels props extrahiert wird. Dieser uvIndex wird nun temporär verwendet, um den Wert als Text innerhalb der <template> anzuzeigen. Später werden wir diesen Wert in die Datenstruktur einfügen, die zum Rendern eines Diagramms geeignet ist.

Sichtbarkeit.vue

 <template> <div> <p>Visibility: {{ visibility }}</p> </div> </template> <script> export default { props: ["highlights"], data () { return { visibility: this.highlights.visibility.toString() } }, methods: { }, computed: { }, } </script> <style> </style>

Die einzige Änderung, die in dieser Datei (in Bezug auf den vorherigen Code) vorgenommen wurde, besteht darin, dass die Definition des visibility , das von der data() Funktion zurückgegeben wird, jetzt toString() an ihrem Ende enthält, da der vom übergeordneten Element empfangene Wert ein Gleitkommawert ist Punktnummer, die in einen String umgewandelt werden muss.

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>

Die am vorherigen Code vorgenommenen Änderungen lauten wie folgt:

  • In der gesamten Datei wurde windstatus in windStatus umbenannt, um die Lesbarkeit zu verbessern und auch mit dem Highlight-Objekt, das App.vue mit tatsächlichen Daten bereitstellt, synchron zu sein.
  • Ähnliche Namensänderungen wurden für Geschwindigkeit und Richtung vorgenommen – die neuen sind windSpeed ​​und windDirection .
  • Ein neues Objekt derivedWindDirection ist ins Spiel gekommen (ebenfalls von App.vue im Highlights-Bundle bereitgestellt).

Vorerst werden die empfangenen Daten als Text gerendert; später wird es in die für die Visualisierung notwendige Datenstruktur eingesteckt.

Testen mit Dummy-Daten

Das wiederholte Zugreifen auf Dummy-Daten mag für Sie etwas frustrierend sein, aber es gibt einige gute Gründe dafür:

  • Wir haben viele Änderungen am Code jeder Komponente vorgenommen, und es ist eine gute Idee zu testen, ob diese Änderungen den Code brechen. Mit anderen Worten, wir sollten überprüfen, ob der Datenfluss intakt ist, jetzt, wo wir zu komplexeren Teilen des Projekts übergehen.
  • Die echten Daten aus der Online-Wetter-API müssen viel bearbeitet werden, und es kann für Sie überwältigend sein, zwischen dem Code für die Datenerfassung und -verarbeitung und dem Code für den reibungslosen Datenfluss durch die Komponenten zu jonglieren. Die Idee ist, das Quantum an Komplexität unter Kontrolle zu halten, damit wir die Fehler besser verstehen, denen wir begegnen könnten.

In diesem Abschnitt codieren wir im Wesentlichen einige JSON-Daten in App.vue , die offensichtlich in naher Zukunft durch Live-Daten ersetzt werden. Es gibt viele Ähnlichkeiten zwischen der Dummy-JSON-Struktur und der JSON-Struktur, die wir für die tatsächlichen Daten verwenden werden. Es gibt Ihnen also auch eine ungefähre Vorstellung davon, was Sie von den realen Daten erwarten können, sobald wir darauf stoßen.

Wir geben jedoch zu, dass dies alles andere als der ideale Ansatz ist, den man verfolgen könnte, wenn man ein solches Projekt von Grund auf neu erstellt. In der realen Welt beginnen Sie oft mit der realen Datenquelle, spielen ein wenig damit herum, um zu verstehen, was getan werden kann und sollte, um sie zu zähmen, und denken dann über die geeignete JSON-Datenstruktur nach, um die relevanten Informationen zu erfassen. Wir haben Sie absichtlich von all dieser schmutzigen Arbeit abgeschirmt, da Sie dadurch weiter vom Ziel entfernt sind – zu lernen, wie man Vue.js und FusionCharts verwendet, um ein Dashboard zu erstellen.

Lassen Sie uns nun in den neuen Code für App.vue springen:

 <template> <div> <dashboard-content :highlights="highlights" :tempVar="tempVar"></dashboard-content> </div> </template> <script> import Content from './components/Content.vue' export default { name: 'app', components: { 'dashboard-content': Content }, data () { return { tempVar: { tempToday: [ {hour: '11.00 AM', temp: '35'}, {hour: '12.00 PM', temp: '36'}, {hour: '1.00 PM', temp: '37'}, {hour: '2.00 PM', temp: '38'}, {hour: '3.00 PM', temp: '36'}, {hour: '4.00 PM', temp: '35'}, ], }, highlights: { uvIndex: 4, visibility: 10, windStatus: { windSpeed: '30 km/h', windDirection: '30', derivedWindDirection: 'NNE', }, }, } }, methods: { }, computed: { }, } </script> <style> </style>

Die Änderungen, die am Code gegenüber der vorherigen Version vorgenommen wurden, lauten wie folgt:

  • Der Name der untergeordneten Komponente wurde in Dashboard-Content geändert, und dementsprechend wurde das benutzerdefinierte HTML-Element in der <template> überarbeitet. Beachten Sie, dass wir jetzt zwei Attribute haben – highlights und tempVar – anstelle eines einzigen Attributs, das wir zuvor mit dem benutzerdefinierten Element verwendet haben. Dementsprechend haben sich auch die diesen Attributen zugeordneten Daten geändert. Interessant ist hier, dass wir die v-bind: Direktive oder ihre Kurzform : (wie wir es hier getan haben) mit mehreren Attributen eines benutzerdefinierten HTML-Elements verwenden können!
  • Die Funktion data() gibt jetzt das filename -Objekt (das früher existierte) zurück, zusammen mit zwei neuen Objekten (anstelle der alten weather_data ): tempVar und highlights . Die Struktur des json ist für den Code geeignet, den wir in die untergeordneten Komponenten geschrieben haben, damit sie die benötigten Datenteile aus den Dumps extrahieren können. Die Strukturen sind ziemlich selbsterklärend, und Sie können erwarten, dass sie ziemlich ähnlich sind, wenn wir es mit Live-Daten zu tun haben. Die wesentliche Änderung, auf die Sie stoßen werden, ist jedoch das Fehlen von Hardcoding (offensichtlich, nicht wahr) – wir lassen die Werte als Standardzustand leer und schreiben Code, um sie basierend auf den Werten, die wir von erhalten, dynamisch zu aktualisieren Wetter-API.

Sie haben in diesem Abschnitt viel Code geschrieben, ohne die eigentliche Ausgabe zu sehen. Bevor Sie fortfahren, werfen Sie einen Blick auf den Browser (starten Sie den Server ggf. mit npm run dev neu) und sonnen Sie sich in der Herrlichkeit Ihrer Errungenschaft. Die Webseite, die Sie an dieser Stelle sehen sollten, sieht wie das folgende Bild aus:

Ergebnis des im Browser angezeigten Codes
(Große Vorschau)

Code für die Datenerfassung und -verarbeitung

Dieser Abschnitt wird das Herzstück des Projekts sein, mit dem gesamten Code, der in App.vue für Folgendes geschrieben werden muss:

  • Standorteingabe durch den Benutzer – ein Eingabefeld und ein Call-to-Action-Button sind ausreichend;
  • Hilfsfunktionen für verschiedene Aufgaben; diese Funktionen werden später in verschiedenen Teilen des Komponentencodes aufgerufen;
  • Abrufen detaillierter Geolokalisierungsdaten von Google Maps API für JavaScript;
  • Abrufen detaillierter Wetterdaten von der Dark Sky API;
  • Formatierung und Verarbeitung der Geolokalisierungs- und Wetterdaten, die an die untergeordneten Komponenten weitergegeben werden.

Die folgenden Unterkapitel veranschaulichen, wie wir die in den vorstehenden Punkten an uns gestellten Aufgaben umsetzen können. Mit einigen Ausnahmen werden die meisten von ihnen der Reihenfolge folgen.

Eingabe vom Benutzer

Es ist ganz offensichtlich, dass die Aktion beginnt, wenn der Benutzer den Namen des Ortes eingibt, für den die Wetterdaten angezeigt werden sollen. Dazu müssen wir Folgendes implementieren:

  • Ein Eingabefeld zur Eingabe des Ortes;
  • Eine Senden-Schaltfläche, die unserer Anwendung mitteilt, dass der Benutzer den Ort eingegeben hat und es an der Zeit ist, den Rest zu erledigen. Wir werden auch das Verhalten implementieren, wenn die Verarbeitung beim Drücken der Eingabetaste beginnt.

Der unten gezeigte Code ist auf den HTML-Vorlagenteil von App.vue . Wir erwähnen nur den Namen der mit den Klickereignissen verknüpften Methode und definieren sie später im Methodenobjekt von <script> in App.vue.

 <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>

Das obige Snippet an der richtigen Stelle zu platzieren ist trivial – das überlassen wir Ihnen. Die interessanten Teile des Snippets sind jedoch:

  • @keyup.enter="organizeAllDetails"
  • @click="organizeAllDetails"

Wie Sie aus den vorherigen Abschnitten wissen, ist @ die Abkürzung von Vue für die Direktive v-on :, die mit einem Ereignis verknüpft ist. Das Neue ist „ organizeAllDetails “ – es ist nichts anderes als die Methode, die ausgelöst wird, sobald die Ereignisse (Drücken der Eingabetaste oder Klicken auf die Schaltfläche) eintreten. Wir müssen noch die Methode definieren, und das Puzzle wird am Ende dieses Abschnitts vollständig sein.

Textinformationsanzeige gesteuert von App.vue

Sobald die Benutzereingabe die Aktion auslöst und viele Daten von den APIs erfasst werden, stoßen wir auf die unvermeidliche Frage – „Was tun mit all diesen Daten?“. Offensichtlich ist etwas Datenmassierung erforderlich, aber das beantwortet unsere Frage nicht vollständig! Wir müssen entscheiden, was die Endverwendung der Daten ist, oder direkter, welche Entitäten sind die verschiedenen Teile der erfassten und verarbeiteten Daten?

Die untergeordneten Komponenten von App.vue sind, basierend auf ihrer Hierarchie und ihrem Zweck, die ersten Konkurrenten für den Großteil der Daten. Wir werden jedoch auch einige Daten haben, die zu keiner dieser untergeordneten Komponenten gehören, aber dennoch sehr informativ sind und das Dashboard vervollständigen. Wir können sie gut gebrauchen, wenn wir sie als Textinformationen anzeigen, die direkt von App.vue gesteuert werden, während die restlichen Daten an das Kind weitergegeben werden, um letztendlich als hübsche Diagramme angezeigt zu werden.

Konzentrieren wir uns vor diesem Hintergrund auf den Code zum Festlegen der Phase der Verwendung von Textdaten. Es ist an dieser Stelle ein einfaches HTML-Template, auf dem die Daten schließlich kommen und sitzen werden.

 <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>

Im obigen Snippet sollten Sie Folgendes verstehen:

  • Das Zeug in {{ }} — sie sind Vues Art, dynamische Daten in die HTML-Vorlage einzufügen, bevor sie im Browser gerendert werden. Sie sind ihnen schon einmal begegnet, und es gibt nichts Neues oder Überraschendes. Denken Sie nur daran, dass diese Datenobjekte aus der Methode data() im Objekt export default() von App.vue . Sie haben Standardwerte, die wir gemäß unseren Anforderungen festlegen und dann bestimmte Methoden schreiben, um die Objekte mit echten API-Daten zu füllen.

Machen Sie sich keine Sorgen, wenn Sie die Änderungen nicht im Browser sehen – die Daten sind noch nicht definiert, und es ist für Vue selbstverständlich, Dinge nicht zu rendern, die es nicht kennt. Sobald die Daten jedoch festgelegt sind (und vorerst können Sie die Daten sogar durch Hartcodierung überprüfen), werden die Textdaten von App.vue gesteuert.

Die data() Methode

Die data() -Methode ist ein spezielles Konstrukt in den .vue Dateien – sie enthält und gibt Datenobjekte zurück, die für die Anwendung so wichtig sind. Erinnern Sie sich an die generische Struktur des <script> -Teils in jeder .vue -Datei – sie enthält ungefähr Folgendes:

 <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>

Bisher sind Sie auf die Namen einiger Datenobjekte gestoßen, aber es gibt noch viel mehr. Die meisten von ihnen sind für die untergeordneten Komponenten relevant, von denen jede einen anderen Aspekt des Wetterinformationsdumps verarbeitet. Unten ist die gesamte data() -Methode aufgeführt, die wir für dieses Projekt benötigen – Sie werden eine ungefähre Vorstellung davon haben, welche Daten wir von den APIs erwarten und wie wir die Daten basierend auf der Nomenklatur der Objekte verbreiten.

 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: '' }, } }; },

Wie Sie sehen können, ist der Standardwert in den meisten Fällen leer, da dies an dieser Stelle ausreicht. Es werden Methoden geschrieben, um die Daten zu manipulieren und mit geeigneten Werten zu füllen, bevor sie gerendert oder an die untergeordneten Komponenten weitergegeben werden.

Methoden in App.vue

Bei .vue Dateien werden die Methoden im Allgemeinen als Werte von Schlüsseln geschrieben, die im methods { } verschachtelt sind. Ihre Hauptaufgabe besteht darin, die Datenobjekte der Komponente zu manipulieren. Wir werden die Methoden in App.vue schreiben und dabei dieselbe Philosophie im Hinterkopf behalten. Basierend auf ihrem Zweck können wir die Methoden von App.vue wie folgt kategorisieren:

  • Utility-Methoden
  • Aktions-/ereignisorientierte Methoden
  • Methoden der Datenerfassung
  • Methoden der Datenverarbeitung
  • Klebeverfahren auf höchstem Niveau

Es ist wichtig, dass Sie das verstehen – wir präsentieren Ihnen die Methoden auf einer Platte, weil wir bereits herausgefunden haben, wie die APIs funktionieren, welche Daten sie liefern und wie wir die Daten in unserem Projekt verwenden sollten. Es ist nicht so, dass wir die Methoden aus dem Nichts gezogen und irgendeinen obskuren Code geschrieben hätten, um mit den Daten umzugehen. Zum Zwecke des Lernens ist es eine gute Übung, den Code für die Methoden und Daten sorgfältig zu lesen und zu verstehen. Wenn Sie jedoch mit einem neuen Projekt konfrontiert sind, das Sie von Grund auf neu erstellen müssen, müssen Sie die ganze Drecksarbeit selbst erledigen, und das bedeutet, viel mit den APIs zu experimentieren – ihrem programmatischen Zugriff und ihrer Datenstruktur, bevor Sie sie nahtlos mit den Daten verkleben Struktur, die Ihr Projekt erfordert. Sie werden keine Händchen halten müssen und es wird frustrierende Momente geben, aber das gehört alles dazu, als Entwickler zu reifen.

In den folgenden Unterabschnitten erläutern wir jeden der Methodentypen und zeigen auch die Implementierung der Methoden, die zu dieser Kategorie gehören. Die Methodennamen sind in Bezug auf ihren Zweck ziemlich selbsterklärend, ebenso wie ihre Implementierung, von der wir glauben, dass Sie sie leicht nachvollziehen können. Erinnern Sie sich jedoch vorher an das allgemeine Schema der Schreibmethoden in .vue Dateien:

 <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>

Utility-Methoden

Die Utility-Methoden sind, wie der Name schon sagt, Methoden, die hauptsächlich zum Zwecke der Modularisierung von sich wiederholendem Code geschrieben wurden, der für Randaufgaben verwendet wird. Sie werden bei Bedarf von anderen Methoden aufgerufen. Im Folgenden sind die Dienstprogrammmethoden für 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); },
 // To format the wind direction based on the angle 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; },

Obwohl wir es nicht implementiert haben, können Sie die Dienstprogrammmethoden aus der .vue -Datei herausnehmen und in eine separate JavaScript-Datei einfügen. Alles, was Sie tun müssen, ist, die .js -Datei am Anfang des Skriptteils in die .vue -Datei zu importieren, und Sie sollten loslegen können. Ein solcher Ansatz funktioniert wirklich gut und hält den Code sauber, insbesondere in großen Anwendungen, in denen Sie möglicherweise viele Methoden verwenden, die je nach Zweck besser gruppiert werden. Sie können diesen Ansatz auf alle in diesem Artikel aufgeführten Methodengruppen anwenden und den Effekt selbst sehen. Wir empfehlen Ihnen jedoch, diese Übung zu machen, nachdem Sie den hier vorgestellten Kurs absolviert haben, damit Sie das Gesamtbild aller Teile verstehen, die vollständig synchron arbeiten, und auch eine funktionierende Software haben, auf die Sie sich einmal beziehen können Pausen beim Experimentieren.

Aktions-/ereignisorientierte Methoden

Diese Methoden werden im Allgemeinen ausgeführt, wenn wir eine Aktion ausführen müssen, die einem Ereignis entspricht. Je nach Fall kann das Ereignis durch eine Benutzerinteraktion oder programmgesteuert ausgelöst werden. In der App.vue -Datei befinden sich diese Methoden unterhalb der Utility-Methoden.

 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(); },

Eine interessante Sache in einigen der obigen Codeschnipsel ist die Verwendung von $ref . Einfach ausgedrückt ist es die Art und Weise von Vue, die Code-Anweisung, die sie enthält, mit dem HTML-Konstrukt zu verknüpfen, auf das sie sich auswirken soll (für weitere Informationen lesen Sie die offizielle Anleitung). Beispielsweise wirken sich die Methoden makeInputEmpty() und detectEnterKeyPress() auf das Eingabefeld aus, da wir im HTML des Eingabefelds den Wert des Attributs ref als input erwähnt haben.

Datenerfassungsmethoden

Wir verwenden die folgenden zwei APIs in unserem Projekt:

  • Google Maps Geocoder-API
    Diese API dient zum Abrufen der Koordinaten des Standorts, den der Benutzer sucht. Sie benötigen selbst einen API-Schlüssel, den Sie erhalten, indem Sie der Dokumentation im angegebenen Link folgen. Im Moment können Sie den von FusionCharts verwendeten API-Schlüssel verwenden, aber wir bitten Sie, ihn nicht zu missbrauchen und einen eigenen Schlüssel zu erwerben. Wir verweisen auf die JavaScript-API aus der index.html dieses Projekts und verwenden die von ihr bereitgestellten Konstruktoren für unseren Code in der App.vue -Datei.
  • Die Dark-Sky-Weather-API
    Diese API dient zum Abrufen der den Koordinaten entsprechenden Wetterdaten. Wir werden es jedoch nicht direkt verwenden; Wir werden es in eine URL einschließen, die über einen der Server von FusionCharts umgeleitet wird. Der Grund dafür ist, dass das Senden einer GET-Anfrage an die API von einer vollständig clientseitigen Anwendung wie unserer zu dem frustrierenden CORS -Fehler führt (weitere Informationen hier und hier).

Wichtiger Hinweis : Da wir Google Maps- und Dark Sky-APIs verwendet haben, haben beide APIs ihre eigenen API-Schlüssel, die wir Ihnen in diesem Artikel mitgeteilt haben. Dies hilft Ihnen, sich auf clientseitige Entwicklungen zu konzentrieren, anstatt sich mit der Backend-Implementierung zu beschäftigen. Wir empfehlen Ihnen jedoch, Ihre eigenen Schlüssel zu erstellen , da unsere APIs-Schlüssel Beschränkungen unterliegen und Sie die Anwendung nicht selbst ausprobieren können, wenn diese Beschränkungen überschritten werden.

Gehen Sie für Google Maps zu diesem Artikel, um Ihren API-Schlüssel zu erhalten. Besuchen Sie für die Dark Sky-API https://darksky.net/dev, um Ihren API-Schlüssel und die entsprechenden Endpunkte zu erstellen.

Sehen wir uns vor dem Hintergrund des Kontexts die Implementierung der Datenerfassungsmethoden für unser Projekt an.

 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.

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); } },
 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 ); },

Klebemethoden auf hohem Niveau

Nachdem wir die Dienstprogramm-, Erfassungs- und Verarbeitungsmethoden aus dem Weg geräumt haben, bleibt uns nun die Aufgabe, das Ganze zu orchestrieren. Dazu erstellen wir High-Level-Glue-Methoden, die im Wesentlichen die oben beschriebenen Methoden in einer bestimmten Reihenfolge aufrufen, sodass die gesamte Operation nahtlos ausgeführt wird.

 // 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(); },

montiert

Vue bietet Instanzlebenszyklus-Hooks – Eigenschaften, die im Wesentlichen Methoden sind und ausgelöst werden, wenn der Instanzlebenszyklus diese Phase erreicht. Beispielsweise sind „created“, „mounted“, „beforUpdate“ usw. sehr nützliche Lebenszyklus-Hooks, die es dem Programmierer ermöglichen, die Instanz auf einer viel granulareren Ebene zu steuern, als dies sonst möglich gewesen wäre.

Im Code einer Vue-Komponente werden diese Lifecycle-Hooks genauso implementiert wie für jede andere prop . Zum Beispiel:

 <template> </template> <script> // import statements export default { data() { return { // data objects here } }, methods: { // methods here }, mounted: function(){ // function body here }, } </script> <style> </style>

Bewaffnet mit diesem neuen Verständnis, werfen Sie einen Blick auf den folgenden Code für die mounted Requisite von App.vue :

 mounted: async function() { this.location = "New York"; await this.organizeAllDetails(); }

Vollständiger Code für App.vue

Wir haben in diesem Abschnitt viel Boden behandelt, und die letzten paar Abschnitte haben Ihnen die Dinge in Stücken und Stücken gegeben. Es ist jedoch wichtig, dass Sie über den vollständigen, zusammengestellten Code für App.vue (vorbehaltlich weiterer Änderungen in den nachfolgenden Abschnitten). Hier kommt's:

 <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>

Und schließlich, nach so viel Geduld und harter Arbeit, können Sie den Datenfluss mit seiner rohen Kraft sehen! Besuchen Sie die Anwendung im Browser, aktualisieren Sie die Seite, suchen Sie im Suchfeld der Anwendung nach einem Ort und drücken Sie die Eingabetaste !

Die Anwendung, wie sie im Browser angezeigt wird
(Große Vorschau)

Jetzt, da wir mit all dem schweren Heben fertig sind, machen Sie eine Pause. Die folgenden Abschnitte konzentrieren sich darauf, die Daten zu verwenden, um schöne und informative Diagramme zu erstellen, gefolgt davon, unserer hässlich aussehenden Anwendung eine wohlverdiente Grooming-Sitzung mit CSS zu geben.

5. Datenvisualisierung mit FusionCharts

Grundlegende Überlegungen zu Diagrammen

Für den Endbenutzer ist die Essenz eines Dashboards im Wesentlichen dies: eine Sammlung gefilterter und sorgfältig kuratierter Informationen zu einem bestimmten Thema, die durch visuelle/grafische Instrumente zur schnellen Aufnahme vermittelt werden. Sie kümmern sich nicht um die Feinheiten Ihres Datenpipeline-Engineerings oder wie ästhetisch Ihr Code ist – alles, was sie wollen, ist eine allgemeine Ansicht in 3 Sekunden. Daher bedeutet ihnen unsere grobe Anwendung, die Textdaten anzeigt, nichts, und es ist höchste Zeit, dass wir Mechanismen implementieren, um die Daten mit Diagrammen zu verpacken.

Bevor wir jedoch tief in die Implementierung von Diagrammen eintauchen, lassen Sie uns einige relevante Fragen und die möglichen Antworten aus unserer Sicht betrachten:

  • Welche Art von Diagrammen ist für die Art von Daten geeignet, mit denen wir es zu tun haben?
    Nun, die Antwort hat zwei Aspekte – den Kontext und den Zweck. Mit Kontext meinen wir die Art der Daten, und sie passen insgesamt in das Schema größerer Dinge, begrenzt durch den Umfang und die Zielgruppe des Projekts. Und mit Absicht meinen wir im Wesentlichen „Worauf wollen wir betonen?“. Beispielsweise können wir die heutige Temperatur zu verschiedenen Tageszeiten darstellen, indem wir ein Säulendiagramm verwenden (vertikale Säulen gleicher Breite, deren Höhe proportional zum Wert ist, den die Säule darstellt). Uns interessieren jedoch selten die einzelnen Werte, sondern die Gesamtvariation und der Trend in den Daten. Um dem Zweck gerecht zu werden, ist es in unserem besten Interesse, ein Liniendiagramm zu verwenden, und wir werden dies in Kürze tun.
  • Was sollte vor der Auswahl einer Diagrammbibliothek beachtet werden?
    Da wir ein Projekt hauptsächlich mit JavaScript-basierten Technologien durchführen, ist es selbstverständlich, dass jede Diagrammbibliothek, die wir für unser Projekt auswählen, aus der JavaScript-Welt stammen sollte. Unter Berücksichtigung dieser grundlegenden Prämisse sollten wir Folgendes berücksichtigen, bevor wir uns auf eine bestimmte Bibliothek beschränken:
    • Unterstützung für die Frameworks unserer Wahl , in diesem Fall Vue.js. Ein Projekt kann in anderen gängigen JavaScript-Frameworks wie React oder Angular entwickelt werden – überprüfen Sie die Unterstützung der Diagrammbibliothek für Ihr bevorzugtes Framework. Auch die Unterstützung anderer gängiger Programmiersprachen wie Python, Java, C++, .Net (AS und VB), insbesondere wenn das Projekt ernsthafte Backend-Sachen beinhaltet, muss in Betracht gezogen werden.
    • Verfügbarkeit von Diagrammtypen und Funktionen , da es fast unmöglich ist, im Voraus zu wissen, was die endgültige Form und der Zweck der Daten im Projekt sein werden (insbesondere wenn die Anforderungen von Ihren Kunden in einem professionellen Umfeld geregelt werden). In diesem Fall sollten Sie Ihr Netz weit auswerfen und eine Diagrammbibliothek auswählen, die über die größte Sammlung von Diagrammen verfügt. Um Ihr Projekt von anderen abzuheben, sollte die Bibliothek vor allem über genügend Funktionen in Form von konfigurierbaren Diagrammattributen verfügen, damit Sie die meisten Aspekte der Diagramme und die richtige Granularität feinabstimmen und anpassen können. Außerdem sollten die standardmäßigen Diagrammkonfigurationen sinnvoll sein und die Bibliotheksdokumentation muss erstklassig sein, aus Gründen, die für professionelle Entwickler offensichtlich sind.
    • Lernkurve, Support-Community und Ausgewogenheit müssen ebenfalls berücksichtigt werden, insbesondere wenn Sie neu in der Datenvisualisierung sind. An einem Ende des Spektrums haben Sie proprietäre Tools wie Tableau und Qlickview, die eine Bombe kosten, eine reibungslose Lernkurve haben, aber auch mit so vielen Einschränkungen in Bezug auf Anpassbarkeit, Integration und Bereitstellung einhergehen. Auf der anderen Seite gibt es d3.js – umfangreich, kostenlos (Open Source) und bis ins Mark anpassbar, aber Sie müssen den Preis einer sehr steilen Lernkurve zahlen, um mit der Bibliothek etwas Produktives tun zu können.

Was Sie brauchen, ist der optimale Punkt – das richtige Gleichgewicht zwischen Produktivität, Abdeckung, Anpassbarkeit, Lernkurve und natürlich Kosten. Wir empfehlen Ihnen, einen Blick auf FusionCharts zu werfen – die weltweit umfassendste und unternehmenstauglichste JavaScript-Diagrammbibliothek für das Web und Mobilgeräte, die wir in diesem Projekt zum Erstellen von Diagrammen verwenden werden.

Einführung in FusionCharts

FusionCharts wird weltweit von Millionen von Entwicklern in Hunderten von Ländern rund um den Globus als Go-to-JavaScript-Diagrammbibliothek verwendet. Technisch gesehen ist es so geladen und konfigurierbar wie es nur sein kann, mit Unterstützung für die Integration in fast jeden gängigen Tech-Stack, der für webbasierte Projekte verwendet wird. Die kommerzielle Nutzung von FusionCharts erfordert eine Lizenz, und Sie müssen für die Lizenz je nach Anwendungsfall bezahlen (bitte wenden Sie sich an den Vertrieb, wenn Sie neugierig sind). Wir verwenden FusionCharts in diesen Projekten jedoch nur, um ein paar Dinge auszuprobieren, und daher die nicht lizenzierte Version (kommt mit einem kleinen Wasserzeichen in Ihren Diagrammen und einigen anderen Einschränkungen). Die Verwendung der nicht lizenzierten Version ist völlig in Ordnung, wenn Sie die Diagramme ausprobieren und sie in Ihren nicht-kommerziellen oder persönlichen Projekten verwenden. Wenn Sie die Anwendung kommerziell bereitstellen möchten, stellen Sie bitte sicher, dass Sie über eine Lizenz von FusionCharts verfügen.

Da dies ein Projekt mit Vue.js ist, benötigen wir zwei Module, die installiert werden müssen, falls dies nicht früher geschehen ist:

  • Das fusioncharts -Modul, weil es alles enthält, was Sie zum Erstellen der Diagramme benötigen
  • Das Modul vue-fusioncharts , das im Wesentlichen ein Wrapper für Fusioncharts ist, sodass es in einem Vue.js-Projekt verwendet werden kann

Wenn Sie sie nicht zuvor installiert haben (wie im dritten Abschnitt beschrieben), installieren Sie sie, indem Sie den folgenden Befehl im Stammverzeichnis des Projekts ausführen:

 npm install fusioncharts vue-fusioncharts --save

Stellen Sie als Nächstes sicher, dass die Datei src/main.js des Projekts den folgenden Code enthält (auch in Abschnitt 3 erwähnt):

 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) })

Die vielleicht kritischste Zeile im obigen Ausschnitt ist die folgende:

 Vue.use(VueFusionCharts, FusionCharts)

Es weist Vue an, das vue-fusioncharts-Modul zu verwenden, um vielen Dingen im Projekt einen Sinn zu geben, die anscheinend nicht explizit von uns definiert sind, aber im Modul selbst definiert sind. Außerdem impliziert diese Art von Anweisung eine globale Deklaration, d. h. überall dort, wo Vue auf etwas Seltsames im Code unseres Projekts stößt (Dinge, die wir nicht explizit über die Verwendung von FusionCharts definiert haben), wird es mindestens einmal in den vue-fusioncharts nachsehen und Fusioncharts-Knotenmodule für ihre Definitionen, bevor Fehler ausgegeben werden. Wenn wir FusionCharts in einem isolierten Teil unseres Projekts verwendet hätten (und es nicht in fast allen Komponentendateien verwendet hätten), wäre die lokale Deklaration vielleicht sinnvoller gewesen.

Damit sind Sie bereit, FusionCharts im Projekt zu verwenden. Wir werden eine ganze Reihe verschiedener Diagramme verwenden, wobei die Auswahl vom Aspekt der Wetterdaten abhängt, die wir visualisieren möchten. Außerdem werden wir das Zusammenspiel von Datenbindung, benutzerdefinierten Komponenten und Beobachtern in Aktion sehen.

Allgemeines Schema zur Verwendung von Fusioncharts in .vue Dateien

In diesem Abschnitt erläutern wir die allgemeine Idee der Verwendung von FusionCharts zum Erstellen verschiedener Diagramme in den .vue Dateien. Aber zuerst sehen wir uns den Pseudocode an, der die Kernidee schematisch darstellt.

 <template> <div> <fusioncharts :attribute_1="data_object_1" :attribute_2="data_object_2" … … ... > </fusioncharts> </div> </template> <script> export default { props: ["data_prop_received_by_the_component"], components: {}, data() { return { data_object_1: "value_1", data_object_2: "value_2", … … }; }, methods: {}, computed: {}, watch: { data_prop_received_by_the_component: { handler: function() { // some code/logic, mainly data manipulation based }, deep: true } } }; </script> <style> // component specific special CSS code here </style>

Lassen Sie uns verschiedene Teile des obigen Pseudocodes verstehen:

  • In der <template> , innerhalb der obersten Ebene <div> (das ist ziemlich obligatorisch für den Template-HTML-Code jeder Komponente), haben wir die benutzerdefinierte Komponente <fusioncharts> . Wir haben die Definition der Komponente, die im vue-fusioncharts Node-Modul enthalten ist, das wir für dieses Projekt installiert haben. Intern setzt vue-fusioncharts auf das ebenfalls installierte Modul fusioncharts . Wir haben die notwendigen Module importiert und ihre Abhängigkeiten aufgelöst, Vue angewiesen, den Wrapper global (im gesamten Projekt) in der Datei src/main.js zu verwenden, und daher fehlt es nicht an Definition für die von uns verwendete benutzerdefinierte <fusioncharts> -Komponente Hier. Außerdem verfügt die benutzerdefinierte Komponente über benutzerdefinierte Attribute, und jedes der benutzerdefinierten Attribute ist an ein Datenobjekt (und damit an deren Werte) durch die v-bind Direktive gebunden, für die die Abkürzung der Doppelpunkt ( : ) ist. Wir werden mehr über die Attribute und die ihnen zugeordneten Datenobjekte erfahren, wenn wir einige der spezifischen Diagramme besprechen, die in diesem Projekt verwendet werden.
  • Im <script> deklarieren Sie zunächst die Props, die die Komponente erhalten soll, und definieren dann die Datenobjekte, die an die Attribute von <fusioncharts> sind. Die den Datenobjekten zugewiesenen Werte sind die Werte, die die Attribute von <fusioncharts> , und die Diagramme werden auf der Grundlage dieser eingezogenen Werte erstellt. Abgesehen davon ist der interessanteste Teil des Codes das Objekt watch { } . Dies ist ein ganz besonderes Objekt in Vues Schema der Dinge – es weist Vue im Wesentlichen an, alle Änderungen an bestimmten Daten zu überwachen und dann Maßnahmen zu ergreifen, die darauf basieren, wie die handler -Funktion für diese Daten definiert wurde. Zum Beispiel möchten wir, dass Vue die empfangene prop überwacht, dh data_prop_received_by_the_component im Pseudocode. Die prop wird zu einem Schlüssel im watch { } -Objekt, und der Wert des Schlüssels ist ein weiteres Objekt – eine Handler-Methode, die beschreibt, was getan werden muss, wenn sich die prop ändert. Mit solch eleganten Mechanismen zur Handhabung der Änderungen behält die App ihre Reaktivität. deep: true stellt ein boolesches Flag dar, das Sie Beobachtern zuordnen können, sodass das beobachtete Objekt ziemlich genau beobachtet wird, dh sogar die Änderungen, die in den verschachtelten Ebenen des Objekts vorgenommen werden, werden verfolgt.
    ( Weitere Informationen zu Beobachtern finden Sie in der offiziellen Dokumentation ).

Nachdem Sie nun mit einem Verständnis des allgemeinen Schemas der Dinge ausgestattet sind, lassen Sie uns in die spezifischen Implementierungen der Diagramme in den .vue Komponentendateien eintauchen. Der Code wird ziemlich selbsterklärend sein, und Sie sollten versuchen zu verstehen, wie die Besonderheiten in das allgemeine Schema der oben beschriebenen Dinge passen.

Implementierung von Diagrammen in .vue-Dateien

Während die Besonderheiten der Implementierung von einem Diagramm zum anderen variieren, gilt die folgende Erklärung für alle:

  • <template>
    Wie bereits erläutert, verfügt die benutzerdefinierte Komponente <fusioncharts> über mehrere Attribute, von denen jedes mit der v-bind : -Direktive an das entsprechende Datenobjekt gebunden ist, das in der data() -Funktion definiert ist. Die Bedeutung der Attributnamen ist ziemlich selbsterklärend, und auch das Herausfinden der entsprechenden Datenobjekte ist trivial.
  • <script>
    In der Funktion data() sorgen die Datenobjekte und ihre Werte dafür, dass die Diagramme funktionieren, da die Bindung durch die v-bind ( : ) -Direktiven erfolgt, die für die Attribute von <fusioncharts> verwendet werden. Bevor wir tief in die einzelnen Datenobjekte eintauchen, seien einige allgemeine Merkmale erwähnt:
    • Die Datenobjekte, deren Werte entweder 0 oder 1 sind, sind boolescher Natur, wobei 0 für etwas steht, das nicht verfügbar/ausgeschaltet ist, und 1 für Verfügbarkeit/eingeschalteten Zustand steht. Seien Sie jedoch vorsichtig, dass nicht-boolesche Datenobjekte neben anderen möglichen Werten auch 0 oder 1 als Wert haben können – dies hängt vom Kontext ab. Beispielsweise ist containerbackgroundopacity mit seinem Standardwert 0 ein boolescher Wert, während lowerLimit mit seinem Standardwert 0 einfach bedeutet, dass die Zahl Null ihr Literalwert ist.
    • Einige Datenobjekte befassen sich mit CSS-Eigenschaften wie Rand, Polsterung, Schriftgröße usw. – der Wert hat eine implizierte Einheit von „px“ oder Pixel. In ähnlicher Weise können andere Datenobjekte implizite Einheiten haben, die ihren Werten zugeordnet sind. Ausführliche Informationen finden Sie auf der entsprechenden Diagrammattributseite des FusionCharts Dev Center.
  • Das vielleicht interessanteste und nicht offensichtlichste Objekt in der data() -Funktion ist die dataSource. In diesem Objekt sind drei Hauptobjekte verschachtelt:
    • chart : Dieses Objekt kapselt viele Diagrammattribute, die sich auf die Konfiguration und Optik des Diagramms beziehen. Es ist fast ein obligatorisches Konstrukt, das Sie in allen Diagrammen finden werden, die Sie für dieses Projekt erstellen werden.
    • Farbbereich : Dieses Objekt ist etwas spezifisch für das betrachtete Diagramm und ist hauptsächlich in Diagrammen vorhanden, die sich mit mehreren Farben / Schattierungen befassen, um verschiedene Unterbereiche der im Diagramm verwendeten Skala abzugrenzen.
    • Wert: Dieses Objekt ist wiederum in Diagrammen vorhanden, die einen bestimmten Wert haben, der im Bereich der Skala hervorgehoben werden muss.
  • Das watch { } ist vielleicht das Wichtigste, das dieses Diagramm und die anderen in diesem Projekt verwendeten Diagramme zum Leben erweckt. Die Reaktivität der Diagramme, dh die Aktualisierung der Diagramme auf Basis der neuen Werte, die sich aus einer neuen Benutzeranfrage ergeben, wird durch die in diesem Objekt definierten Beobachter gesteuert. Zum Beispiel haben wir einen Beobachter für die von der Komponente empfangenen Requisiten- highlights definiert und dann eine Handler-Funktion definiert, um Vue über die erforderlichen Aktionen zu informieren, die es ausführen soll, wenn sich etwas an dem Objekt ändert, das im gesamten Projekt beobachtet wird. Das bedeutet, dass immer dann, wenn App.vue einen neuen Wert für eines der Objekte in highlights liefert, die Informationen bis zu dieser Komponente durchsickern und der neue Wert in den Datenobjekten dieser Komponente aktualisiert wird. Durch diesen Mechanismus wird auch das an die Werte gebundene Diagramm aktualisiert.

Die obigen Erklärungen sind ziemlich weit gefasst, um uns zu helfen, ein intuitives Verständnis des Gesamtbildes zu entwickeln. Sobald Sie die Konzepte intuitiv verstanden haben, können Sie jederzeit die Dokumentation von Vue.js und FusionCharts zu Rate ziehen, wenn Ihnen etwas aus dem Code selbst nicht klar ist. Wir überlassen die Übung Ihnen, und ab dem nächsten Unterabschnitt werden wir die Dinge, die wir in diesem Unterabschnitt behandelt haben, nicht mehr erklären.

src/components/TempVarChart.vue

Ein Diagramm, das die stündliche Temperatur zeigt
(Große Vorschau)
 <template> <div class="custom-card header-card card"> <div class="card-body pt-0"> <fusioncharts type="spline" width="100%" height="100%" dataformat="json" dataEmptyMessage="i-https://i.postimg.cc/R0QCk9vV/Rolling-0-9s-99px.gif" dataEmptyMessageImageScale=39 :datasource="tempChartData" > </fusioncharts> </div> </div> </template> <script> export default { props: ["tempVar"], components: {}, data() { return { tempChartData: { chart: { caption: "Hourly Temperature", captionFontBold: "0", captionFontColor: "#000000", captionPadding: "30", baseFont: "Roboto", chartTopMargin: "30", showHoverEffect: "1", theme: "fusion", showaxislines: "1", numberSuffix: "°C", anchorBgColor: "#6297d9", paletteColors: "#6297d9", drawCrossLine: "1", plotToolText: "$label<br><hr><b>$dataValue</b>", showAxisLines: "0", showYAxisValues: "0", anchorRadius: "4", divLineAlpha: "0", labelFontSize: "13", labelAlpha: "65", labelFontBold: "0", rotateLabels: "1", slantLabels: "1", canvasPadding: "20" }, data: [], }, }; }, methods: { setChartData: function() { var data = []; for (var i = 0; i < this.tempVar.tempToday.length; i++) { var dataObject = { label: this.tempVar.tempToday[i].hour, value: this.tempVar.tempToday[i].temp }; data.push(dataObject); } this.tempChartData.data = data; }, }, mounted: function() { this.setChartData(); }, watch: { tempVar: { handler: function() { this.setChartData(); }, deep: true }, }, }; </script> <style> </style>

src/components/UVIndex.vue

Diese Komponente enthält ein äußerst nützliches Diagramm – die Winkellehre.

UV-Index
(Große Vorschau)

Der Code für die Komponente ist unten angegeben. Ausführliche Informationen zu den Diagrammattributen von Angular Gauge finden Sie auf der FusionCharts Dev Center-Seite für Angular Gauge.

 <template> <div class="highlights-item col-md-4 col-sm-6 col-xs-12 border-top"> <div> <fusioncharts :type="type" :width="width" :height="height" :containerbackgroundopacity="containerbackgroundopacity" :dataformat="dataformat" :datasource="datasource" ></fusioncharts> </div> </div> </template> <script> export default { props: ["highlights"], components: {}, data() { return { type: "angulargauge", width: "100%", height: "100%", containerbackgroundopacity: 0, dataformat: "json", datasource: { chart: { caption: "UV Index", captionFontBold: "0", captionFontColor: "#000000", captionPadding: "30", lowerLimit: "0", upperLimit: "15", lowerLimitDisplay: "1", upperLimitDisplay: "1", showValue: "0", theme: "fusion", baseFont: "Roboto", bgAlpha: "0", canvasbgAlpha: "0", gaugeInnerRadius: "75", gaugeOuterRadius: "110", pivotRadius: "0", pivotFillAlpha: "0", valueFontSize: "20", valueFontColor: "#000000", valueFontBold: "1", tickValueDistance: "3", autoAlignTickValues: "1", majorTMAlpha: "20", chartTopMargin: "30", chartBottomMargin: "40" }, colorrange: { color: [ { minvalue: "0", maxvalue: this.highlights.uvIndex.toString(), code: "#7DA9E0" }, { minvalue: this.highlights.uvIndex.toString(), maxvalue: "15", code: "#D8EDFF" } ] }, annotations: { groups: [ { items: [ { id: "val-label", type: "text", text: this.highlights.uvIndex.toString(), fontSize: "20", font: "Source Sans Pro", fontBold: "1", fillcolor: "#212529", x: "$gaugeCenterX", y: "$gaugeCenterY" } ] } ] }, dials: { dial: [ { value: this.highlights.uvIndex.toString(), baseWidth: "0", radius: "0", borderThickness: "0", baseRadius: "0" } ] } } }; }, methods: {}, computed: {}, watch: { highlights: { handler: function() { this.datasource.colorrange.color[0].maxvalue = this.highlights.uvIndex.toString(); this.datasource.colorrange.color[1].minvalue = this.highlights.uvIndex.toString(); this.datasource.annotations.groups[0].items[0].text = this.highlights.uvIndex.toString(); }, deep: true } } }; </script>

src/components/Visibility.vue

In dieser Komponente verwenden wir ein horizontales lineares Messgerät, um die Sichtbarkeit darzustellen, wie in der folgenden Abbildung gezeigt:

Ein Screenshot des horizontalen linearen Messgeräts, das die Luftsicht darstellt (16 km)
(Große Vorschau)

Der Code für die Komponente ist unten angegeben. Weitere Informationen zu den verschiedenen Attributen dieses Diagrammtyps finden Sie auf der FusionCharts Dev Center-Seite für Horizontal Linear Gauge.

 <template> <div class="highlights-item col-md-4 col-sm-6 col-xs-12 border-left border-right border-top"> <div> <fusioncharts :type="type" :width="width" :height="height" :containerbackgroundopacity="containerbackgroundopacity" :dataformat="dataformat" :datasource="datasource" > </fusioncharts> </div> </div> </template> <script> export default { props: ["highlights"], components: {}, methods: {}, computed: {}, data() { return { type: "hlineargauge", width: "100%", height: "100%", containerbackgroundopacity: 0, dataformat: "json", creditLabel: false, datasource: { chart: { caption: "Air Visibility", captionFontBold: "0", captionFontColor: "#000000", baseFont: "Roboto", numberSuffix: " km", lowerLimit: "0", upperLimit: "40", showPointerShadow: "1", animation: "1", transposeAnimation: "1", theme: "fusion", bgAlpha: "0", canvasBgAlpha: "0", valueFontSize: "20", valueFontColor: "#000000", valueFontBold: "1", pointerBorderAlpha: "0", chartBottomMargin: "40", captionPadding: "30", chartTopMargin: "30" }, colorRange: { color: [ { minValue: "0", maxValue: "4", label: "Fog", code: "#6297d9" }, { minValue: "4", maxValue: "10", label: "Haze", code: "#7DA9E0" }, { minValue: "10", maxValue: "40", label: "Clear", code: "#D8EDFF" } ] }, pointers: { pointer: [ { value: this.highlights.visibility.toString() } ] } } }; }, watch: { highlights: { handler: function() { this.datasource.pointers.pointer[0].value = this.highlights.visibility.toString(); }, deep: true } } }; </script>

src/components/WindStatus.vue

Diese Komponente zeigt die Windgeschwindigkeit und -richtung an (Windgeschwindigkeit, wenn Sie sich mit Physik auskennen), und es ist sehr schwierig, einen Vektor mithilfe eines Diagramms darzustellen. Für solche Fälle empfehlen wir, sie mit Hilfe einiger schöner Bilder und Textwerte darzustellen. Da die Darstellung, an die wir gedacht haben, vollständig von CSS abhängig ist, werden wir sie im nächsten Abschnitt implementieren, der sich mit CSS befasst. Schauen Sie sich jedoch an, was wir zu schaffen beabsichtigen:

Windstatus: Windrichtung (links) und Windgeschwindigkeit (rechts)
(Große Vorschau)
 <template> <div class="highlights-item col-md-4 col-sm-6 col-xs-12 border-top"> <div> <div class="card-heading pt-5">Wind Status</div> <div class="row pt-4 mt-4"> <div class="col-sm-6 col-md-6 mt-2 text-center align-middle"> <p class="card-sub-heading mt-3">Wind Direction</p> <p class="mt-4"><img src="../assets/winddirection.svg" height="40" width="40"></p> <p class="card-value mt-4">{{ highlights.windStatus.derivedWindDirection }}</p> </div> <div class="col-sm-6 col-md-6 mt-2"> <p class="card-sub-heading mt-3">Wind Speed</p> <p class="mt-4"><img src="../assets/windspeed.svg" height="40" width="40"></p> <p class="card-value mt-4">{{ highlights.windStatus.windSpeed }} km/h</p> </div> </div> </div> </div> </template> <script> export default { props: ["highlights"], components: {}, data() { return {}; }, methods: {}, computed: {} }; </script>

Abschluss mit Highlights.vue

Denken Sie daran, dass wir bereits Code mit CSS für alle Komponenten implementiert haben – außer Content.vue und Highlights.vue . Da Content.vue eine dumme Komponente ist, die nur Daten weiterleitet, wurde das erforderliche minimale Styling bereits abgedeckt. Außerdem haben wir bereits entsprechenden Code für die Gestaltung der Seitenleiste und der Karten mit den Diagrammen geschrieben. Daher müssen wir nur noch einige stilistische Details zu Highlights.vue hinzufügen, was hauptsächlich die Verwendung der CSS-Klassen beinhaltet:

 <template> <div class="custom-content-card content-card card"> <div class="card-body pb-0"> <div class="content-header h4 text-center pt-2 pb-3">Highlights</div> <div class="row"> <uv-index :highlights="highlights"></uv-index> <visibility :highlights="highlights"></visibility> <wind-status :highlights="highlights"></wind-status> </div> </div> </div> </template> <script> import UVIndex from "./UVIndex.vue"; import Visibility from "./Visibility.vue"; import WindStatus from "./WindStatus.vue"; export default { props: ["highlights"], components: { "uv-index": UVIndex, "visibility": Visibility, "wind-status": WindStatus, }, }; </script>

Bereitstellung und Quellcode

Wenn die Diagramme und der Stil in Ordnung sind, sind wir fertig! Nehmen Sie sich einen Moment Zeit, um die Schönheit Ihrer Kreation zu schätzen.

Das Ergebnis
(Große Vorschau)

Jetzt ist es an der Zeit, dass Sie Ihre Anwendung bereitstellen und sie mit Ihren Kollegen teilen. Wenn Sie nicht viel Ahnung von der Bereitstellung haben und erwarten, dass wir Ihnen helfen, sehen Sie sich hier unsere Ideen zur Bereitstellung an. Der verlinkte Artikel enthält auch Vorschläge zum Entfernen des FusionCharts-Wasserzeichens links unten in jedem Diagramm.

Wenn Sie irgendwo Fehler machen und einen Bezugspunkt suchen, ist der Quellcode auf Github verfügbar.