各位程式設(shè)計師,大家好!在這個簡短系列的第一部分中,我們看到了桌面應(yīng)用程式的建立和操作,用於儲存和加密使用 Wails 框架建立的密碼。我們還描述了 Go 後端以及如何將其綁定到前端。
在這一部分中,我們將處理使用者介面。正如我們在那篇文章中所述,Wails 允許我們使用任何我們喜歡的 Web 框架,甚至 Vanilla JS,來建立我們的 GUI。正如我所說,Wails 的創(chuàng)作者似乎偏愛 Svelte,因?yàn)樗麄兛偸菍⑵渥鳛槭走x。當(dāng)我們要求使用 Svelte Typescript (wails init -n myproject -t svelte-ts) 建立專案時,Wails CLI(目前版本)會使用 Svelte3 產(chǎn)生鷹架。正如我已經(jīng)告訴過您的,如果您更喜歡使用 Svelte5(及其新功能),我有一個 bash 腳本可以自動建立它(無論如何,您必須安裝 Wails CLI)。此外,它還添加了 Taildwindcss Daisyui,這在我看來是介面設(shè)計的完美組合。
事實(shí)是,我先使用Vanilla Js 和Vue,然後使用React,甚至使用那個對許多人來說是React 的奇怪函式庫。 ??>HTMX(我必須說我喜歡??)。但是Svelte
讓你從一開始就愛上它,我不得不說,我是在嘗試Wails時第一次使用它的(我保證會繼續(xù)使用它......)。但是,儘管 Web 框架很舒服,但我們必須提醒後端開發(fā)人員,前端並不那麼容易? ! !但是讓我們進(jìn)入正題吧。
I - 前端結(jié)構(gòu)概覽
如果您使用過任何 Web 框架,您很快就會認(rèn)識到 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 產(chǎn)生的任何 Web 框架,您不會對其設(shè)定檔感到驚訝。這裡我使用 Svelte5 (加上 Taildwindcss Daisyui 的配置),這就是產(chǎn)生我自己的 bash 腳本的原因,正如我已經(jīng)告訴你的。我們也使用了TypeScript
,這樣會方便前端的開發(fā),所以你也可以看到它的配置。
但這個解釋中最重要的是wailsjs資料夾的內(nèi)容。這就是Wails 所做的編譯發(fā)揮其魔力的地方。 go 子資料夾是儲存必須與前端互動的後端部分的「翻譯」為 Js/Ts
的方法的位置。例如,在 main/App.js(或其 TypeScript 版本 main/App.d.ts)中,有 App 結(jié)構(gòu)的所有匯出(公共)方法:
... . ├── 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 結(jié)構(gòu),或者相應(yīng)的函數(shù)採用參數(shù)類型,則會有一個模組(models.ts,在本例中為類型,因?yàn)槲覀兪褂肨ypeScript),其中包含與Go對應(yīng)的類別命名空間中的結(jié)構(gòu)及其建構(gòu)函數(shù)。
此外,runtime 子資料夾包含 Go 執(zhí)行時間套件中的所有方法,這些方法允許我們分別操作視窗以及從後端偵聽或發(fā)出的事件。
src 資料夾包含將由 Vite 編譯的文件,並將它們保存在「frontend/dist」中(並嵌入到最終的可執(zhí)行檔中),就像在任何 Web 應(yīng)用程式中一樣。請注意,由於我們使用 Tailwindcss,style.css 包含基本的 Tailwind 配置以及我們需要使用的任何 CSS 類別。此外,作為介面使用網(wǎng)路技術(shù)的優(yōu)勢,我們可以輕鬆地使用一種或多種字體(資料夾資產(chǎn)/字體)或交換它們。
為了完成這個概述,請注意,當(dāng)我們在開發(fā)模式(wails dev)下編譯時,除了允許我們熱重載之外,我們不僅可以觀察所做的更改(無論是在後端還是在前端)應(yīng)用程式視窗本身,也透過位址http://localhost:34115 在Web 瀏覽器中,因?yàn)閃eb 伺服器已啟動。這允許您使用您最喜歡的瀏覽器開發(fā)擴(kuò)充功能。雖然必須說Wails本人為我們提供了一些非常有用的開發(fā)工具,但是當(dāng)我們右鍵單擊應(yīng)用程式視窗(僅在開發(fā)模式下)並選擇“Inspect Element”時:
II - 現(xiàn)在…深入研究 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<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>;
如你所見,我已經(jīng)在 Svelte 中加入了 4 個 JavaScript 套件(除了已經(jīng)提到的 Tailwindcss Daisyui):
- svelte-copy,可以更輕鬆地將使用者名稱和密碼複製到剪貼簿。
- svelte-i18n,用於 i18n 處理,即允許使用者更改應(yīng)用程式的語言。
- svelte-spa-router,Svelte 的一個小型路由庫,它可以更輕鬆地更改應(yīng)用程式視窗中的視圖,因?yàn)樵谶@種情況下,使用由SvelteKit。
- sweetalert2,基本上用它來輕鬆快速地建立模態(tài)框/對話框。
每個 SPA 的入口點(diǎn)都是 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 產(chǎn)生的框架中的內(nèi)容。 svelte-i18n 庫要求在main.js/ts 文件中註冊包含翻譯的JSON 文件,同時設(shè)定fallback/initial 語言(儘管我們'你會看到,稍後將根據(jù)使用者選擇的偏好進(jìn)行操作)。包含翻譯的 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<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>;
我發(fā)現(xiàn)這個函式庫的系統(tǒng)對於促進(jìn) Svelte 應(yīng)用程式的翻譯非常簡單和方便(您可以查閱其文件以了解更多詳細(xì)資訊):
/* package.json */ ... }, "dependencies": { "svelte-copy": "^2.0.0", "svelte-i18n": "^4.0.1", "svelte-spa-router": "^4.0.1", "sweetalert2": "^11.14.5" } ...
您也可以使用像這樣的網(wǎng)站,它將幫助您將 JSON 檔案翻譯成不同的語言。然而,問題是,當(dāng)您使用 $format 填充 .svelte 檔案時,您必須手動追蹤它們,這是乏味且容易出錯的。我不知道有什麼方法可以自動完成這項(xiàng)任務(wù),如果有人知道,我會很感興趣,如果你能讓我知道? ……否則,我必須想出某種腳本來完成這項(xiàng)工作。
與任何 Svelte 應(yīng)用程式一樣,建立介面的下一步是 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,它是編譯應(yīng)用程式時自動產(chǎn)生的 綁定,並被宣告為 struct App 的公共方法(請參閱本系列的第一部分)。該函數(shù)查詢資料庫,如果其中註冊了主密碼,它會認(rèn)為使用者已經(jīng)註冊(它會傳回一個包含布林值的承諾),要求他輸入所述密碼以允許他存取其餘內(nèi)容的意見。如果資料庫中沒有主密碼,則該使用者被視為“新”,要求他產(chǎn)生自己的密碼以首次進(jìn)入應(yīng)用程式。
最後,在安裝 Login.svelte 元件時,我們做了一些對應(yīng)用程式的其餘部分很重要的事情。儘管 svelte-i18n 庫強(qiáng)制我們聲明初始語言代碼,但正如我們已經(jīng)看到的,在安裝 Login.svelte 時,我們要求資料庫(使用 GetLanguage 綁定)檢查是否保存了語言代碼。如果資料庫傳回空字串,即沒有配置為使用者首選項(xiàng)的語言,svelte-i18n 將使用配置為initialLocale 的值。如果配置了一種語言,則將設(shè)定該語言(locale.set(result);) 並發(fā)出「change_titles」事件,標(biāo)題列和應(yīng)用程式本機(jī)對話方塊的翻譯標(biāo)題將傳遞到該事件供後端處理:
/* 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,綁定到獲取用戶輸入內(nèi)容的輸入的狀態(tài),首先由onLogin 檢查它是否至少有6 個字符,並且它們都是ASCII 字符,即,通過這個小函數(shù)const isAscii = (str: string): boolean => 它們只有1 個位元組長(請參閱本系列第一部分的原因)。 /^[x00-x7F] $/.test(str);.如果檢查失敗,函數(shù)將傳回並向使用者顯示警告 toast。之後,如果資料庫中沒有保存主密碼(isLogin = false),則SaveMasterPassword 函數(shù)將保存使用者輸入的任何內(nèi)容(Wails 產(chǎn)生的綁定);如果Promise 成功解析(傳回uuid 字串作為資料庫中儲存的記錄的Id),使用者將被svelte-spa-router 帶到主頁視圖庫的推送方法。相反,如果密碼通過了長度檢查,且不存在非ASCII 字符,且資料庫中存在主密碼(isLogin = true),則CheckMasterPassword 函數(shù)將根據(jù)儲存的密碼驗(yàn)證其身份,或?qū)⑹褂谜邘У街饕晥D(promise 為true 解決)或顯示toast 表示輸入的密碼不正確。
應(yīng)用程式的中心視圖,同時也是最複雜的視圖是主頁視圖。它的HTML 實(shí)際上分為3 個元件:一個帶有搜尋輸入的頂部按鈕列(TopActions 元件)、一個底部按鈕欄(BottomActions 元件)以及一個中心區(qū)域,其中使用以下命令顯示已儲存密碼條目的總數(shù)或這些項(xiàng)目的清單:可捲動視窗(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<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>;
也就是說,它使搜尋狀態(tài)(searchTerms)成為一個空字串,這樣如果有任何搜尋詞,它就會被重置,從而顯示整個列表。另一方面,它會切換 showList 狀態(tài)(props TopActions 中的 isEntriesList),以便父元件顯示或隱藏清單。
如我們在上圖中所看到的,兩個子元件與父元件的 searchTerms 狀態(tài)共用相同的 props。 TopActions 元件會擷取使用者的輸入,並將其作為狀態(tài)傳遞給父元件 Home,而父元件 Home 又將其作為 props 傳遞給其子元件 EntriesList。
顯示完整清單或按使用者輸入的搜尋字詞過濾的清單的主要邏輯按預(yù)期由 EntriesList 元件執(zhí)行:
... . ├── 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)並維護(hù)一個狀態(tài)(讓條目:models.PasswordEntry[] = $state([]);)。當(dāng)根據(jù)使用者的請求安裝該元件時,後端會被要求提供已儲存密碼條目的完整清單。如果沒有搜尋字詞,則將其儲存在狀態(tài)中;如果有,則對獲得的陣列進(jìn)行簡單過濾,並將其保存在狀態(tài):
// 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>;
在顯示的清單中,使用者可以執(zhí)行 2 個操作。第一個是顯示條目的詳細(xì)信息,當(dāng)他點(diǎn)擊相應(yīng)的按鈕時執(zhí)行:onclick={() =>推送(`/details/${entry.Id}`)}?;旧?,我們呼叫路由庫的 Push 方法將使用者帶到詳細(xì)資訊視圖,但傳遞與相關(guān)項(xiàng)目相對應(yīng)的 Id 參數(shù)。
使用者可以執(zhí)行的另一個操作是從清單中刪除項(xiàng)目。如果他單擊相應(yīng)的按鈕,將顯示一個確認(rèn)彈出窗口,並呼叫 showAlert 函數(shù)。函數(shù)依序呼叫 showWarning,它實(shí)際上是 sweetalert2 函式庫的抽象層(呼叫 sweetalert2 函式庫的所有函數(shù)都在 frontend/src/lib/popups/popups.ts 中)。如果使用者確認(rèn)刪除操作,則呼叫DeleteEntry綁定(將其從資料庫中刪除),反過來,如果它傳回的promise得到解析,則呼叫deleteItem(將其從儲存在條目狀態(tài)的陣列中刪除) :
/* 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 並且僅限於將使用者重新導(dǎo)向到各種視圖(Settings、About 或 AddPassword)。
AddPassword 和 EditPassword 視圖的邏輯非常相似,也與 Login 視圖類似。兩者都不允許使用者在文字輸入的開頭和結(jié)尾輸入空格,並遵循與登入視圖相同的策略,要求密碼長度至少為 6 個 ASCII 字元?;旧希鼈兊呐c眾不同之處在於它們調(diào)用與需要執(zhí)行的操作相關(guān)的由 Wails 產(chǎn)生的連結(jié):
/* 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
另一個有點(diǎn)複雜的視圖是「設(shè)定」。它有一個語言元件,它從其父元件(設(shè)定)接收 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 事件中,它接收一個函數(shù) (handleChange),該函數(shù)執(zhí)行 3 件事:
- 使用 svelte-i18n 函式庫設(shè)定前端語言
- 發(fā)出一個事件(“change_titles”),以便Wails運(yùn)行時更改應(yīng)用程式標(biāo)題列的標(biāo)題以及相關(guān)的選擇目錄和選擇檔案對話框的標(biāo)題到上一個操作
- 將使用者選擇的語言保存在資料庫中,以便下次啟動應(yīng)用程式時,它將開啟配置為該語言的。
傳回「設(shè)定」視圖,其整個操作由一系列傳送到後端或從後端接收的事件控制。最簡單的是退出按鈕:當(dāng)使用者點(diǎn)擊它時,會在後端觸發(fā)並偵聽退出事件,然後應(yīng)用程式關(guān)閉(onclick={() => EventsEmit("quit")})。 提示 通知使用者 Escape 鍵(快速鍵)執(zhí)行相同的操作,正如我們已經(jīng)解釋過的。
重置按鈕呼叫顯示彈出視窗的函數(shù):
... . ├── 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 視圖,顯示指示操作成功的模式。
剩下的另外兩個操作彼此類似,所以讓我們看看導(dǎo)入資料。
如果使用者點(diǎn)擊對應(yīng)的按鈕,則會發(fā)出事件(onclick={() => EventsEmit("import_data")}),該事件在後端監(jiān)聽。收到後,將開啟本機(jī)選擇檔案對話方塊以允許使用者選擇備份檔案。如果使用者選擇文件,包含路徑(fileLocation)的變數(shù)將不包含空字串,這將在後端觸發(fā)一個事件(“enter_password”),該事件現(xiàn)在在前端偵聽,然後顯示新的彈出視窗詢問匯出時使用的主密碼。同樣,前端將發(fā)出另一個事件(「密碼」),其中攜帶使用者輸入的主密碼。當(dāng)後端接收到這個新事件時,會執(zhí)行 Db 套件的 ImportDump 方法,該方法執(zhí)行從使用者選擇的備份檔案中讀取和還原 DB 中的資料的工作。結(jié)果,發(fā)出一個新事件(“imported_data”),該事件將其執(zhí)行結(jié)果(成功或不成功)作為附加資料攜帶。前端收到事件後只需執(zhí)行 2 個任務(wù):
- 如果結(jié)果成功,請?jiān)O(shè)定備份檔案中儲存的語言並顯示指示操作成功的模式
- 如果由於某種原因無法完成匯入,請顯示錯誤及其原因。
所有這些在程式碼邏輯中比用文字解釋要容易得多? :
... . ├── 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 運(yùn)行時函數(shù) (EventsOn) 傳回一個函數(shù),該函數(shù)在呼叫時會取消所述偵聽器。當(dāng)組件被銷毀時,取消所述監(jiān)聽器是很方便的。與React 類似,onMount 鉤子可以透過讓監(jiān)聽器傳回一個清理函數(shù)來「清理」它們,在這種情況下,該函數(shù)將呼叫EventsOn 傳回的所有函數(shù),我們已採取預(yù)防措施將其保存在單獨(dú)的文件中。變數(shù):
// 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>;
為了完成對我們應(yīng)用程式前端部分的審查,只需介紹一下「關(guān)於」組件即可。這幾乎沒有邏輯,因?yàn)樗鼉H限於顯示有關(guān)應(yīng)用程式的信息,就像常見的 about 一樣。然而,應(yīng)該說,正如我們所看到的,該視圖顯示了指向應(yīng)用程式儲存庫的連結(jié)。顯然,在普通網(wǎng)頁中,錨標(biāo)記() 將使我們導(dǎo)航到相應(yīng)的鏈接,但在桌面應(yīng)用程式中,如果Wails 在運(yùn)行時沒有為此提供特定函數(shù)(BrowserOpenURL),則不會發(fā)生這種情況:
/* package.json */ ... }, "dependencies": { "svelte-copy": "^2.0.0", "svelte-i18n": "^4.0.1", "svelte-spa-router": "^4.0.1", "sweetalert2": "^11.14.5" } ...
這會將二進(jìn)位檔案建置到 build/bin 資料夾中。但是,要選擇其他建置選項(xiàng)或執(zhí)行交叉編譯,您可能需要查看 Wails CLI 文件。
對於這個應(yīng)用程序,我想我已經(jīng)在本系列的第一部分中提到過,我只關(guān)注Windows和Linux的編譯。為了以舒適的方式執(zhí)行這些任務(wù)(由於測試,這些任務(wù)是重複的),我創(chuàng)建了一些小腳本和一個「協(xié)調(diào)」它們的 Makefile。
make create-bundles 指令為Linux 版本建立一個.tar.xz 壓縮文件,其中包含應(yīng)用程式和一個充當(dāng)安裝執(zhí)行檔的「安裝程式」的Makefile,一個用於在開始功能表以及對應(yīng)的應(yīng)用程式圖示。對於 Windows 版本,二進(jìn)位檔案只是在名為 dist/ 的資料夾中壓縮為 .zip。但是,如果您喜歡跨平臺自動構(gòu)建,Wails 有一個 Github Actions,允許您上傳(預(yù)設(shè)選項(xiàng))生成的工件到您的儲存庫。
請注意,如果您在運(yùn)行時使用make create-bundles 命令,它將呼叫Wails 命令wails build -clean -upx (對於Linux)或wails build -skipbindings -s -platform windows/amd64 - upx(對於Windows )。 -upx 標(biāo)誌是指使用您應(yīng)該安裝在電腦上的最後,請注意,建置腳本會自動將目前儲存庫標(biāo)籤新增至「關(guān)於」視圖,並在建置後將其值還原為預(yù)設(shè)值 (DEV_VERSION)。UPX 實(shí)用程式來壓縮二進(jìn)位檔案??蓤?zhí)行檔案體積小的部分秘密是由於該實(shí)用程式所做的出色的壓縮工作。
?。∵@兩篇文章比我想像的還要長!但我希望您喜歡它們,最重要的是,它們可以幫助您思考新專案。在程式設(shè)計中學(xué)習(xí)一些東西就像這樣...
請記住,您可以在此 GitHub 儲存庫中找到所有應(yīng)用程式程式碼。
我相信我會在其他貼文中見到你。編碼愉快? ! !
以上是極簡密碼管理器桌面應(yīng)用程式:進(jìn)軍 Golang 的 Wails 框架(第 2 部分)的詳細(xì)內(nèi)容。更多資訊請關(guān)注PHP中文網(wǎng)其他相關(guān)文章!

熱AI工具

Undress AI Tool
免費(fèi)脫衣圖片

Undresser.AI Undress
人工智慧驅(qū)動的應(yīng)用程序,用於創(chuàng)建逼真的裸體照片

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

Clothoff.io
AI脫衣器

Video Face Swap
使用我們完全免費(fèi)的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱門文章

熱工具

記事本++7.3.1
好用且免費(fèi)的程式碼編輯器

SublimeText3漢化版
中文版,非常好用

禪工作室 13.0.1
強(qiáng)大的PHP整合開發(fā)環(huán)境

Dreamweaver CS6
視覺化網(wǎng)頁開發(fā)工具

SublimeText3 Mac版
神級程式碼編輯軟體(SublimeText3)

Golang主要用於後端開發(fā),但也能在前端領(lǐng)域間接發(fā)揮作用。其設(shè)計目標(biāo)聚焦高性能、並發(fā)處理和系統(tǒng)級編程,適合構(gòu)建API服務(wù)器、微服務(wù)、分佈式系統(tǒng)、數(shù)據(jù)庫操作及CLI工具等後端應(yīng)用。雖然Golang不是網(wǎng)頁前端的主流語言,但可通過GopherJS編譯成JavaScript、通過TinyGo運(yùn)行於WebAssembly,或搭配模板引擎生成HTML頁面來參與前端開發(fā)。然而,現(xiàn)代前端開發(fā)仍需依賴JavaScript/TypeScript及其生態(tài)。因此,Golang更適合以高性能後端為核心的技術(shù)棧選擇。

要構(gòu)建一個GraphQLAPI在Go語言中,推薦使用gqlgen庫以提高開發(fā)效率。 1.首先選擇合適的庫,如gqlgen,它支持根據(jù)schema自動生成代碼;2.接著定義GraphQLschema,描述API的結(jié)構(gòu)和查詢?nèi)肟?,如定義Post類型和查詢方法;3.然後初始化項(xiàng)目並生成基礎(chǔ)代碼,實(shí)現(xiàn)resolver中的業(yè)務(wù)邏輯;4.最後將GraphQLhandler接入HTTPserver,通過內(nèi)置Playground測試API。注意事項(xiàng)包括字段命名規(guī)範(fàn)、錯誤處理、性能優(yōu)化及安全設(shè)置等,確保項(xiàng)目可維護(hù)性

安裝Go的關(guān)鍵在於選擇正確版本、配置環(huán)境變量並驗(yàn)證安裝。 1.前往官網(wǎng)下載對應(yīng)系統(tǒng)的安裝包,Windows使用.msi文件,macOS使用.pkg文件,Linux使用.tar.gz文件並解壓至/usr/local目錄;2.配置環(huán)境變量,在Linux/macOS中編輯~/.bashrc或~/.zshrc添加PATH和GOPATH,Windows則在系統(tǒng)屬性中設(shè)置PATH為Go的安裝路徑;3.使用goversion命令驗(yàn)證安裝,並運(yùn)行測試程序hello.go確認(rèn)編譯執(zhí)行正常。整個流程中PATH設(shè)置和環(huán)

sync.WaitGroup用於等待一組goroutine完成任務(wù),其核心是通過Add、Done、Wait三個方法協(xié)同工作。 1.Add(n)設(shè)置需等待的goroutine數(shù)量;2.Done()在每個goroutine結(jié)束時調(diào)用,計數(shù)減一;3.Wait()阻塞主協(xié)程直到所有任務(wù)完成。使用時需注意:Add應(yīng)在goroutine外調(diào)用、避免重複Wait、務(wù)必確保Done被調(diào)用,推薦配合defer使用。常見於並發(fā)抓取網(wǎng)頁、批量數(shù)據(jù)處理等場景,能有效控制並發(fā)流程。

使用Go的embed包可以方便地將靜態(tài)資源嵌入二進(jìn)制,適合Web服務(wù)打包HTML、CSS、圖片等文件。 1.聲明嵌入資源需在變量前加//go:embed註釋,如嵌入單個文件hello.txt;2.可嵌入整個目錄如static/*,通過embed.FS實(shí)現(xiàn)多文件打包;3.開發(fā)時建議通過buildtag或環(huán)境變量切換磁盤加載模式以提高效率;4.注意路徑正確性、文件大小限制及嵌入資源的只讀特性。合理使用embed能簡化部署並優(yōu)化項(xiàng)目結(jié)構(gòu)。

音視頻處理的核心在於理解基本流程與優(yōu)化方法。 1.其基本流程包括採集、編碼、傳輸、解碼和播放,每個環(huán)節(jié)均有技術(shù)難點(diǎn);2.常見問題如音畫不同步、卡頓延遲、聲音噪音、畫面模糊等,可通過同步調(diào)整、編碼優(yōu)化、降噪模塊、參數(shù)調(diào)節(jié)等方式解決;3.推薦使用FFmpeg、OpenCV、WebRTC、GStreamer等工具實(shí)現(xiàn)功能;4.性能管理方面應(yīng)注重硬件加速、合理設(shè)置分辨率幀率、控制並發(fā)及內(nèi)存洩漏問題。掌握這些關(guān)鍵點(diǎn)有助於提升開發(fā)效率和用戶體驗(yàn)。

搭建一個用Go編寫的Web服務(wù)器並不難,核心在於利用net/http包實(shí)現(xiàn)基礎(chǔ)服務(wù)。 1.使用net/http啟動最簡服務(wù)器:通過幾行代碼註冊處理函數(shù)並監(jiān)聽端口;2.路由管理:使用ServeMux組織多個接口路徑,便於結(jié)構(gòu)化管理;3.常見做法:按功能模塊分組路由,並可用第三方庫支持複雜匹配;4.靜態(tài)文件服務(wù):通過http.FileServer提供HTML、CSS和JS文件;5.性能與安全:啟用HTTPS、限制請求體大小、設(shè)置超時時間以提升安全性與性能。掌握這些要點(diǎn)後,擴(kuò)展功能將更加容易。

select加default的作用是讓select在沒有其他分支就緒時執(zhí)行默認(rèn)行為,避免程序阻塞。 1.非阻塞地從channel接收數(shù)據(jù)時,若channel為空,會直接進(jìn)入default分支;2.結(jié)合time.After或ticker定時嘗試發(fā)送數(shù)據(jù),若channel滿則不阻塞而跳過;3.防止死鎖,在不確定channel是否被關(guān)閉時避免程序卡?。皇褂脮r需注意default分支會立即執(zhí)行,不能濫用,且default與case互斥,不會同時執(zhí)行。
