Hallo nochmal, Programmierer! Im ersten Teil dieser kurzen Serie haben wir die Erstellung und den Betrieb einer Desktop-Anwendung zum Speichern und Verschlüsseln unserer mit dem Wails-Framework erstellten Passwörter gesehen. Wir haben auch eine Beschreibung des Go-Backends erstellt und wie wir es an die Frontend-Seite binden.
In diesem Teil beschäftigen wir uns mit der Benutzeroberfläche. Wie wir in diesem Beitrag erklärt haben, können wir mit Wails jedes beliebige Web-Framework, sogar Vanilla JS, zum Erstellen unserer GUI verwenden. Wie gesagt, es scheint, dass die Macher von Wails eine Vorliebe für Svelte haben, weil sie es immer als ihre erste Wahl erwähnen. Die Wails-CLI (in ihrer aktuellen Version) generiert das Gerüst mit Svelte3, wenn wir darum bitten, ein Projekt mit Svelte Typescript zu erstellen (wails init -n myproject -t svelte-ts). Wie ich Ihnen bereits gesagt habe, wenn Sie lieber Svelte5 (und seine neuen Funktionen) verwenden möchten, habe ich ein Bash-Skript, das seine Erstellung automatisiert (in jedem Fall muss die Wails-CLI installiert sein). Darüber hinaus wird Taildwindcss Daisyui hinzugefügt, was mir eine perfekte Kombination für das Interface-Design zu sein scheint.
Die Wahrheit ist, dass ich zuerst mit Vanilla Js und Vue gearbeitet habe, dann mit React und sogar mit dieser seltsamen Bibliothek, die für viele HTMX (was ich sagen muss, dass ich es liebe ❤️). Aber Svelte sorgt dafür, dass man sich von Anfang an verliebt, und ich muss sagen, dass ich es zum ersten Mal beim Experimentieren mit Wails verwendet habe (und ich verspreche, es auch weiterhin zu verwenden …). Aber so komfortabel ein Webframework auch ist, wir müssen Backend-Entwickler daran erinnern, dass das Frontend nicht so einfach ist?!!
Aber kommen wir zum Punkt.
Wenn Sie ein Web-Framework verwendet haben, werden Sie schnell erkennen, dass die Wails-CLI unter der Haube ViteJs verwendet:
... . ├── index.html ├── package.json ├── package.json.md5 ├── package-lock.json ├── postcss.config.js ├── README.md ├── src │ ├── App.svelte │ ├── assets │ │ ├── fonts │ │ │ ├── nunito-v16-latin-regular.woff2 │ │ │ └── OFL.txt │ │ └── images │ │ └── logo-universal.png │ ├── lib │ │ ├── BackBtn.svelte │ │ ├── BottomActions.svelte │ │ ├── EditActions.svelte │ │ ├── EntriesList.svelte │ │ ├── Language.svelte │ │ ├── popups │ │ │ ├── alert-icons.ts │ │ │ └── popups.ts │ │ ├── ShowPasswordBtn.svelte │ │ └── TopActions.svelte │ ├── locales │ │ ├── en.json │ │ └── es.json │ ├── main.ts │ ├── pages │ │ ├── About.svelte │ │ ├── AddPassword.svelte │ │ ├── Details.svelte │ │ ├── EditPassword.svelte │ │ ├── Home.svelte │ │ ├── Login.svelte │ │ └── Settings.svelte │ ├── style.css │ └── vite-env.d.ts ├── svelte.config.js ├── tailwind.config.js ├── tsconfig.json ├── tsconfig.node.json ├── vite.config.ts └── wailsjs ├── go │ ├── main │ │ ├── App.d.ts │ │ └── App.js │ └── models.ts └── runtime ├── package.json ├── runtime.d.ts └── runtime.js ...
Wenn Sie ein von Vite generiertes Webframework verwendet haben, werden Sie von seinen Konfigurationsdateien nicht überrascht sein. Hier verwende ich Svelte5 (plus die Konfiguration von Taildwindcss Daisyui), was mein eigenes Bash-Skript generiert, wie ich Ihnen bereits gesagt habe. Wir verwenden auch TypeScript, was die Entwicklung des Frontends erleichtert, sodass Sie auch dessen Konfigurationen sehen können.
Aber das Wichtigste in dieser Erklärung ist der Inhalt des Ordners wailsjs. Hier hat die von Wails erstellte Zusammenstellung ihre Wirkung entfaltet. Im Unterordner „go“ werden die in Js/Ts „übersetzten“ Methoden des Backend-Teils gespeichert, der mit dem Frontend interagieren muss. Beispielsweise gibt es in main/App.js (oder seiner TypeScript-Version, main/App.d.ts) alle exportierten (öffentlichen) Methoden der App-Struktur:
... . ├── index.html ├── package.json ├── package.json.md5 ├── package-lock.json ├── postcss.config.js ├── README.md ├── src │ ├── App.svelte │ ├── assets │ │ ├── fonts │ │ │ ├── nunito-v16-latin-regular.woff2 │ │ │ └── OFL.txt │ │ └── images │ │ └── logo-universal.png │ ├── lib │ │ ├── BackBtn.svelte │ │ ├── BottomActions.svelte │ │ ├── EditActions.svelte │ │ ├── EntriesList.svelte │ │ ├── Language.svelte │ │ ├── popups │ │ │ ├── alert-icons.ts │ │ │ └── popups.ts │ │ ├── ShowPasswordBtn.svelte │ │ └── TopActions.svelte │ ├── locales │ │ ├── en.json │ │ └── es.json │ ├── main.ts │ ├── pages │ │ ├── About.svelte │ │ ├── AddPassword.svelte │ │ ├── Details.svelte │ │ ├── EditPassword.svelte │ │ ├── Home.svelte │ │ ├── Login.svelte │ │ └── Settings.svelte │ ├── style.css │ └── vite-env.d.ts ├── svelte.config.js ├── tailwind.config.js ├── tsconfig.json ├── tsconfig.node.json ├── vite.config.ts └── wailsjs ├── go │ ├── main │ │ ├── App.d.ts │ │ └── App.js │ └── models.ts └── runtime ├── package.json ├── runtime.d.ts └── runtime.js ...
Sie alle geben ein Versprechen zurück. Wenn das Versprechen eine als Rückgabetyp verwendete Go-Struktur „umschließt“ oder die entsprechende Funktion einen Argumenttyp annimmt, gibt es ein Modul (models.ts, in diesem Fall typisiert, da wir TypeScript verwenden), das die dem Go entsprechende Klasse enthält Struktur und ihr Konstruktor in einem Namespace.
Darüber hinaus enthält der Laufzeit-Unterordner alle Methoden aus dem Laufzeitpaket von Go, die es uns ermöglichen, das Fenster und die Ereignisse zu manipulieren, die vom bzw. an das Backend abgehört oder ausgegeben werden.
Der Ordner src enthält die Dateien, die von Vite kompiliert werden, um sie wie in jeder Webanwendung in „frontend/dist“ zu speichern (und in die endgültige ausführbare Datei einzubetten). Beachten Sie, dass style.css die grundlegende Tailwind-Konfiguration sowie alle CSS-Klassen enthält, die wir verwenden müssen, da wir Tailwindcss verwenden. Ein Vorteil der Verwendung von Web-Technologie für die Benutzeroberfläche besteht außerdem darin, dass wir problemlos eine oder mehrere Schriftarten (Ordner-Assets/Schriftarten) verwenden oder austauschen können.
Um diese Übersicht abzuschließen, beachten Sie, dass wir beim Kompilieren im Entwicklungsmodus (wails dev) nicht nur das Hot-Reloading ermöglichen, sondern auch die vorgenommenen Änderungen (sowohl im Backend als auch im Frontend) beobachten können im Anwendungsfenster selbst, aber auch in einem Webbrowser über die Adresse http://localhost:34115, da ein Webserver gestartet wird. Dadurch können Sie Ihre bevorzugten Browser-Entwicklungserweiterungen verwenden. Allerdings muss man sagen, dass Wails selbst uns einige sehr nützliche Entwicklungstools zur Verfügung stellt, wenn wir mit der rechten Maustaste auf das Anwendungsfenster klicken (nur im Entwicklungsmodus) und „Element prüfen“ wählen:
// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL // This file is automatically generated. DO NOT EDIT import {models} from '../models'; export function AddPasswordEntry(arg1:string,arg2:string,arg3:string):Promise<string>; export function CheckMasterPassword(arg1:string):Promise<boolean>; export function DeleteEntry(arg1:string):Promise<void>; export function Drop():Promise<void>; export function GetAllEntries():Promise<Array<models.PasswordEntry>>; export function GetEntryById(arg1:string):Promise<models.PasswordEntry>; export function GetLanguage():Promise<string>; export function GetMasterPassword():Promise<boolean>; export function GetPasswordCount():Promise<number>; export function SaveLanguage(arg1:string):Promise<void>; export function SaveMasterPassword(arg1:string):Promise<string>; export function UpdateEntry(arg1:models.PasswordEntry):Promise<boolean>;
Wie Sie sehen können, gibt es 4 JavaScript-Pakete, die ich zu Svelte hinzugefügt habe (außer dem bereits erwähnten Tailwindcss Daisyui):
Der Einstiegspunkt jedes SPA ist die Datei main.js (oder main.ts), also fangen wir damit an:
... . ├── index.html ├── package.json ├── package.json.md5 ├── package-lock.json ├── postcss.config.js ├── README.md ├── src │ ├── App.svelte │ ├── assets │ │ ├── fonts │ │ │ ├── nunito-v16-latin-regular.woff2 │ │ │ └── OFL.txt │ │ └── images │ │ └── logo-universal.png │ ├── lib │ │ ├── BackBtn.svelte │ │ ├── BottomActions.svelte │ │ ├── EditActions.svelte │ │ ├── EntriesList.svelte │ │ ├── Language.svelte │ │ ├── popups │ │ │ ├── alert-icons.ts │ │ │ └── popups.ts │ │ ├── ShowPasswordBtn.svelte │ │ └── TopActions.svelte │ ├── locales │ │ ├── en.json │ │ └── es.json │ ├── main.ts │ ├── pages │ │ ├── About.svelte │ │ ├── AddPassword.svelte │ │ ├── Details.svelte │ │ ├── EditPassword.svelte │ │ ├── Home.svelte │ │ ├── Login.svelte │ │ └── Settings.svelte │ ├── style.css │ └── vite-env.d.ts ├── svelte.config.js ├── tailwind.config.js ├── tsconfig.json ├── tsconfig.node.json ├── vite.config.ts └── wailsjs ├── go │ ├── main │ │ ├── App.d.ts │ │ └── App.js │ └── models.ts └── runtime ├── package.json ├── runtime.d.ts └── runtime.js ...
Ich habe die Dinge hervorgehoben, die ich dem von der Wails-CLI generierten Skelett hinzugefügt habe. Die svelte-i18n-Bibliothek erfordert, dass JSON-Dateien, die Übersetzungen enthalten, in der Datei main.js/ts registriert werden, während gleichzeitig die Sprache Fallback/Initial festgelegt wird (obwohl wir Ich werde sehen, das wird später basierend auf den Präferenzen des Benutzers manipuliert. Die JSON-Dateien mit den Übersetzungen haben das Format:
// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL // This file is automatically generated. DO NOT EDIT import {models} from '../models'; export function AddPasswordEntry(arg1:string,arg2:string,arg3:string):Promise<string>; export function CheckMasterPassword(arg1:string):Promise<boolean>; export function DeleteEntry(arg1:string):Promise<void>; export function Drop():Promise<void>; export function GetAllEntries():Promise<Array<models.PasswordEntry>>; export function GetEntryById(arg1:string):Promise<models.PasswordEntry>; export function GetLanguage():Promise<string>; export function GetMasterPassword():Promise<boolean>; export function GetPasswordCount():Promise<number>; export function SaveLanguage(arg1:string):Promise<void>; export function SaveMasterPassword(arg1:string):Promise<string>; export function UpdateEntry(arg1:models.PasswordEntry):Promise<boolean>;
Ich finde, dass das System dieser Bibliothek einfach und praktisch ist, um Übersetzungen von Svelte-Anwendungen zu erleichtern (weitere Informationen finden Sie in der Dokumentation):
/* package.json */ ... }, "dependencies": { "svelte-copy": "^2.0.0", "svelte-i18n": "^4.0.1", "svelte-spa-router": "^4.0.1", "sweetalert2": "^11.14.5" } ...
Sie können auch Websites wie diese verwenden, die Ihnen bei der Übersetzung von JSON-Dateien in verschiedene Sprachen helfen. Das Problem besteht jedoch darin, dass Sie beim Füllen Ihrer .svelte-Dateien mit $format den Überblick manuell behalten müssen, was mühsam und fehleranfällig ist. Ich kenne keine Möglichkeit, diese Aufgabe zu automatisieren. Wenn es jemand weiß, würde es mich interessieren, wenn Sie es mir mitteilen würden. Ansonsten müsste ich mir eine Art Skript ausdenken, um diese Aufgabe zu erledigen.
Der nächste Schritt beim Aufbau der Schnittstelle ist, wie in jeder Svelte-Anwendung, die App.svelte-Datei:
/* main.ts */ import { mount } from 'svelte' import './style.css' import App from './App.svelte' import { addMessages, init } from "svelte-i18n"; // ⇐ ⇐ import en from './locales/en.json'; // ⇐ ⇐ import es from './locales/es.json'; // ⇐ ⇐ addMessages('en', en); // ⇐ ⇐ addMessages('es', es); // ⇐ ⇐ init({ fallbackLocale: 'en', // ⇐ ⇐ initialLocale: 'en', // ⇐ ⇐ }); const app = mount(App, { target: document.getElementById('app')!, }) export default app
Hier verwenden wir GetMasterPassword, eine Bindung, die beim Kompilieren der Anwendung automatisch generiert und als öffentliche Methode der Struktur-App deklariert wurde (siehe erster Teil dieser Serie). Diese Funktion fragt die Datenbank ab und betrachtet den Benutzer für den Fall, dass darin ein Master-Passwort registriert ist, als bereits registriert (sie gibt ein Versprechen zurück, das einen booleschen Wert umschließt) und fordert ihn auf, das Passwort einzugeben, um ihm Zugriff auf den Rest zu gewähren der Ansichten. Wenn in der Datenbank kein Master-Passwort vorhanden ist, gilt der Benutzer als „neu“ und wird gebeten, sein eigenes Passwort zu generieren, um die Anwendung zum ersten Mal zu betreten.
Abschließend tun wir beim Mounten der Login.svelte-Komponente etwas, das für den Rest der Anwendung wichtig ist. Obwohl die svelte-i18n-Bibliothek uns zwingt, den anfänglichen Sprachcode zu deklarieren, wie wir bereits gesehen haben, fordern wir beim Mounten von Login.svelte die Datenbank auf (mithilfe der GetLanguage-Bindung), zu überprüfen, ob ein Sprachcode gespeichert ist. Falls die Datenbank eine leere Zeichenfolge zurückgibt, d. h. wenn keine Sprache als Präferenz des Benutzers konfiguriert ist, verwendet svelte-i18n den als initialLocale konfigurierten Wert. Wenn stattdessen eine Sprache konfiguriert ist, wird diese Sprache festgelegt (locale.set(result);) und das Ereignis „change_titles“ ausgegeben, an das die übersetzten Titel der Titelleiste und der nativen Dialoge der App übergeben werden damit das Backend Folgendes verarbeiten kann:
/* frontend/src/locales/en.json */ { "language": "Language", "app_title": "Nu-i uita • minimalist password store", "select_directory": "Select the directory where to save the data export", "select_file": "Select the backup file to import", "master_password": "Master Password ?", "generate": "Generate", "insert": "Insert", "login": "Login", ... } /* frontend/src/locales/es.json */ { "language": "Idioma", "app_title": "Nu-i uita • almacén de contraseñas minimalista", "select_directory": "Selecciona el directorio donde guardar los datos exportados", "select_file": "Selecciona el archivo de respaldo que deseas importar", "master_password": "Contraseña Maestra ?", "generate": "Generar", "insert": "Insertar", "login": "Inciar sesión", ... }
Das Folgende ist die Logik für die Handhabung der Anmeldung:
... . ├── index.html ├── package.json ├── package.json.md5 ├── package-lock.json ├── postcss.config.js ├── README.md ├── src │ ├── App.svelte │ ├── assets │ │ ├── fonts │ │ │ ├── nunito-v16-latin-regular.woff2 │ │ │ └── OFL.txt │ │ └── images │ │ └── logo-universal.png │ ├── lib │ │ ├── BackBtn.svelte │ │ ├── BottomActions.svelte │ │ ├── EditActions.svelte │ │ ├── EntriesList.svelte │ │ ├── Language.svelte │ │ ├── popups │ │ │ ├── alert-icons.ts │ │ │ └── popups.ts │ │ ├── ShowPasswordBtn.svelte │ │ └── TopActions.svelte │ ├── locales │ │ ├── en.json │ │ └── es.json │ ├── main.ts │ ├── pages │ │ ├── About.svelte │ │ ├── AddPassword.svelte │ │ ├── Details.svelte │ │ ├── EditPassword.svelte │ │ ├── Home.svelte │ │ ├── Login.svelte │ │ └── Settings.svelte │ ├── style.css │ └── vite-env.d.ts ├── svelte.config.js ├── tailwind.config.js ├── tsconfig.json ├── tsconfig.node.json ├── vite.config.ts └── wailsjs ├── go │ ├── main │ │ ├── App.d.ts │ │ └── App.js │ └── models.ts └── runtime ├── package.json ├── runtime.d.ts └── runtime.js ...
Einfach ausgedrückt: newPassword, der an die Eingabe gebundene Status, der abruft, was der Benutzer eingibt, wird zunächst von onLogin überprüft, um festzustellen, ob es mindestens 6 Zeichen enthält und ob es sich bei allen um ASCII-Zeichen handelt. d.h. sie sind nur 1 Byte lang (siehe den Grund dafür in Teil I dieser Serie) durch diese kleine Funktion const isAscii = (str: string): boolean => /^[x00-x7F] $/.test(str);. Wenn die Prüfung fehlschlägt, kehrt die Funktion zurück und zeigt dem Benutzer eine Warnung Toast an. Wenn anschließend kein Master-Passwort in der Datenbank gespeichert ist (isLogin = false), wird der Benutzertyp unabhängig von der SaveMasterPassword-Funktion (einer von Wails generierten Bindung) gespeichert. Wenn das Versprechen erfolgreich gelöst wird (gibt eine uuid-Zeichenfolge als Id des in der Datenbank gespeicherten Datensatzes zurück), wird der Benutzer vom svelte-spa-router zur Home-Ansicht weitergeleitet Push-Methode der Bibliothek. Umgekehrt, wenn das Passwort die Prüfung auf Länge und Abwesenheit von Nicht-ASCII-Zeichen besteht und es ein Master-Passwort in der Datenbank gibt (isLogin = true), dann überprüft die CheckMasterPassword-Funktion seine Identität anhand des gespeicherten Passworts und entweder bringt den Benutzer zur Home-Ansicht (Versprechen mit „true“ aufgelöst) oder es wird ein Toast angezeigt, der darauf hinweist, dass das eingegebene Passwort falsch war.
Die zentrale Ansicht der Anwendung und gleichzeitig die komplexeste ist die Home-Ansicht. Sein HTML ist eigentlich in 3 Komponenten unterteilt: eine obere Schaltflächenleiste mit einer Sucheingabe (TopActions-Komponente), eine untere Schaltflächenleiste (BottomActions-Komponente) und einen zentralen Bereich, in dem die Gesamtzahl der gespeicherten Passworteinträge oder die Liste dieser angezeigt wird ein scrollbares Fenster (EntriesList-Komponente):
// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL // This file is automatically generated. DO NOT EDIT import {models} from '../models'; export function AddPasswordEntry(arg1:string,arg2:string,arg3:string):Promise<string>; export function CheckMasterPassword(arg1:string):Promise<boolean>; export function DeleteEntry(arg1:string):Promise<void>; export function Drop():Promise<void>; export function GetAllEntries():Promise<Array<models.PasswordEntry>>; export function GetEntryById(arg1:string):Promise<models.PasswordEntry>; export function GetLanguage():Promise<string>; export function GetMasterPassword():Promise<boolean>; export function GetPasswordCount():Promise<number>; export function SaveLanguage(arg1:string):Promise<void>; export function SaveMasterPassword(arg1:string):Promise<string>; export function UpdateEntry(arg1:models.PasswordEntry):Promise<boolean>;
Das heißt, es macht den Suchstatus (searchTerms) zu einem leeren String, sodass dieser zurückgesetzt wird, wenn Suchbegriffe vorhanden sind und somit die gesamte Liste angezeigt wird. Und andererseits schaltet es den showList-Status um (props isEntriesList in TopActions), sodass die übergeordnete Komponente die Liste ein- oder ausblendet.
Wie wir im Diagramm oben sehen können, haben beide untergeordneten Komponenten dieselben props mit dem searchTerms-Status der übergeordneten Komponente. Die TopActions-Komponente erfasst die Eingaben des Benutzers und übergibt sie als Status an die übergeordnete Komponente Home, die sie wiederum als props an ihre untergeordnete Komponente EntriesList.
weitergibtDie Hauptlogik der Anzeige der vollständigen Liste oder einer nach den vom Benutzer eingegebenen Suchbegriffen gefilterten Liste wird erwartungsgemäß von der EntriesList-Komponente ausgeführt:
... . ├── index.html ├── package.json ├── package.json.md5 ├── package-lock.json ├── postcss.config.js ├── README.md ├── src │ ├── App.svelte │ ├── assets │ │ ├── fonts │ │ │ ├── nunito-v16-latin-regular.woff2 │ │ │ └── OFL.txt │ │ └── images │ │ └── logo-universal.png │ ├── lib │ │ ├── BackBtn.svelte │ │ ├── BottomActions.svelte │ │ ├── EditActions.svelte │ │ ├── EntriesList.svelte │ │ ├── Language.svelte │ │ ├── popups │ │ │ ├── alert-icons.ts │ │ │ └── popups.ts │ │ ├── ShowPasswordBtn.svelte │ │ └── TopActions.svelte │ ├── locales │ │ ├── en.json │ │ └── es.json │ ├── main.ts │ ├── pages │ │ ├── About.svelte │ │ ├── AddPassword.svelte │ │ ├── Details.svelte │ │ ├── EditPassword.svelte │ │ ├── Home.svelte │ │ ├── Login.svelte │ │ └── Settings.svelte │ ├── style.css │ └── vite-env.d.ts ├── svelte.config.js ├── tailwind.config.js ├── tsconfig.json ├── tsconfig.node.json ├── vite.config.ts └── wailsjs ├── go │ ├── main │ │ ├── App.d.ts │ │ └── App.js │ └── models.ts └── runtime ├── package.json ├── runtime.d.ts └── runtime.js ...
Wie gesagt, es werden 2 Requisiten empfangen (listCounter und search) und ein Status wird beibehalten (Einträge zulassen: models.PasswordEntry[] = $state([]);). Beim Mounten der Komponente auf Wunsch des Benutzers wird das Backend nach der vollständigen Liste der gespeicherten Passworteinträge gefragt. Sind keine Suchbegriffe vorhanden, werden diese im Bundesland gespeichert; Wenn dies der Fall ist, wird eine einfache Filterung des erhaltenen Arrays durchgeführt und es wird im Status:
gespeichert
// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL // This file is automatically generated. DO NOT EDIT import {models} from '../models'; export function AddPasswordEntry(arg1:string,arg2:string,arg3:string):Promise<string>; export function CheckMasterPassword(arg1:string):Promise<boolean>; export function DeleteEntry(arg1:string):Promise<void>; export function Drop():Promise<void>; export function GetAllEntries():Promise<Array<models.PasswordEntry>>; export function GetEntryById(arg1:string):Promise<models.PasswordEntry>; export function GetLanguage():Promise<string>; export function GetMasterPassword():Promise<boolean>; export function GetPasswordCount():Promise<number>; export function SaveLanguage(arg1:string):Promise<void>; export function SaveMasterPassword(arg1:string):Promise<string>; export function UpdateEntry(arg1:models.PasswordEntry):Promise<boolean>;
In der angezeigten Liste kann der Benutzer 2 Aktionen ausführen. Die erste besteht darin, die Details der Eingabe anzuzeigen, die ausgeführt wird, wenn er auf die entsprechende Schaltfläche klickt: onclick={() => push(`/details/${entry.Id}`)}. Im Grunde rufen wir die Push-Methode der Routing-Bibliothek auf, um den Benutzer zur Detailansicht zu führen, übergeben aber den Parameter Id, der dem betreffenden Element entspricht.
Die andere Aktion, die der Benutzer ausführen kann, besteht darin, ein Element aus der Liste zu löschen. Wenn er auf den entsprechenden Button klickt, wird ihm ein Bestätigungs-Popup angezeigt, das die Funktion showAlert aufruft. Diese Funktion ruft wiederum showWarning auf, was eigentlich eine Abstraktionsschicht über der Sweetalert2-Bibliothek ist (alle Funktionen, die die Sweetalert2-Bibliothek aufrufen, befinden sich in frontend/src/lib/popups/popups.ts). Wenn der Benutzer die Löschaktion bestätigt, wird die Bindung „DeleteEntry“ aufgerufen (um sie aus der Datenbank zu löschen). Wenn wiederum das von ihr zurückgegebene Versprechen aufgelöst wird, wird „deleteItem“ aufgerufen (um sie aus dem im Eintragsstatus gespeicherten Array zu löschen). :
/* package.json */ ... }, "dependencies": { "svelte-copy": "^2.0.0", "svelte-i18n": "^4.0.1", "svelte-spa-router": "^4.0.1", "sweetalert2": "^11.14.5" } ...
Die andere Komponente der Home-Ansicht (BottomActions) ist viel einfacher: Sie empfängt keine Requisiten und ist darauf beschränkt, den Benutzer zu verschiedenen Ansichten (Einstellungen, Info oder AddPassword) weiterzuleiten.
Die Ansichten „AddPassword“ und „EditPassword“ haben eine sehr ähnliche Logik und ähneln auch der Ansicht „Anmelden“. Beide erlauben es dem Benutzer nicht, am Anfang und Ende seiner Texteingabe Leerzeichen einzugeben, und folgen der gleichen Richtlinie wie die Anmeldeansicht, wonach Passwörter mindestens 6 ASCII-Zeichen lang sein müssen. Was sie im Wesentlichen auszeichnet, ist, dass sie die von Wails generierten Links als relevant für die Aktion bezeichnen, die sie ausführen müssen:
/* main.ts */ import { mount } from 'svelte' import './style.css' import App from './App.svelte' import { addMessages, init } from "svelte-i18n"; // ⇐ ⇐ import en from './locales/en.json'; // ⇐ ⇐ import es from './locales/es.json'; // ⇐ ⇐ addMessages('en', en); // ⇐ ⇐ addMessages('es', es); // ⇐ ⇐ init({ fallbackLocale: 'en', // ⇐ ⇐ initialLocale: 'en', // ⇐ ⇐ }); const app = mount(App, { target: document.getElementById('app')!, }) export default app
Die andere Ansicht, die etwas komplex ist, sind die Einstellungen. Dies verfügt über eine Sprachkomponente, die als props languageName von ihrer übergeordneten Komponente (Einstellungen) erhält:
/* frontend/src/locales/en.json */ { "language": "Language", "app_title": "Nu-i uita • minimalist password store", "select_directory": "Select the directory where to save the data export", "select_file": "Select the backup file to import", "master_password": "Master Password ?", "generate": "Generate", "insert": "Insert", "login": "Login", ... } /* frontend/src/locales/es.json */ { "language": "Idioma", "app_title": "Nu-i uita • almacén de contraseñas minimalista", "select_directory": "Selecciona el directorio donde guardar los datos exportados", "select_file": "Selecciona el archivo de respaldo que deseas importar", "master_password": "Contraseña Maestra ?", "generate": "Generar", "insert": "Insertar", "login": "Inciar sesión", ... }
Der HTML-Code für diese Komponente ist eine einzelne Auswahl, die die Sprachauswahl des Benutzers übernimmt. In seinem onchange-Ereignis erhält es eine Funktion (handleChange), die drei Dinge tut:
Zurück zur Einstellungsansicht: Der gesamte Betrieb wird durch eine Reihe von Ereignissen gesteuert, die an das Backend gesendet und von diesem empfangen werden. Am einfachsten ist die Schaltfläche „Beenden“: Wenn der Benutzer darauf klickt, wird ein Beendigungsereignis ausgelöst und im Backend abgehört, und die Anwendung wird geschlossen (onclick={() => EventsEmit("quit")}). Ein Tipp informiert den Benutzer darüber, dass die Escape-Taste (Verknüpfung) die gleiche Aktion ausführt, wie wir bereits erklärt haben.
Die Reset-Taste ruft eine Funktion auf, die ein Popup-Fenster anzeigt:
... . ├── index.html ├── package.json ├── package.json.md5 ├── package-lock.json ├── postcss.config.js ├── README.md ├── src │ ├── App.svelte │ ├── assets │ │ ├── fonts │ │ │ ├── nunito-v16-latin-regular.woff2 │ │ │ └── OFL.txt │ │ └── images │ │ └── logo-universal.png │ ├── lib │ │ ├── BackBtn.svelte │ │ ├── BottomActions.svelte │ │ ├── EditActions.svelte │ │ ├── EntriesList.svelte │ │ ├── Language.svelte │ │ ├── popups │ │ │ ├── alert-icons.ts │ │ │ └── popups.ts │ │ ├── ShowPasswordBtn.svelte │ │ └── TopActions.svelte │ ├── locales │ │ ├── en.json │ │ └── es.json │ ├── main.ts │ ├── pages │ │ ├── About.svelte │ │ ├── AddPassword.svelte │ │ ├── Details.svelte │ │ ├── EditPassword.svelte │ │ ├── Home.svelte │ │ ├── Login.svelte │ │ └── Settings.svelte │ ├── style.css │ └── vite-env.d.ts ├── svelte.config.js ├── tailwind.config.js ├── tsconfig.json ├── tsconfig.node.json ├── vite.config.ts └── wailsjs ├── go │ ├── main │ │ ├── App.d.ts │ │ └── App.js │ └── models.ts └── runtime ├── package.json ├── runtime.d.ts └── runtime.js ...
Wenn der Benutzer die Aktion akzeptiert, wird die Drop-Bindung aufgerufen, die alle Sammlungen in der Datenbank bereinigt, und wenn das zurückgegebene Versprechen aufgelöst wird, wird der Benutzer zur Anmeldeansicht weitergeleitet und angezeigt ein Modal, das den Erfolg der Aktion angibt.
Die anderen beiden verbleibenden Aktionen sind einander ähnlich, schauen wir uns also Daten importieren an.
Wenn der Benutzer auf die entsprechende Schaltfläche klickt, wird ein Ereignis ausgegeben (onclick={() => EventsEmit("import_data")}), auf das im Backend gewartet wird. Nach dem Empfang wird das native Dialogfeld Datei auswählen geöffnet, damit der Benutzer die Sicherungsdatei auswählen kann. Wenn der Benutzer die Datei auswählt, enthält die Variable mit dem Pfad (fileLocation) keinen leeren String und dies löst ein Ereignis im Backend („enter_password“) aus, das nun im Frontend abgehört wird, um wiederum ein Ereignis anzuzeigen Neues Popup-Fenster, in dem Sie nach dem Master-Passwort gefragt werden, das beim Export verwendet wurde. Auch hier gibt das Frontend ein weiteres Ereignis („Passwort“) aus, das das vom Benutzer eingegebene Master-Passwort trägt. Wenn dieses neue Ereignis im Backend empfangen wird, führt es die ImportDump-Methode des DB-Pakets aus, die die Arbeit des Lesens und Wiederherstellens der Daten in der DB aus der vom Benutzer ausgewählten Sicherungsdatei ausführt. Als Ergebnis wird ein neues Ereignis („imported_data“) ausgegeben, das das Ergebnis (erfolgreich oder nicht erfolgreich) seiner Ausführung als angehängte Daten trägt. Wenn das Frontend das Ereignis empfängt, muss es nur zwei Aufgaben ausführen:
All dies ist in der Codelogik viel einfacher zu erkennen, als mit Worten zu erklären?:
... . ├── index.html ├── package.json ├── package.json.md5 ├── package-lock.json ├── postcss.config.js ├── README.md ├── src │ ├── App.svelte │ ├── assets │ │ ├── fonts │ │ │ ├── nunito-v16-latin-regular.woff2 │ │ │ └── OFL.txt │ │ └── images │ │ └── logo-universal.png │ ├── lib │ │ ├── BackBtn.svelte │ │ ├── BottomActions.svelte │ │ ├── EditActions.svelte │ │ ├── EntriesList.svelte │ │ ├── Language.svelte │ │ ├── popups │ │ │ ├── alert-icons.ts │ │ │ └── popups.ts │ │ ├── ShowPasswordBtn.svelte │ │ └── TopActions.svelte │ ├── locales │ │ ├── en.json │ │ └── es.json │ ├── main.ts │ ├── pages │ │ ├── About.svelte │ │ ├── AddPassword.svelte │ │ ├── Details.svelte │ │ ├── EditPassword.svelte │ │ ├── Home.svelte │ │ ├── Login.svelte │ │ └── Settings.svelte │ ├── style.css │ └── vite-env.d.ts ├── svelte.config.js ├── tailwind.config.js ├── tsconfig.json ├── tsconfig.node.json ├── vite.config.ts └── wailsjs ├── go │ ├── main │ │ ├── App.d.ts │ │ └── App.js │ └── models.ts └── runtime ├── package.json ├── runtime.d.ts └── runtime.js ...
Es ist erwähnenswert, dass die Wails-Laufzeitfunktion, die Listener im Frontend registriert (EventsOn), eine Funktion zurückgibt, die bei Aufruf den Listener löscht. Es ist praktisch, diese Listener abzubrechen, wenn die Komponente zerstört wird. Ähnlich wie React kann der onMount-Hook diese Listener „bereinigen“, indem er sie dazu bringt, eine Bereinigungsfunktion zurückzugeben, die in diesem Fall alle von EventsOn zurückgegebenen Funktionen aufruft, die wir vorsorglich separat gespeichert haben Variablen:
// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL // This file is automatically generated. DO NOT EDIT import {models} from '../models'; export function AddPasswordEntry(arg1:string,arg2:string,arg3:string):Promise<string>; export function CheckMasterPassword(arg1:string):Promise<boolean>; export function DeleteEntry(arg1:string):Promise<void>; export function Drop():Promise<void>; export function GetAllEntries():Promise<Array<models.PasswordEntry>>; export function GetEntryById(arg1:string):Promise<models.PasswordEntry>; export function GetLanguage():Promise<string>; export function GetMasterPassword():Promise<boolean>; export function GetPasswordCount():Promise<number>; export function SaveLanguage(arg1:string):Promise<void>; export function SaveMasterPassword(arg1:string):Promise<string>; export function UpdateEntry(arg1:models.PasswordEntry):Promise<boolean>;
Um diese Überprüfung des Frontend-Teils unserer Anwendung abzuschließen, bleibt nur noch etwas über die About-Komponente zu sagen. Dies hat wenig Logik, da es sich auf die Anzeige von Informationen über die Anwendung beschränkt, wie es in einem About üblich ist. Es sollte jedoch gesagt werden, dass die Ansicht, wie wir sehen können, einen Link zum Anwendungs-Repository anzeigt. Offensichtlich würde uns ein Ankertag () auf einer normalen Webseite dazu veranlassen, zum entsprechenden Link zu navigieren, aber in einer Desktop-Anwendung würde dies nicht passieren, wenn Wails in seiner Laufzeit keine spezielle Funktion (BrowserOpenURL) dafür hätte :
/* package.json */ ... }, "dependencies": { "svelte-copy": "^2.0.0", "svelte-i18n": "^4.0.1", "svelte-spa-router": "^4.0.1", "sweetalert2": "^11.14.5" } ...
Dadurch wird die Binärdatei im Ordner build/bin erstellt. Wenn Sie jedoch andere Build-Optionen auswählen oder Cross-Compiling durchführen möchten, sollten Sie sich die Wails-CLI-Dokumentation ansehen.
Für diese Anwendung habe ich sie, glaube ich, bereits im ersten Teil dieser Serie erwähnt, ich habe mich nur auf die Kompilierung für Windows und Linux konzentriert. Um diese Aufgaben (die sich aufgrund von Tests wiederholen) auf komfortable Weise auszuführen, habe ich einige kleine Skripte und ein Makefile erstellt, das sie „koordiniert“.
Der Befehl make create-bundles erstellt für die Linux-Version eine .tar.xz-komprimierte Datei mit der Anwendung und einem Makefile, das als „Installer“ fungiert, der die ausführbare Datei installiert, einen Desktop-Eintrag zum Erstellen eines Eintrags im Startmenü und das entsprechende Anwendungssymbol. Für die Windows-Version wird die Binärdatei einfach als .zip in einem Ordner namens dist/ komprimiert. Wenn Sie jedoch einen plattformübergreifenden automatisierten Build bevorzugen, verfügt Wails über Github-Aktionen, mit denen Sie die generierten Artefakte hochladen können (Standardoption). zu Ihrem Repository.
Beachten Sie, dass, wenn Sie den Befehl make create-bundles verwenden, wenn Sie ihn ausführen, die Wails-Befehle wails build -clean -upx (im Fall von Linux) oder wails build -skipbindings -s -platform windows/amd64 aufgerufen werden. upx (im Fall von Windows). Das Flag -upx bezieht sich auf die Komprimierung der Binärdatei mit dem Dienstprogramm UPX, das Sie auf Ihrem Computer installiert haben sollten. Ein Teil des Geheimnisses der geringen Größe der ausführbaren Datei liegt in der hervorragenden Komprimierungsleistung, die dieses Dienstprogramm leistet.
Beachten Sie abschließend, dass die Build-Skripte das aktuelle Repository-Tag automatisch zur Info-Ansicht hinzufügen und nach dem Build seinen Wert auf den Standardwert (DEV_VERSION) zurücksetzen.
Puh! Diese beiden Beiträge waren am Ende länger als ich dachte! Aber ich hoffe, dass sie Ihnen gefallen haben und dass sie Ihnen vor allem dabei helfen, über neue Projekte nachzudenken. Etwas im Programmieren zu lernen funktioniert so…
Denken Sie daran, dass Sie den gesamten Anwendungscode in diesem GitHub-Repository finden.
Ich bin sicher, wir sehen uns in anderen Beiträgen. Viel Spaß beim Codieren ?!!
Das obige ist der detaillierte Inhalt vonEine minimalistische Passwort-Manager-Desktop-App: ein Ausflug in das Wails-Framework von Golang (Teil 2). Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!