搜尋
首頁後端開發Golang極簡密碼管理器桌面應用程式:進軍 Golang 的 Wails 框架(第 2 部分)

各位程式設計師,大家好!在這個簡短系列的第一部分中,我們看到了桌面應用程式的建立和操作,用於儲存和加密使用 Wails 框架建立的密碼。我們還描述了 Go 後端以及如何將其綁定到前端。

在這一部分中,我們將處理使用者介面。正如我們在那篇文章中所述,Wails 允許我們使用任何我們喜歡的 Web 框架,甚至 Vanilla JS,來建立我們的 GUI。正如我所說,Wails 的創作者似乎偏愛 Svelte,因為他們總是將其作為首選。當我們要求使用 Svelte Typescript (wails init -n myproject -t svelte-ts) 建立專案時,Wails CLI(目前版本)會使用 Svelte3 產生鷹架。正如我已經告訴過您的,如果您更喜歡使用 Svelte5(及其新功能),我有一個 bash 腳本可以自動建立它(無論如何,您必須安裝 Wails CLI)。此外,它還添加了 Taildwindcss Daisyui,這在我看來是介面設計的完美組合。

事實是,我先使用Vanilla JsVue,然後使用React,甚至使用那個對許多人來說是React 的奇怪函式庫。 🎜>HTMX(我必須說我喜歡❤️)。但是Svelte

讓你從一開始就愛上它,我不得不說,我是在嘗試Wails時第一次使用它的(我保證會繼續使用它......)。但是,儘管 Web 框架很舒服,但我們必須提醒後端開發人員,前端並不那麼容易? ! !

但是讓我們進入正題吧。

I - 前端結構概覽


如果您使用過任何 Web 框架,您很快就會認識到 Wails CLI 在底層使用了 ViteJ:

...
.
├── 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

...

如果您使用過 Vite 產生的任何 Web 框架,您不會對其設定檔感到驚訝。這裡我使用 Svelte5 (加上 Taildwindcss Daisyui 的配置),這就是產生我自己的 bash 腳本的原因,正如我已經告訴你的。我們也使用了TypeScript

,這樣會方便前端的開發,所以你也可以看到它的配置。

但這個解釋中最重要的是wailsjs資料夾的內容。這就是Wails 所做的編譯發揮其魔力的地方。 go 子資料夾是儲存必須與前端互動的後端部分的「翻譯」為 Js/Ts
的方法的位置。例如,在 main/App.js(或其 TypeScript 版本 main/App.d.ts)中,有 App 結構的所有匯出(公共)方法:

...
.
├── 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

...

他們都回傳一個承諾。如果Promise「包裝」了一些用作返回類型的Go 結構,或者相應的函數採用參數類型,則會有一個模組(models.ts,在本例中為類型,因為我們使用TypeScript),其中包含與Go對應的類別命名空間中的結構及其建構函數。

此外,runtime 子資料夾包含 Go 執行時間套件中的所有方法,這些方法允許我們分別操作視窗以及從後端偵聽或發出的事件。

src 資料夾包含將由 Vite 編譯的文件,並將它們保存在「frontend/dist」中(並嵌入到最終的可執行檔中),就像在任何 Web 應用程式中一樣。請注意,由於我們使用 Tailwindcss,style.css 包含基本的 Tailwind 配置以及我們需要使用的任何 CSS 類別。此外,作為介面使用網路技術的優勢,我們可以輕鬆地使用一種或多種字體(資料夾資產/字體)或交換它們。

為了完成這個概述,請注意,當我們在開發模式(wails dev)下編譯時,除了允許我們熱重載之外,我們不僅可以觀察所做的更改(無論是在後端還是在前端)應用程式視窗本身,也透過位址http://localhost:34115 在Web 瀏覽器中,因為Web 伺服器已啟動。這允許您使用您最喜歡的瀏覽器開發擴充功能。雖然必須說Wails本人為我們提供了一些非常有用的開發工具,但是當我們右鍵單擊應用程式視窗(僅在開發模式下)並選擇“Inspect Element”時:

A minimalist password manager desktop app: a foray into Golang

II - 現在…深入研究 HTML、CSS 和 JavaScript?


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

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>;
</boolean></string></void></number></boolean></string></models.passwordentry></array></void></void></boolean></string>

如你所見,我已經在 Svelte 中加入了 4 個 JavaScript 套件(除了已經提到的 Tailwindcss Daisyui):

  • svelte-copy,可以更輕鬆地將使用者名稱和密碼複製到剪貼簿。
  • svelte-i18n,用於 i18n 處理,即允許使用者更改應用程式的語言。
  • svelte-spa-router,Svelte 的一個小型路由庫,它可以更輕鬆地更改應用程式視窗中的視圖,因為在這種情況下,使用由SvelteKit。
  • sweetalert2,基本上用它來輕鬆快速地建立模態框/對話框。

每個 SPA 的入口點都是 main.js(或 main.ts)文件,所以讓我們從它開始:

...
.
├── 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

...

我突出顯示了添加到由 Wails CLI 產生的框架中的內容。 svelte-i18n 庫要求在main.js/ts 文件中註冊包含翻譯的JSON 文件,同時設定fallback/initial 語言(儘管我們'你會看到,稍後將根據使用者選擇的偏好進行操作)。包含翻譯的 JSON 檔案的格式為:

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

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>;
</boolean></string></void></number></boolean></string></models.passwordentry></array></void></void></boolean></string>

我發現這個函式庫的系統對於促進 Svelte 應用程式的翻譯非常簡單和方便(您可以查閱其文件以了解更多詳細資訊):

/* package.json */
...
},
  "dependencies": {
    "svelte-copy": "^2.0.0",
    "svelte-i18n": "^4.0.1",
    "svelte-spa-router": "^4.0.1",
    "sweetalert2": "^11.14.5"
  }
...

您也可以使用像這樣的網站,它將幫助您將 JSON 檔案翻譯成不同的語言。然而,問題是,當您使用 $format 填充 .svelte 檔案時,您必須手動追蹤它們,這是乏味且容易出錯的。我不知道有什麼方法可以自動完成這項任務,如果有人知道,我會很感興趣,如果你能讓我知道? ……否則,我必須想出某種腳本來完成這項工作。

與任何 Svelte 應用程式一樣,建立介面的下一步是 App.svelte 檔案:

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

這裡我們使用 GetMasterPassword,它是編譯應用程式時自動產生的 綁定,並被宣告為 struct App 的公共方法(請參閱本系列的第一部分)。該函數查詢資料庫,如果其中註冊了主密碼,它會認為使用者已經註冊(它會傳回一個包含布林值的承諾),要求他輸入所述密碼以允許他存取其餘內容的意見。如果資料庫中沒有主密碼,則該使用者被視為“新”,要求他產生自己的密碼以首次進入應用程式。

最後,在安裝 Login.svelte 元件時,我們做了一些對應用程式的其餘部分很重要的事情。儘管 svelte-i18n 庫強制我們聲明初始語言代碼,但正如我們已經看到的,在安裝 Login.svelte 時,我們要求資料庫(使用 GetLanguage 綁定)檢查是否保存了語言代碼。如果資料庫傳回空字串,即沒有配置為使用者首選項的語言,svelte-i18n 將使用配置為initialLocale 的值。如果配置了一種語言,則將設定該語言(locale.set(result);) 並發出「change_titles」事件,標題列和應用程式本機對話方塊的翻譯標題將傳遞到該事件供後端處理:

/* 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",
    ...
}

以下是處理登入的邏輯:

...
.
├── 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

...

簡單地說:newPassword,綁定到獲取用戶輸入內容的輸入的狀態,首先由onLogin 檢查它是否至少有6 個字符,並且它們都是ASCII 字符,即,通過這個小函數const isAscii = (str: string): boolean => 它們只有1 個位元組長(請參閱本系列第一部分的原因)。 /^[x00-x7F] $/.test(str);.如果檢查失敗,函數將傳回並向使用者顯示警告 toast。之後,如果資料庫中沒有保存主密碼(isLogin = false),則SaveMasterPassword 函數將保存使用者輸入的任何內容(Wails 產生的綁定);如果Promise 成功解析(傳回uuid 字串作為資料庫中儲存的記錄的Id),使用者將被svelte-spa-router 帶到主頁視圖庫的推送方法。相反,如果密碼通過了長度檢查,且不存在非ASCII 字符,且資料庫中存在主密碼(isLogin = true),則CheckMasterPassword 函數將根據儲存的密碼驗證其身份,或將使用者帶到主視圖(promise 為true 解決)或顯示toast 表示輸入的密碼不正確。

應用程式的中心視圖,同時也是最複雜的視圖是主頁視圖。它的HTML 實際上分為3 個元件:一個帶有搜尋輸入的頂部按鈕列(TopActions 元件)、一個底部按鈕欄(BottomActions 元件)以及一個中心區域,其中使用以下命令顯示已儲存密碼條目的總數或這些項目的清單:可捲動視窗(EntriesList 元件):

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

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>;
</boolean></string></void></number></boolean></string></models.passwordentry></array></void></void></boolean></string>

也就是說,它使搜尋狀態(searchTerms)成為一個空字串,這樣如果有任何搜尋詞,它就會被重置,從而顯示整個列表。另一方面,它會切換 showList 狀態(props TopActions 中的 isEntriesList),以便父元件顯示或隱藏清單。

如我們在上圖中所看到的,兩個子元件與父元件的 searchTerms 狀態共用相同的 props。 TopActions 元件會擷取使用者的輸入,並將其作為狀態傳遞給父元件 Home,而父元件 Home 又將其作為 props 傳遞給其子元件 EntriesList。

顯示完整清單或按使用者輸入的搜尋字詞過濾的清單的主要邏輯按預期由 EntriesList 元件執行:

...
.
├── 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

...

正如我們所說,收到 2 個 props(listCounter 和 search)並維護一個狀態(讓條目:models.PasswordEntry[] = $state([]);)。當根據使用者的請求安裝該元件時,後端會被要求提供已儲存密碼條目的完整清單。如果沒有搜尋字詞,則將其儲存在狀態中;如果有,則對獲得的陣列進行簡單過濾,並將其保存在狀態:

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

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>;
</boolean></string></void></number></boolean></string></models.passwordentry></array></void></void></boolean></string>

在顯示的清單中,使用者可以執行 2 個操作。第一個是顯示條目的詳細信息,當他點擊相應的按鈕時執行:onclick={() =>推送(`/details/${entry.Id}`)}。基本上,我們呼叫路由庫的 Push 方法將使用者帶到詳細資訊視圖,但傳遞與相關項目相對應的 Id 參數。

使用者可以執行的另一個操作是從清單中刪除項目。如果他單擊相應的按鈕,將顯示一個確認彈出窗口,並呼叫 showAlert 函數。函數依序呼叫 showWarning,它實際上是 sweetalert2 函式庫的抽象層(呼叫 sweetalert2 函式庫的所有函數都在 frontend/src/lib/popups/popups.ts 中)。如果使用者確認刪除操作,則呼叫DeleteEntry綁定(將其從資料庫中刪除),反過來,如果它傳回的promise得到解析,則呼叫deleteItem(將其從儲存在條目狀態的陣列中刪除) :

/* package.json */
...
},
  "dependencies": {
    "svelte-copy": "^2.0.0",
    "svelte-i18n": "^4.0.1",
    "svelte-spa-router": "^4.0.1",
    "sweetalert2": "^11.14.5"
  }
...

主頁視圖的另一個元件(BottomActions)簡單得多:它不會接收 props 並且僅限於將使用者重新導向到各種視圖(Settings、About 或 AddPassword)。

AddPassword 和 EditPassword 視圖的邏輯非常相似,也與 Login 視圖類似。兩者都不允許使用者在文字輸入的開頭和結尾輸入空格,並遵循與登入視圖相同的策略,要求密碼長度至少為 6 個 ASCII 字元。基本上,它們的與眾不同之處在於它們調用與需要執行的操作相關的由 Wails 產生的連結:

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

另一個有點複雜的視圖是「設定」。它有一個語言元件,它從其父元件(設定)接收 props languageName:

/* 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",
    ...
}

此元件的 HTML 是一個處理使用者語言選擇的 select。在其 onchange 事件中,它接收一個函數 (handleChange),該函數執行 3 件事:

  • 使用 svelte-i18n 函式庫設定前端語言
  • 發出一個事件(“change_titles”),以便Wails運行時更改應用程式標題列的標題以及相關的選擇目錄選擇檔案對話框的標題到上一個操作
  • 將使用者選擇的語言保存在資料庫中,以便下次啟動應用程式時,它將開啟配置為該語言的。

傳回「設定」視圖,其整個操作由一系列傳送到後端或從後端接收的事件控制。最簡單的是退出按鈕:當使用者點擊它時,會在後端觸發並偵聽退出事件,然後應用程式關閉(onclick={() => EventsEmit("quit")})。 提示 通知使用者 Escape 鍵(快速鍵)執行相同的操作,正如我們已經解釋過的。

重置按鈕呼叫顯示彈出視窗的函數:

...
.
├── 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

...

如果使用者接受該操作,則會呼叫Drop 綁定,這會清除資料庫中的所有collections,如果它傳回的Promise 已解析,則會將使用者傳送至Login 視圖,顯示指示操作成功的模式。

剩下的另外兩個操作彼此類似,所以讓我們看看導入資料

如果使用者點擊對應的按鈕,則會發出事件(onclick={() => EventsEmit("import_data")}),該事件在後端監聽。收到後,將開啟本機選擇檔案對話方塊以允許使用者選擇備份檔案。如果使用者選擇文件,包含路徑(fileLocation)的變數將不包含空字串,這將在後端觸發一個事件(“enter_password”),該事件現在在前端偵聽,然後顯示新的彈出視窗詢問匯出時使用的主密碼。同樣,前端將發出另一個事件(「密碼」),其中攜帶使用者輸入的主密碼。當後端接收到這個新事件時,會執行 Db 套件的 ImportDump 方法,該方法執行從使用者選擇的備份檔案中讀取和還原 DB 中的資料的工作。結果,發出一個新事件(“imported_data”),該事件將其執行結果(成功或不成功)作為附加資料攜帶。前端收到事件後只需執行 2 個任務:

  • 如果結果成功,請設定備份檔案中儲存的語言並顯示指示操作成功的模式
  • 如果由於某種原因無法完成匯入,請顯示錯誤及其原因。

所有這些在程式碼邏輯中比用文字解釋要容易得多? :

...
.
├── 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

...

值得一提的是,在前端註冊偵聽器的 Wails 運行時函數 (EventsOn) 傳回一個函數,該函數在呼叫時會取消所述偵聽器。當組件被銷毀時,取消所述監聽器是很方便的。與React 類似,onMount 鉤子可以透過讓監聽器傳回一個清理函數來「清理」它們,在這種情況下,該函數將呼叫EventsOn 傳回的所有函數,我們已採取預防措施將其保存在單獨的文件中。變數:

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

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>;
</boolean></string></void></number></boolean></string></models.passwordentry></array></void></void></boolean></string>

為了完成對我們應用程式前端部分的審查,只需介紹一下「關於」組件即可。這幾乎沒有邏輯,因為它僅限於顯示有關應用程式的信息,就像常見的 about 一樣。然而,應該說,正如我們所看到的,該視圖顯示了指向應用程式儲存庫的連結。顯然,在普通網頁中,錨標記() 將使我們導航到相應的鏈接,但在桌面應用程式中,如果Wails 在運行時沒有為此提供特定函數(BrowserOpenURL),則不會發生這種情況:

/* package.json */
...
},
  "dependencies": {
    "svelte-copy": "^2.0.0",
    "svelte-i18n": "^4.0.1",
    "svelte-spa-router": "^4.0.1",
    "sweetalert2": "^11.14.5"
  }
...

這會將二進位檔案建置到 build/bin 資料夾中。但是,要選擇其他建置選項或執行交叉編譯,您可能需要查看 Wails CLI 文件。

對於這個應用程序,我想我已經在本系列的第一部分中提到過,我只關注Windows和Linux的編譯。為了以舒適的方式執行這些任務(由於測試,這些任務是重複的),我創建了一些小腳本和一個「協調」它們的 Makefile。

make create-bundles 指令為Linux 版本建立一個.tar.xz 壓縮文件,其中包含應用程式和一個充當安裝執行檔的「安裝程式」的Makefile,一個用於在開始功能表以及對應的應用程式圖示。對於 Windows 版本,二進位檔案只是在名為 dist/ 的資料夾中壓縮為 .zip。但是,如果您喜歡跨平台自動構建,Wails 有一個 Github Actions,允許您上傳(預設選項)生成的工件到您的儲存庫。

請注意,如果您在運行時使用make create-bundles 命令,它將呼叫Wails 命令wails build -clean -upx (對於Linux)或wails build -skipbindings -s -platform windows/amd64 - upx(對於Windows )。 -upx 標誌是指使用您應該安裝在電腦上的

UPX 實用程式來壓縮二進位檔案。可執行檔案體積小的部分秘密是由於該實用程式所做的出色的壓縮工作。

最後,請注意,建置腳本會自動將目前儲存庫標籤新增至「關於」視圖,並在建置後將其值還原為預設值 (DEV_VERSION)。

唷!這兩篇文章比我想像的還要長!但我希望您喜歡它們,最重要的是,它們可以幫助您思考新專案。在程式設計中學習一些東西就像這樣...

請記住,您可以在此 GitHub 儲存庫中找到所有應用程式程式碼。

我相信我會在其他貼文中見到你。編碼愉快? ! !

以上是極簡密碼管理器桌面應用程式:進軍 Golang 的 Wails 框架(第 2 部分)的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn
了解Goroutines:深入研究GO的並發了解Goroutines:深入研究GO的並發May 01, 2025 am 12:18 AM

goroutinesarefunctionsormethodsthatruncurranceingo,啟用效率和燈威量。 1)shememanagedbodo'sruntimemultimusingmultiplexing,允許千sstorunonfewerosthreads.2)goroutinessimproverentimensImproutinesImproutinesImproveranceThroutinesImproveranceThrountinesimproveranceThroundinesImproveranceThroughEasySytaskParallowalizationAndeff

了解GO中的初始功能:目的和用法了解GO中的初始功能:目的和用法May 01, 2025 am 12:16 AM

purposeoftheInitfunctionoIsistoInitializeVariables,setUpConfigurations,orperformneccesSetarySetupBeforEtheMainFunctionExeCutes.useInitby.UseInitby:1)placingitinyourcodetorunautoamenationally oneraty oneraty oneraty on inity in ofideShortAndAndAndAndForemain,2)keepitiTshortAntAndFocusedonSimImimpletasks,3)

了解GO界面:綜合指南了解GO界面:綜合指南May 01, 2025 am 12:13 AM

Gointerfacesaremethodsignaturesetsthattypesmustimplement,enablingpolymorphismwithoutinheritanceforcleaner,modularcode.Theyareimplicitlysatisfied,usefulforflexibleAPIsanddecoupling,butrequirecarefulusetoavoidruntimeerrorsandmaintaintypesafety.

從恐慌中恢復:何時以及如何使用recover()從恐慌中恢復:何時以及如何使用recover()May 01, 2025 am 12:04 AM

在Go中使用recover()函數可以從panic中恢復。具體方法是:1)在defer函數中使用recover()捕獲panic,避免程序崩潰;2)記錄詳細的錯誤信息以便調試;3)根據具體情況決定是否恢復程序執行;4)謹慎使用,以免影響性能。

您如何使用'字符串”包裝操縱串中的琴弦?您如何使用'字符串”包裝操縱串中的琴弦?Apr 30, 2025 pm 02:34 PM

本文討論了使用GO的“字符串”軟件包進行字符串操作,詳細介紹了共同的功能和最佳實踐,以提高效率並有效地處理Unicode。

您如何使用'加密”在Go中執行加密操作的軟件包?您如何使用'加密”在Go中執行加密操作的軟件包?Apr 30, 2025 pm 02:33 PM

本文使用GO的“加密”軟件包詳細介紹了加密操作,討論了安全實施的關鍵生成,管理和最佳實踐。

您如何使用'時間”處理日期和時間的包裝?您如何使用'時間”處理日期和時間的包裝?Apr 30, 2025 pm 02:32 PM

本文詳細介紹了GO的“時間”包用於處理日期,時間和時區,包括獲得當前時間,創建特定時間,解析字符串以及測量經過的時間。

您如何使用'反映”包裹檢查GO中變量的類型和值?您如何使用'反映”包裹檢查GO中變量的類型和值?Apr 30, 2025 pm 02:29 PM

文章討論了使用GO的“反射”軟件包進行可變檢查和修改,突出顯示方法和性能注意事項。

See all articles

熱AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover

AI Clothes Remover

用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool

Undress AI Tool

免費脫衣圖片

Clothoff.io

Clothoff.io

AI脫衣器

Video Face Swap

Video Face Swap

使用我們完全免費的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱工具

SublimeText3 Linux新版

SublimeText3 Linux新版

SublimeText3 Linux最新版

WebStorm Mac版

WebStorm Mac版

好用的JavaScript開發工具

Dreamweaver Mac版

Dreamweaver Mac版

視覺化網頁開發工具

SublimeText3 英文版

SublimeText3 英文版

推薦:為Win版本,支援程式碼提示!

禪工作室 13.0.1

禪工作室 13.0.1

強大的PHP整合開發環境