Quasar

In der heutigen Zeit ist es üblich, dass eine App auf verschiedenen Geräten läuft. Dabei ist die einfachste, aber für den Nutzer unbequemste Möglichkeit, das Erstellen einer Web-App. Zum Aufrufen benötigt der Nutzer somit nur einen Browser. Eine für den Nutzer angenehmere Lösung ist das Erstellen von nativen Apps, die installiert werden müssen. In einem klassischen Ansatz der Multiplattform-Entwicklung muss für jede Zielplattform eine App mit eigener Codebasis geschrieben werden. Dies kann sehr ressourcenintensiv werden. Ein ressourcenschonender Ansatz ist die Verwendung von Multiplattform-Frameworks. Dabei wird eine Codebasis verwendet, um die App für verschiedene Plattformen bereitzustellen. Solch ein Framework ist Quasar (Quasar, 2021a) und wird in diesem Artikel vorgestellt. Der Artikel beginnt mit einer Einleitung in das Framework. Diese wird gefolgt von der Beschreibung der Anforderungen an die Entwicklungsumgebung. Im Anschluss wird ein Prototyp erstellt, wobei dieser in den darauffolgenden Abschnitten für die Plattformen Windows und Android gebuildet wird. Zum Schluss werden mögliche Vor- und Nachteile von Quasar vorgestellt.

Dieser Artikel ist Teil einer Reihe von Artikeln, in welchen verschiedene Frameworks für die Multiplattform-Entwicklung vorgestellt werden:

Was ist Quasar

Bei Quasar handelt es sich um ein Open Source Multiplattform-Framework unter der MIT-Lizenz. Die GUI wird dabei mit den gängigen Webtechnologien HTML, CSS und JavaScript geschrieben, wobei das Frontend-Framework VueJs (Vue, 2021a) verwendet wird. Die Entwicklung kann dabei lokal in einem Browser oder Emulator sowie über USB erfolgen. Alternativ kann auch sich auch über WLAN mit einem Endgerät verbunden werden. Um eine Quasar App zu builden werden Capacitor (Capacitor, 2021), Cordova (Cordova, 2021) und Electron (Electron, 2021) verwendet. Dabei werden Capacitor und Cordova für mobile Geräte und Electron für Desktops verwendet. Bei diesen drei Tools handelt es sich jeweils um eine Laufzeitumgebung, die als eine Art Brücke zwischen einer Webview und der Zielplattform agiert.

Abbildung 1: Sinnbildlicher Aufbau einer Quasar-App in Schichten. Die grüne Schicht soll dabei Quasar repräsentieren.

Quasar selbst besteht aus zwei grundlegenden Teilen: der CLI und der App. Bei der App handelt es sich um das Herzstück jeder App. Die CLI hingegen ist optional und ermöglicht die Verwendung von Quasar-Befehlen (Quasar, 2021b). Eine Besonderheit von Quasar ist, dass Quasar eine große Bandbreite von Komponenten unter anderem im Style des Material Designs, mitliefert. Gleichzeitig werden auch Utility-CSS-Klassen angeboten.

Anforderungen

Für die Entwicklung und das Builden auf Windows, wird die folgende Software benötigt:

Initialisierung des Prototypen

In diesem Artikel wird ein Prototyp mit den folgenden drei Seiten erstellt:

  1. Index: Eine simple Landingpage, die die Verwendung von Quasar-Komponenten und -Klassen aufzeigt.
  2. Users: Diese Seite soll Komponenten für eine Liste und einen Input beinhalten.
  3. Hilfe: Diese Seite zeigt eine simple Animation.

Der Prototyp wird mit den folgenden Befehlen erstellt:

# install quasar cli
$ npm i -g @quasar/cli
# create project
# this opens a dialog for tailoring the project to your needs
$ quasar create hello_quasar
$ cd hello_quasar
# add capacitor for mobile/android
$ quasar mode add capacitor
# add electron for desktop
$ quasar mode add electron
# start app in wlan to allow debugging via device
$ quasar dev -m capacitor -T android

Abbildung 2: Befehle zum Erstellen einer Quasar-App und dem Hinzufügen der Plattformen Windows und Android.

Nachdem diese Befehle ausgeführt wurden, wurde eine Quasar-App initialisiert und im WLAN verfügbar gemacht. Änderungen werden augenblicklich via Hot Reload auf dem Endgerät sichtbar. Jedoch ist bei dem Zugriff über WLAN zu beachten, dass die verwendete Firewall die verwendeten Ports nicht blockiert.

Erstellung Index-Seite

Die Index-Seite ist nicht weiter kompliziert: Es werden mehrere Quasar-Komponenten für Chatnachrichten verwendet. Dafür wird die bestehende Index.vue Datei wie in der folgenden Abbildung angepasst:

<template>
    <q-page padding class="q-pa-md row justify-center">
        <div style="width: 100%; max-width: 400px">
            <q-chat-message
                :text="['Hello, new explorer of quasar']"
                sent
                name="Bot"
            />
            <q-chat-message :text="['Hey, can you read me?']" sent />
            <q-chat-message bg-color="amber" name="Me">
                <q-spinner-dots size="2rem" />
            </q-chat-message>
        </div>
    </q-page>
</template>
 
<script lang="ts">
import { defineComponent } from 'vue';
 
export default defineComponent({
    name: 'PageIndex',
});
</script>

Abbildung 3: Code der Index-Seite.

Es sind mehrere Besonderheiten im Template zu finden: Im Template werden von Quasar zur Verfügung gestellte Komponenten, CSS-Klassen und Direktiven verwendet. Die Komponenten und Klassen sind dabei mit dem Präfix q- versehen (zum Beispiel q-page). Gleichzeitig sind die Komponenten sofort verfügbar und müssen nicht importiert werden. Auf die q-page wurde zusätzlich eine von Quasar mitgelieferte Direktive namens padding angewendet. Diese fügt, wie der Name sagt, der q-page ein Padding hinzu. Ansonsten wird normales Vue verwendet.

Erstellung der User-Seite

Als Nächstes wird die User-Seite sowie vorsorglich die Hilfe-Seite erstellt. Gleichzeitig werden die Komponenten der User-Seite angelegt. Dafür werden die folgenden Befehle ausgeführt:

# generate a new page
$ quasar new page Users
$ quasar new page Help
# generate a new component
$ quasar new component UserList
$ quasar new component UserInput

Abbildung 4: Befehl zum Generieren der User-Seite und der verwendeten Komponenten über die CLI.

Nach der Ausführung der Befehle sind in den entsprechenden Ordnern die erstellten Dateien zu finden. Bei den erstellten Seiten ergibt sich jedoch die Besonderheit, dass diese noch nicht über eine Route verfügbar sind. Daher werden diese im Router als Unterseiten der Root-Route registriert.

import { RouteRecordRaw } from 'vue-router';
 
const routes: RouteRecordRaw[] = [
    {
        path: '/',
        component: () => import('layouts/MainLayout.vue'),
        children: [
            // dynamic import for lazy load
            { path: '', component: () => import('pages/Index.vue') },
            { path: 'users', component: () => import('pages/Users.vue') },
            { path: 'help', component: () => import('pages/Help.vue') },
        ],
    },
 
    // Always leave this as last one,
    // but you can also remove it
    {
        path: '/:catchAll(.*)*',
        component: () => import('pages/Error404.vue'),
    },
];
 
export default routes;

Abbildung 5: Datei routes.ts, die das Routing der App beinhaltet.

Nun können die Seiten über den zugewiesenen Routen erreicht werden. Zum Beispiel kann unter /users die User-Seite aufgerufen werden. Da das Routing jetzt funktioniert, wird der Inhalt der User-Seite wie folgt angepasst:

<template>
    <q-page padding>
        <user-input @user-added="addUser"></user-input>
        <user-list :users="users"></user-list>
    </q-page>
</template>
 
<script lang="ts">
import { defineComponent, ref, defineAsyncComponent } from 'vue';
import IUser from '../models/user';
 
export default defineComponent({
    name: 'IndexPage',
    components: {
        UserInput: defineAsyncComponent(
            () => import('../components/UserInput.vue')
        ),
        UserList: defineAsyncComponent(
            () => import('../components/UserList.vue')
        ),
    },
    setup() {
        const users = ref<IUser[]>([
            { name: 'Udo', id: 0 },
            { name: 'Peter', id: 1 },
            { name: 'Hans', id: 2 },
        ]);
 
        function addUser({ name }: { name: string }) {
            users.value = [
                ...users.value,
                { name, id: Math.ceil(Math.random() * 10000000) },
            ];
        }
 
        return { users, addUser };
    },
});
</script>

Abbildung 6: Inhalt der Seite Users.

Im Script werden als Erstes die zwei zuvor erstellten Komponenten mittels dynamischer Imports für das Lazy Loading importiert und registriert. Im Anschluss wird in der Setup-Methode ein Array von Nutzern sowie eine Methode zum Hinzufügen eines Nutzers definiert. Beide werden zurückgegeben, um somit in der Komponente verwendet werden zu können. Im Template werden die beiden registrierten Komponenten verwendet. Dabei wird beim user-input auf das Event user-added gehorcht und beim Auftreten wird ein User hinzugefügt. Bei der user-list hingegen wird das Array von Usern als Property übergeben.

Die UserInput-Component

Im Template werden die Quasar-Komponenten Form, Input und Button verwendet. Der Button ist vom Typen Submit und sendet das Form ab. Beim Absenden des Forms wird eine im Script definierte Methode aufgerufen. Diese sendet das Event user-added an die Parent-Komponente und gibt dabei den Wert des Inputs als Payload mit.

<template>
    <q-form @submit="handleSubmit">
        <q-input v-model="nameInput" label="Insert name"></q-input>
        <div class="q-mt-sm">
            <q-btn color="primary" ripple label="Add" type="submit" />
        </div>
    </q-form>
</template>
 
<script lang="ts">
import { ref, defineComponent } from 'vue';
 
export default defineComponent({
    name: 'UserInput',
    emits: {
        'user-added': (payload: { name: string }) => payload.name,
    },
    setup(_, { emit }) {
        const nameInput = ref('');
 
        function handleSubmit() {
            emit('user-added', { name: nameInput.value });
            nameInput.value = '';
        }
 
        return { nameInput, handleSubmit };
    },
});
</script>

Abbildung 7: Code der Komponente für das Hinzufügen eines Users.

Die UserList-Component

Diese Komponente soll ein Array von Nutzern visuell darstellen. Dafür bekommt sie ein entsprechendes Array übergeben. Damit dies möglich ist, wird ein entsprechendes Property users im Script definiert. Dieses Property wird als benötigt definiert und gleichzeitig wird ein leeres Array als Default-Wert definiert. Im Template findet das Rendern des Arrays statt: Es wird eine Liste erstellt, wobei jedes Item beziehungsweise jeder User mit einem Bild als Avatar und einem Namen dargestellt wird.

<template>
    <q-list class="q-mt-md">
        <q-item v-for="user in users" :key="user.id" tag="li">
            <q-item-section avatar>
                <q-avatar rounded>
                    <q-img
                        src="https://source.unsplash.com/random"
                        alt="a cool avatar"
                        loading="lazy"
                    />
                </q-avatar>
            </q-item-section>
            <q-item-selection> {{ user.name }}</q-item-selection>
        </q-item>
    </q-list>
</template>
 
<script lang="ts">
import { PropertyType, defineComponent } from 'vue';
import IUser from '../../models/user';
 
export default defineComponent({
    name: 'UserInput',
    Propertys: {
        users: {
            type: Array as PropertyType<IUser[]>,
            default: [] as IUser[],
            required: true,
        },
    },
});
</script>

Abbildung 8: Code der Komponente zum Auflisten der User.

Erstellung der Hilfe-Seite

Diese Seite besteht aus einem Button sowie einer Card. Der Button toggelt einen booleschen Wert und die Card wird abhängig vom gesetzten Wert angezeigt oder nicht. Dabei wird das Anzeigen und Ausblenden der Card animiert. Hierfür wird die Transition-Komponente von Vue verwendet, indem die Card darin eingebettet wird (Vue, 2021b). Zusätzlich wird definiert, dass die Transition-Komponente eine Animation namens fade verwenden soll. Die folgende Abbildung 9 präsentiert das verwendete Template und Script.

<template>
    <q-page padding class="column items-center no-wrap">
        <q-btn
            color="secondary"
            label="Run animation"
            @click="isVisible = !isVisible"
            class="my-button q-mb-md"
        />
        <transition name="fade">
            <q-card class="help-card" v-if="isVisible">
                <img src="https://source.unsplash.com/random" class="my-img" />
                <q-card-section>
                    Lorem, ipsum dolor sit amet consectetur adipisicing elit.
                    Libero quibusdam ipsam nostrum repudiandae aperiam
                    laudantium.
                </q-card-section>
            </q-card>
        </transition>
    </q-page>
</template>
 
<script lang="ts">
import { defineComponent, ref } from 'vue';
 
export default defineComponent({
    name: 'HelpPage',
    setup() {
        const isVisible = ref(false);
        return { isVisible };
    },
});
</script>
 
<style scoped><style>

Abbildung 9: Template und Script der Hilfe-Seite.

Quasar integriert von sich aus ein Paket, welches mehrere Animationen beinhaltet (Quasar, 2021c). Wenn Animation aus diesem Paket verwendet werden sollen, müssen diese in der Konfigurationsdatei von Quasar importiert und im Anschluss der Transition-Komponente übergeben werden. Dieser Ansatz hat bei der Verwendung der Beispiele aus der Dokumentation in diesem Projekt nicht funktioniert. Daher wurde eine Animationen via CSS selbst geschrieben. Im Template wurde definiert, dass die Animation fade von der Transition-Komponente verwendet werden soll. Dafür wurden die von Vue erwarteten Klassen im CSS-Teil der Seite definiert. Diese Klassen haben den Namen der Animation als Präfix erhalten. Die Transition-Komponente sucht sich selbstständig die entsprechenden Klassen raus und verwendet diese für die Animation. Als Animation selbst wurde die Änderung der Hintergrundfarbe sowie der Transparenz über 2,5 Sekunden definiert.

<style scoped>
.help-card {}
.my-img {}
.my-button {}
 
.fade-enter-active,
.fade-leave-active {
    transition: all 2.5s ease;
    background-color: white;
}
 
.fade-enter-from,
.fade-leave-to {
    opacity: 0;
    background-color: pink;
}
</style>

Abbildung 10: Styles der Hilfe-Seite.

Da die Animation nun wohldefiniert ist, wird diese ausgeführt, wenn sich der im v-if betrachtete Wert toggelt. Die Animation sieht wie folgt aus:

Abbildung 11: Die erstellte Animation der Hilfe-Seite.

Erstellung des Layouts

Die Seiten sind erstellt und können über die manuelle Eingabe der Route erreicht werden. Jedoch Fehlt die Möglichkeit zur Navigation über die GUI. Für die Integration der Buttons wird die bestehende Layout-Datei verwendet, in welche die zuvor erstellten Seiten eingebettet werden. Die Datei lässt sich aus der zuvor vorgestellten Routing-Datei ableiten als MainLayout.vue. Entsprechend wird diese Datei verwendet, um die Navigation zu integrieren. Das Layout besteht aus einem Header und einem Container. Der Container beinhaltet die definierten Seiten. Dem Header hingegen wird eine Toolbar, die die Links beinhalten soll, hinzugefügt. Die Toolbar beinhaltet einen Button, der wie das Brand-Element der Toolbar aussieht und den Nutzer zurück zur Index-Seite schickt. Zusätzlich werden zwei Tabs, für jede Seite einen, die die Navigation zu ihren entsprechenden Zielen ermöglicht erstellt. Die Tabs werden entsprechend gehighlightet, wenn einer als aktiv markiert ist.

<template>
    <q-layout view="lHh Lpr lFf">
        <q-header elevated>
            <q-toolbar class="bg-purple text-white shadow-2 rounded-borders">
                <q-btn flat label="Hello Quasar" to="/" />
                <q-space />
                <q-tabs v-model="tab" shrink>
                    <q-route-tab name="users" label="Users" to="/Users" />
                    <q-route-tab name="help" label="Help" to="/help" />
                </q-tabs>
            </q-toolbar>
        </q-header>
        <q-page-container>
            <router-view />
        </q-page-container>
    </q-layout>
</template>
 
<script lang="ts">
import { defineComponent, ref } from 'vue';
 
export default defineComponent({
    name: 'MainLayout',
    setup() {
        return {
            tab: ref(''),
        };
    },
});
</script>

Abbildung 12: Code des Layouts.

Builden für Android

Damit die App als Android Application Package (APK) gebuildet werden kann, wird Capacitor verwendet. Da bei der Initialisierung Capacitor hinzugefügt wurde, kann die App einfach mit dem folgenden Befehl erstellt werden:

$ quasar build -m capacitor -T android

Abbildung 13: Befehl zum Builden einer unsignierten Android-App via Capacitor.

Jedoch ist der Build-Prozess in diesem Projekt nicht ohne weitere Konfiguration durchführbar gewesen. Die folgenden Schritte wurden ergänzend durchgeführt, bevor der Build-Befehl erneut gestartet wurde:

    1. Im Android-Ordner wurde die Datei Propertyerties erstellt und mit dem folgenden Inhalt gefüllt:

      sdk.dir = C:\\Users\\my_user \\AppData\\Local\\Android\\sdk

    2. Die Datei Propertyerties wurde um die folgenden Felder ergänzt:

      org.gradle.java.home=C:\\Program Files\\Java\\jdk-15.0.2
      org.gradle.caching=false

    3. In der Datei gradle-wrapper.Propertyerties wurde die Gradle-Version zu gradle-6.4.1-all.zip geändert

Nun kann der Build-Befehl erneut ausgeführt werden, um erfolgreich einer APK zu erstellen.

Builden für Windows

Ähnlich wie beim Build-Prozess für Android wurde beim Initialisieren die Vorarbeit für den Windows-Build geleistet, indem Electron hinzugefügt wurde. Das Ergebnis des Builds ist eine App die in einem Electron-Fenster unter Windows läuft. Der Build wird mit dem folgenden Befehl angestoßen:

$ quasar build -m electron

Abbildung 14: Befehl zum Builden einer Electron-App für Windows.

Wenn gleichzeitig macOS und Windows bedient werden sollen, muss vor jedem Build in der Quasar-Config die entsprechende Zielplattform spezifiziert werden.

Vor- und Nachteile

Jede Software hat Vor- und Nachteile. In diesem Abschnitt werden einige identifizierte Vor- und Nachteile von Quasar aufgeführt:

Vorteile

  • Mit Quasar kann mit einer Codebasis die Bedienung mehrerer Plattformen erfolgen. Dies spart Ressourcen, da nicht mehrere Codebasen geschrieben und verwaltet werden müssen.
  • Die Dokumentation von Quasar ist ausführlich und es werden viele verschiedene Aspekte betrachtet. Zusätzlich werden Beispiele für jede Quasar-Komponente und ihre Variationen angegeben. Es wird das visuelle Ergebnis und der Code präsentiert. Gleichzeitig werden Links zu interaktiven Editoren mitgeliefert.
  • Es werden Webtechnologien verwendet, die zu den populärsten in der Entwicklung zählen (Stackoverflow, 2021). Entsprechende Vorkenntnisse sind bei vielen Entwickler zu finden und die aufzuwendenden Ressourcen für Training sinkt.

Nachteile

  • Quasar Apps laufen in einer Webview. Diese verwendet eine ähnliche Pipeline zum Darstellen der Inhalte wie ein Webbrowser. Daraus ergibt sich das Problem, dass bei ressourcenintensiven Anwendungen wie beispielsweise AR oder VR Komplikationen bezüglich der Performance auftreten können.
  • Native APIs können über Capacitor-Plugins angesprochen werden. Jedoch besteht die Gefahr, dass eine spezielle Funktionalität benötigt wird, die nicht in der bestehenden Sammlung von Plugins abgedeckt wurde. Für solche Fälle wird ein nativer Entwickler benötigt.

Fazit

Um mehrere Plattformen mit nur eine Codebasis bedienen zu können, kann Quasar verwendet werden. Dabei werden die GUIs mittels Vue erstellt. Durch die Verwendung von Webtechnologien, insbesondere von Vue im Frontend, kann sich schnell und einfach in Quasar eingearbeitet werden. Dabei ist die ausführliche Dokumentation ein ergänzender Faktor, der das Einarbeiten weiter vereinfacht. Während der Entwicklung mit Quasar können Endgeräte via USB, Emulator oder WLAN für das Debugging verwendet werden. Nachdem eine App entwickelt wurde, wird diese für die Zielplattform gebuildet. Dies kann entweder mittels Capacitor für mobile Geräte oder Electron für Desktop, geschehen. Dabei ist das Builden für Desktop sehr simpel. Dem entgegen benötigt das Builden für Android mehr Aufwand in der Konfiguration.

Referenzen

Android Studio (2021). https://developer.android.com/studio. (Stand: 06.10.2021).

Capacitor (2021). https://capacitorjs.com/. (Stand: 14.10.21).

Cordova (2021). http://cordova.apache.org/. (Stand: 14.10.21).

Electron (2021). https://www.electronjs.org/. (Stand: 14.10.21).

Node (2021). https://nodejs.org/en/. (Stand: 14.10.2021).

NPM (2021). https://www.npmjs.com/. (Stand: 14.10.2021).

Quasar (2021a). https://quasar.dev/. (Stand: 14.10.2021).

Quasar (2021b). https://quasar.dev/quasar-cli/installation. (Stand: 14.10.2021).

Quasar (2021c). https://quasar.dev/options/animations. (Stand: 14.10.2021).

Stackoverflow (2021, Juni 15). https://insights.stackoverflow.com/survey/2021#technology-most-popular-technologies. (Stand: 06.10.2021).

Vue (2021a). https://vuejs.org/. (Stand: 14.10.201).

Vue (2021b). https://v3.vuejs.org/guide/transitions-overview.html. (Stand: 14.10.2021).

Leave a comment

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.

Time limit is exhausted. Please reload the CAPTCHA.