簡簡單單纔是真,初試 Svelte

做者:LeanCloud weakishhtml

最近團隊內部須要一個上傳文件的 web 小工具,須要寫一個簡單的前端頁面。 像這樣的小工具,若是引入 React 和 Vue,彷佛過重了,因此想嘗試下 Svelte 這個無框架之框架(最終會編譯成不帶框架的 JS 代碼)。前端

閒話少敘,直接上手。 首先,生成模板項目:node

npx degit sveltejs/template fileup

長期以來 Svelte 的一大痛點是不支持 TypeScript,不過去年開始也加入了 TypeScript 支持。 固然,像這樣簡單的小工具,直接用 JavaScript 寫也沒什麼差異。 不過我仍是打算用下 TypeScript。react

和其餘一些模板生成工具不一樣,Svelte 官方沒有提供單獨的 TypeScript 模板項目倉庫,也不會在生成模板的過程當中讓你選擇是用 TypeScript 仍是 JavaScript,而是在生成模板項目後,運行一個腳本把項目轉成 TypeScript 項目:git

node scripts/setupTypeScript.js

轉換以後,執行常規的依賴安裝步驟,就能夠運行了:github

npm install
npm run dev

項目使用 npm 管理依賴,使用 rollup 打包,這些都不用操心,對於簡單項目而言,只需修改 src/App.svelte 便可,模板、樣式、交互邏輯都寫在這一個文件中(這和 Vue 有點像):web

<script lang="ts">
// TypeScript 代碼
</script>

<style>
/* CSS 代碼 */
</style>

<!-- Svelte 模板代碼 -->

先來編寫上傳界面,google 一下,找到一個現成的組件 svelte-file-dropzonetypescript

npm i svelte-file-dropzone

將它加入依賴,在 App.sveltescript 部分引入,並編寫相應的 TypeScript 代碼和 Svelet 模板代碼:npm

<script lang="ts">
import Dropzone from "svelte-file-dropzone";

let files: File[]
function handleFilesSelect(e) {
    files = e.detail.acceptedFiles
}
</script>

<Dropzone on:drop={handleFilesSelect}>
  Click to select files, or drag and drop some files here.
</Dropzone>

獲取選定的文件以後,再上傳文件。 文件會上傳到 LeanCloud,LeanCloud 是一家 BaaS 雲服務商,也提供文件託管服務。 「svelte」的意思是「slender and elegant」,和「lean」也算是近義詞,從這個角度說,這兩個聽起來還挺配的。編程

一樣,咱們先把 LeanCloud 的 JavaScript SDK 加入依賴:

npm i leancloud-storage

而後初始化 SDK:

import LC from "leancloud-storage";

const appId = "your app id";
const appKey = "your app key";
const serverURL = "https://your-custom-domain.example.com";

LC.init({
    appId,
    appKey,
    serverURL,
});

appIDappKey 能夠從 「LeanCloud 控制檯 > 設置 > 應用 Keys」查看,serverURL 是在 LeanCloud 綁定的域名。 固然,這須要首先在 LeanCloud 註冊帳號並建立應用。 應有關部門的要求,LeanCloud 國內版須要綁定已備案的域名,若是不想綁域名或者域名沒有備案,能夠用 LeanCloud 國際版

國際版初始化時無需傳入 serverURL

LC.init({
    appId,
    appKey
});

上傳文件很是簡單,只需兩行代碼:

async function uploadFile(toUpload): Promise<String> {
    const uploaded = await new LC.File(toUpload.name, toUpload).save();
    return uploaded.get("url");
}

上面的代碼中,用 new LC.File(文件名, 文件) 構建文件後,調用 save 方法保存到雲端,以後就能夠經過 url 屬性獲得能夠訪問該文件的 URL。

到目前爲止,咱們已經完成了最核心的功能,選取文件和上傳文件,只剩下最後一步,把獲取的 URL 顯示給用戶了。

{#if files}
  <h2>Files</h2>
  {#each files as file, i}
    {#await uploadFile(file)}
      <p>Uploading {file.name} ({file.size} bytes) ...</p>
    {:then url}
      <p>Uploaded:
        <code>{url ?? ""}</code>
      </p>
    {:catch error}
      <pre>{error}</pre>
    {/await}
  {/each}
{/if}

這裏用到了 Svelte 的模板語法。 估計是爲了節省寫解析器的功夫,Svelte 的模板語法看起來有點彆扭(#if/if:catch),可是忽略這些視覺上小小的不適,總體上仍是清晰易讀的。 先用 if 判斷是否選取了文件,只有在選取了文件的狀況下,纔會顯示文件列表。 接着用 each 遍歷全部選中文件,進行上傳操做,並在上傳過程當中顯示文件的名稱和大小,上傳完成後則顯示文件的 URL。 固然,幾乎全部程序都少不了錯誤處理的部分。 由於這是一個內部工具,因此就直接在網頁上顯示錯誤信息了,沒額外作更多處理。 注意這裏用了 await 等待上傳文件的異步函數返回結果,獲取到結果後將它賦值給 url 變量,而後就能夠直接在界面中使用 url 變量了。

好了,大功告成,不到 50 行代碼,就完成了這個簡單的上傳工具。 回頭看看代碼,很是直截了當。 但仔細想一想,仍是很神奇的,好比只是經過 files = e.detail.acceptedFiles 這一行賦值操做,界面就能感知到 files 的變化,相應地渲染不一樣的內容。 這是由於 Svelte 會把 = 編譯爲相似 React 等框架中的 setState 方法。 有些人可能以爲這有些太魔法了,但其實大多數編程語言中 = 就挺魔法的,好比會有 x = x + 1; 這樣的語句,只是你們都習慣了而已。

最後須要把它部署到一個地方。 由於是個純靜態頁面,因此部署到什麼地方都行。 既然文件託管已經用了 LeanCloud,那網頁也部署到 LeanCloud 吧。

運行如下命令編譯、打包項目:

npm run build

而後運行如下命令便可部署至 LeanCloud:

cd public
lean switch --region CN --group web YOUR_APP_ID
lean deploy --prod 1

這裏 CN 表示 LeanCloud 華北節點,web 是雲引擎分組的名稱,若是使用了其餘節點或者 LeanCloud 國際版,或者分組名稱不一樣,須要修改相應的值,具體能夠查看「LeanCloud 控制檯 > 雲引擎 > 雲引擎分組 > 部署 > 命令行工具」的說明。

若是沒有安裝 LeanCloud 命令行工具,須要先安裝並關聯帳號,關聯帳號的步驟一樣能夠上述控制檯區域找到。 另外,要訪問部署在雲引擎上的頁面,還須要綁定一個雲引擎域名,或者申請一個 avosapps.us 下的子域名(僅限國際版)。

下面是部署後的效果圖:

fileup 界面

能夠看到,爲了方便複製 URL,我加了複製到剪貼板的按鈕。 使用 svelte-copy-to-clipboard 組件:

npm i svelte-copy-to-clipboard

只需添加 10 行代碼:

<script lang="ts">
import CopyToClipboard from "svelte-copy-to-clipboard";
// 略

function handleSuccessfulCopy(i) {
    copyStatuses[i] = '✅'
}
function handleFailedCopy(i) {
    copyStatuses[i] = '❌'
}
</script>

<!-- 略 -->
{#if files}
  <h2>Files</h2>
    <!-- 略 -->
    {:then url}
      <p>Uploaded:
        <code>{url ?? ""}</code>
        <CopyToClipboard text={url} on:copy={() => handleSuccessfulCopy(i)} on:fail={() => handleFailedCopy(i)} let:copy>
          <button on:click={copy}>{copyStatuses[i]}</button>
        </CopyToClipboard>
      </p>
  <!-- 略 -->
{/if}

若是但願進一步改善用戶體驗,還能夠在上傳時顯示進度。 這項改進就留給讀者做爲練習(提示)。

不能免俗,用 LightHouse 跑個分:

fileup lighthouse

以前寫頁面的時候我並無考慮性能,但性能評分仍然達到了 100。 SEO 和可訪問性方面有些小問題,改起來不麻煩,一樣留給讀者做爲練習。 好吧,你也許已經發現了,我只是在爲我本身的懶惰找藉口。 不過,若是你真的完成了這些練習,歡迎來提 pr

寫到這裏差很少要結束了,總結一下。 就小工具和交互很少的站點而言,Svelte 寫起來更加直截了當,和 React、Vue 相比,編寫的代碼更加簡明扼要, 套路代碼(boilerplate code)更少,編譯出的代碼更加輕量,所以對我的健康(節省鍵盤敲擊次數,避免肌肉勞損)和世界環境(訪問者加載和運行的代碼更少,顯著下降碳排量)都更友好。 Svelte 這樣的無框架(frameless)框架和 LeanCloud 這樣的無服務器(serverless)雲服務,抽象掉了大量無關業務的細節,可讓開發者專一項目的核心功能,提高開發效率,改善開發體驗。

題圖 Paul Gilmore

相關文章
相關標籤/搜索