更輕更快 - Svelte 框架指南

已發表: 2022-03-11

Web 應用程序日益流行。 它們是一個不斷發展的世界,人們選擇它是因為它的簡單性、速度和跨平台可用性。 單頁應用程序 (SPA) 在此過程中發揮了重要作用。 Angular、Vue.js 和 React 等框架可幫助開發人員在短時間內提供最佳用戶體驗,從而使代碼具有可支持性和可擴展性。 這些工具長期以來一直在該領域最受歡迎,與新創建的軟件包相比具有許多優勢。 在 SPA 世界中,它感覺就像是寡頭壟斷。 然而,一群瞄準這個市場的前瞻性開發商可能會與一個嚴肅的競爭對手——Svelte 一起進入。

Svelte 是一種構建用戶界面的新方法。 讓我們通過創建一個通用的登錄表單來深入探索是什麼讓它如此新鮮。

建築學

Svelte 的架構比任何其他庫都快。 它是通過改變加載框架以構建虛擬 DOM 的步驟來實現的。 它不是在運行過程中使用工具,而是在構建階段編譯為 vanilla JS,因此應用程序不需要依賴項即可啟動。

苗條其他 SPA 庫(React、Vue.js、Angular 等)

1.打開一個網站
2.使用純JS渲染頁面

1.打開一個網站
2.等待構建虛擬DOM的代碼加載完成
3.使用庫渲染頁面

上表描述了為什麼 Svelte 在啟動性能方面絕對是贏家。 這不是通過任何優化獲得的,而是通過使用可用的瀏覽器 JavaScript 編譯器而不是輔助編譯器獲得的。

安裝

Svelte 安裝非常簡單,使用起來非常愉快。 第一步是下載項目的模板:

 npx degit sveltejs/template svelte-login-form

完成上述命令意味著我們有了一個 Svelte 項目模板。 目前它是空的,還沒有安裝所需的 NPM 包。 讓我們解決這個問題。

 cd svelte-login-form npm install

現在應用程序已準備好使用以下命令啟動:

 npm run dev

結構

任何 Svelte 組件都可能包含以下部分:

  • 腳本
  • 風格
  • 模板

讓我們看一下src/App.svelte文件中的示例。

 <script> export let name; </script> <style> h1 { color: purple; } </style> <h1>{name}</h1>

上面的代碼正好包含三個部分:

  1. script標籤,這是一個可選的 JavaScript 塊,其中包含應在組件內使用的變量和函數聲明。

  2. style標籤,這是另一個可選塊。 除了一個重要的區別之外,它很像一個常見的 HTML 樣式標籤。 此塊中描述的規則僅適用於此組件。 將樣式應用於p元素不會影響頁面上的所有段落。 這太棒了,因為您不必想出類名,而且您永遠不會意外地覆蓋另一個規則。

  3. 最後一個也是唯一需要的塊是一個模板塊——在本例中是一個h1標籤。 這是您的組件的演示/視圖。 它與樣式和腳本塊緊密綁定,因為它們決定了視圖的樣式和行為方式。

Svelte 是一個試圖將模塊化引入前端遊戲的庫。 它不僅在分離不同的組件方面而且在隔離邏輯、視圖和模板方面保持這種模塊化。

回到我們正在構建的登錄表單,讓我們在src文件夾中創建一個新文件LoginForm.svelte ,其內容如下:

 <style> form { background: #fff; padding: 50px; width: 250px; height: 400px; display: flex; flex-direction: column; justify-content: center; align-items: center; box-shadow: 0px 20px 14px 8px rgba(0, 0, 0, 0.58); } label { margin: 10px 0; align-self: flex-start; font-weight: 500; } input { border: none; border-bottom: 1px solid #ccc; margin-bottom: 20px; transition: all 300ms ease-in-out; width: 100%; } input:focus { outline: 0; border-bottom: 1px solid #666; } button { margin-top: 20px; background: black; color: white; padding: 10px 0; width: 200px; border-radius: 25px; text-transform: uppercase; font-weight: bold; cursor: pointer; transition: all 300ms ease-in-out; } button:hover { transform: translateY(-2.5px); box-shadow: 0px 1px 10px 0px rgba(0, 0, 0, 0.58); } h1 { margin: 10px 20px 30px 20px; font-size: 40px; } </style> <form> <h1></h1> <label>Email</label> <input name="email" placeholder="[email protected]" /> <label>Password</label> <input name="password" type="password" placeholder="password" /> <button type="submit">Log in </button> </form>

這是一個愚蠢的樣式組件,我們稍後會變得更智能。 要在我們的網站上看到這個組件,我們應該在根組件 - App 中呈現它。 讓我們去編輯src/App.svelte ,讓它看起來像這樣:

 <script> import LoginForm from "./LoginForm.svelte"; </script> <style> section { height: 100vh; width: 100%; display: flex; justify-content: center; align-items: center; background: linear-gradient(to right, #cd76e2, #e358ab); } </style> <section> <LoginForm /> </section>

如果一切都正確完成並且應用程序仍在運行,我們的表單將出現在localhost:5000 。 讓我們通過讓表單更智能來提升我們的 Svelte 技能。

進入有狀態

Svelte 中的任何組件都可以有自己的狀態。 狀態是可以在模板內部使用的一個特殊變量或一組特殊變量。 為什麼我說“特別”? 每當更改此類變量時,都會通知模板並以最新狀態呈現內容。 這允許應用程序非常快速地對用戶交互做出反應。

我們將聲明emailpassword狀態變量,其中將存儲適當字段的表單值。 這意味著我們的emailpassword變量將始終與表單值同步,因此我們隨時準備提交這些值,而不必擔心提交值與表單中的實際值之間存在任何差異。

 <script> let email = ""; let password = ""; let isLoading = false; const handleSubmit = () => { isLoading = true; // Simulate network request setTimeout(() => { isLoading = false; // Authorize the user }, 1000); }; </script> <style> /* Style is unchanged */ </style> <form on:submit|preventDefault={handleSubmit}> <h1></h1> <label>Email</label> <input name="email" placeholder="[email protected]" bind:value={email} /> <label>Password</label> <input name="password" type="password" bind:value={password} /> {#if isLoading}Logging in...{:else}Log in {/if} </form>

狀態變量看起來像普通的 JavaScript 變量,但是為了使它們與表單值同步(將它們綁定到表單字段),有必要使用bind:value指令。 還有一些不熟悉的地方:

  • on:submit|preventDefault是防止默認事件行為的簡寫。 這種方式比每次都必須編寫e.preventDefault()更舒服。

  • {#if isLoading}Logging in...{:else}Log in {/if}是 Svelte 模板語法的一部分。 由於模板塊中沒有 JS,因此使用 ifs、循環等有一種特殊的語法。

最後,讓我們使用可用的選項通過使用狀態來向我們的表單添加驗證。 可以通過創建另一個狀態變量errors來實現,當表單提交無效值時,該變量將被錯誤填充。

 <script> let email = ""; let password = ""; let isLoading = false; let errors = {}; const handleSubmit = () => { errors = {}; if (email.length === 0) { errors.email = "Field should not be empty"; } if (password.length === 0) { errors.password = "Field should not be empty"; } if (Object.keys(errors).length === 0) { isLoading = true; // Simulate network request setTimeout(() => { isLoading = false; // Authorize the user }, 1000); } }; </script> <style> // Previous styles unchanged .errors { list-style-type: none; padding: 10px; margin: 0; border: 2px solid #be6283; color: #be6283; background: rgba(190, 98, 131, 0.3); } </style> <form on:submit|preventDefault={handleSubmit}> <h1></h1> <label>Email</label> <input name="email" placeholder="[email protected]" bind:value={email} /> <label>Password</label> <input name="password" type="password" bind:value={password} /> <button type="submit"> {#if isLoading}Logging in...{:else}Log in {/if} </button> {#if Object.keys(errors).length > 0} <ul class="errors"> {#each Object.keys(errors) as field} <li>{field}: {errors[field]}</li> {/each} </ul> {/if} </form> 
登錄表單錯誤

表格幾乎完成了。 唯一剩下的是成功驗證後的成功消息。

讓我們創建一個用於跟踪成功提交的狀態變量,默認情況下為false 。 成功提交表單後,此變量的值應設置為true

 let isSuccess = false;

處理表單提交的函數也應該更改為在操作成功後遵循切換isSuccess的邏輯。

 const handleSubmit = () => { errors = {}; if (email.length === 0) { errors.email = "Field should not be empty"; } if (password.length === 0) { errors.password = "Field should not be empty"; } if (Object.keys(errors).length === 0) { isLoading = true; // Simulate network request setTimeout(() => { isLoading = false; isSuccess = true; // Authorize the user }, 1000); } };

此修改使表單在提交完成後立即進入成功狀態。

但是如果你檢查你的開發服務器,你不會發現表單的行為有任何變化。 我們更改了代碼,但還沒有觸及模板。 我們需要在模板中添加說明,以便在用戶成功登錄時顯示成功消息。Svelte 的模板語法允許我們輕鬆實現:

 <form on:submit|preventDefault={handleSubmit}> {#if isSuccess} <div class="success"> <br /> You've been successfully logged in. </div> {:else} <h1></h1> <label>Email</label> <input name="email" placeholder="[email protected]" bind:value={email} /> <label>Password</label> <input name="password" type="password" bind:value={password} /> <button type="submit"> {#if isLoading}Logging in...{:else}Log in {/if} </button> {#if Object.keys(errors).length > 0} <ul class="errors"> {#each Object.keys(errors) as field} <li>{field}: {errors[field]}</li> {/each} </ul> {/if} {/if} </form>

帶有屬性的抽象

我們已經整理了有關內部組件狀態的所有內容。 現在是時候了解稱為屬性或“props”的外部依賴項了。 Props 是傳遞給組件的輸入或參數,用於向組件描述應該出現的內容或組件的行為方式。

除了關鍵字export之外,屬性的聲明看起來與狀態相似。

 <script> export let answer; </script> <p>The answer is {answer}</p>
 <script> import Nested from './Nested.svelte'; </script> <Nested answer={42}/>

都是關於屬性的。 聲明並通過 - 所有你需要知道的使用道具。

但是這些屬性如何應用於登錄表單組件呢? Props 可以通過將提交函數提取到一個屬性中來使我們的登錄表單更加通用。 它將允許您將此組件與您需要的任何提交操作一起使用(請求測試服務器,請求實際服務器等)。 這個 prop 將被稱為submit並且將是一個函數,如果提交操作成功則返回已解決的承諾,如果有錯誤則返回被拒絕的承諾。 讓我們通過上面給出的示例聲明 prop:

 export let submit;

登錄表單中的提交處理程序也應該被編輯以使用新的submit屬性。

 const handleSubmit = () => { errors = {}; if (email.length === 0) { errors.email = "Field should not be empty"; } if (password.length === 0) { errors.password = "Field should not be empty"; } if (Object.keys(errors).length === 0) { isLoading = true; submit({ email, password }) .then(() => { isSuccess = true; isLoading = false; }) .catch(err => { errors.server = err; isLoading = false; }); } };

該組件似乎已準備就緒。 但是,如果您返回表單並嘗試提交它,您會注意到按鈕的狀態從加載開始並沒有改變。 此外,控制台中有一個異常: Uncaught TypeError: submit is not a function 。 當然,我們聲明了 prop 但忘了傳遞它。 讓我們在 app 組件中聲明一個函數並將其傳遞給登錄表單。

 const submit = ({ email, password }) => new Promise((resolve, reject) => setTimeout(resolve, 1000));
 <section> <LoginForm submit={submit} /> </section>

現在表單正在按預期工作。 它既可以顯示錯誤,也可以通知用戶登錄是否成功。

登錄表單成功

上下文共享

似乎列出了構建應用程序所需的一切。 有了屬性和內部狀態,我們就可以開始了。 不過,這只是部分正確。 這兩點使設計高複雜度的 SPA 成為可能。 但是,如果您嘗試在許多不同的組件之間共享數據,您會發現這非常困難。

最簡單的例子是擁有一個全局可訪問的user變量。 許多組件應該根據用戶的角色、年齡、狀態等來改變與用戶相關的行為。但是,通過使用 props 將用戶傳遞給應用程序中的每個組件來重複我們自己並不是 DRY。

Svelte 對此有一個解決方案:上下文 API。

上下文 API 為組件提供了一種機制,可以相互“對話”,而無需將數據和函數作為道具傳遞或分派大量事件。 這是一項高級功能,但很有用。

讓我們將用戶上下文添加到我們正在設計的登錄表單中。 在src文件夾中創建一個文件userContext.js ,內容如下:

 export const key = "userContext"; export const initialValue = null;

key是上下文的唯一標識符,因為應用程序可能有無限數量的不同上下文必須保持可訪問性。 initialValue只是設置之前上下文的默認值。

下一步是將上下文添加到我們的應用程序中。 導航到App.svelte文件並添加 2 個導入語句:

 import { onMount, setContext } from "svelte"; import { key as userContextKey, initialValue as userContextInitialValue } from "./userContext";

查看上面的代碼,您可能想知道我們從svelte包中導入了什麼。 onMount是一個輔助函數,需要回調函數作為參數。 此回調將在當前組件正在掛載時執行(在加載組件的最開始時)。 setContext是上下文的設置器函數。 它需要上下文的鍵和一個新值作為其參數。

讓我們使用onMount函數來設置上下文的默認值:

 onMount(() => { setContext(userContextKey, userContextInitialValue); });

並修改submit函數以設置用戶上下文:

 const submit = ({ email, password }) => new Promise((resolve, reject) => { setTimeout(() => { setContext(userContextKey, { name: "Foo", lastName: "Bar", email: "[email protected]" }); resolve(); }, 1000); });

而已。 成功的提交會將用戶上下文更改為可以由上下文獲取器getContext訪問的假用戶對象:

 <script> import { getContext } from 'svelte'; import { key as userContextKey } from "./userContext"; const user = getContext(key); </script>

概括

Svelte 是一個強大的工具,具有高性能和靈活的 API。 除了本文介紹的基礎知識外,Svelte 還具有以下開箱即用的功能:

  • 反應式聲明和陳述
  • 等待模板塊
  • 維度綁定
  • 像 Redux 這樣的全球商店
  • 動畫和過渡助手
  • 調試助手

總而言之,Svelte 是一個很棒的庫,可以滿足構建 SPA 的所有需求等等。 它可以與市場上最大的參與者競爭,甚至獲勝。 不過,它現在可以使用的是前端開發人員社區的支持。

注意:本文中的所有代碼都可以在teimurjan/svelte-login-form GitHub 存儲庫中找到。 登錄表單的演示可在此處獲得。