使用 Svelte 來構建 Web Component (超簡單方便)

語言: CN / TW / HK

每個開發人員都應該關注程式碼中的可重用性以及程式碼的業務隔離,這樣可以讓業務邏輯與應用架構分離,提升系統的擴充套件性。而 Web Component 就是這樣一個技術,可以讓我們建立一個獨立的可重用元件。

本文將介紹使用 Svelte 建立通用的 Web Component 的完成過程。『通用』指的是該元件不侷限於 Svelte 應用,還可以用於任何 JavaScript 應用程式(如 Vue、React 等),同時還將介紹使用 Svelte 建立 Web Component 的一些侷限性。

什麼是 Web Component?

Web Component 可以讓我們建立可重用的、自定義的封裝了樣式以及功能的 HTML 元素。

例如,我們使用如下程式碼來建立一個導航條:

<style>
/* CSS code for our navbar */
</style>
<navbar>
<!-- Some long code for our navbar -->
</navbar>

如果用 Web Component 技術,我們可以定義一個自定義元素(如 <custom-navbar/>),然後你可以在網頁任一地方使用該元素顯示導航條,同時這個導航條的樣式和頁面其他元素的樣式是完全隔離的,不會相互影響。而這項技術被稱之為 Shadow DOM (影子 DOM)。

什麼是 Shadow DOM?

Shadow DOM 是一個較小的、獨立的 DOM,它與主 DOM 分開渲染,允許我們將樣式和標記行為隔離到單個元件中。Shadow DOM 本質上允許我們將元件功能進行封裝和隔離,我們可以單獨對其設定樣式、編寫功能指令碼,而不會干擾應用程式的其餘元素。

你大概對 Web Component 有一個基本的認識,下面我們開始用 Svelte 來編寫一個簡單的元件。

構建 Web Component

準備工作

為了完成本文剩餘的內容學習,需要你對掌握以下知識:

  • 對 HTML, CSS 和 JavaScript 有基本的認識
  • 熟悉命令列操作環境
  • 需要一個文字編輯器
  • 最好對 Svelte 有一些基本的瞭解(可以看看我之前翻譯的文章 https://my.oschina.net/javayou/blog/5309554 )

開始

本文我們將開發兩個元件:

  • 第一個元件是一個卡片元件,接收三個屬性分別是:卡片標題、卡片描述以及圖片,該元件名為 <my-card />
  • 第二個元件是一個帶樣式的按鈕,接收一個 type 的屬性,通過該屬性來確定按鈕的顯示樣式,元件名稱為 <cool-button />

同時我們將使用兩種元件的釋出方式,一個是打包到單一檔案,另外一個是每個元件釋出到一個獨立的檔案中。

下圖顯示的是兩個元件執行後的外觀:

Final Preview Of Instantiated Components

我們從建立一個 Svelte 應用開始,並安裝必須的包:

npx degit sveltejs/template web-component-tut
cd web-component-tut
npm install

上述命令執行完畢就可以使用如下命令啟動一個測試環境:

npm run dev

然後我們開啟瀏覽器訪問 http://localhost:8080 就可以看到一個最基礎的 Svelte 應用執行起來後的樣子:

The Welcome Page Of The New Svelte Application

開發一個元件

使用 Svelte 生成通用 Web Component 的過程類似於建立常規 Svelte 元件的過程,只是進行了一些修改。

為了建立第一個卡片元件,我們首先建立一個檔案 src/Card.svelte 並定義元件的屬性、樣式以及 HTML 標籤,程式碼如下:

<script>
  // component props
  // Camel case not supported for props, see drawback section.
  export let card_title, card_desc, card_img;
</script>

<main>
  <div class="card-container">
    <div class="card">
      <img src={card_img} alt="My product" />
      <div class="card-body">
        <div class="row">
          <div class="card-title">
            <h2>{card_title}</h2>
          </div>
        </div>
        <p>
          {card_desc}
        </p>
        <button>Do Something</button>
      </div>
    </div>
  </div>
</main>

<style>
 .card {
    max-width: 350px;
    border-radius: 5px;
    box-shadow: 0 4px 6px 0 #00000033;
    padding: 0 0 10px 0;
  }

  .card img {
    width: 100%;
    height: auto;
  }

  .card-body {
    padding: 5px 10px;
  }

  .card-body p {
    color: #575757;
    margin-bottom: 20px;
    font-size: 14px;
  }
</style>

你可以在其他元件中使用這個卡片元件,如下所示(這一步可忽略):

<script>
  import Card from "./Card.svelte";
</script>

<main>
  <Card
    card_title="My Card Title"
    card_desc="Lorem ipsum dolor…"
    card_img="path/to/my-image.png"
  />

</main>

接下來是按鈕元件,檔名 /src/Button.svelte 程式碼如下:

<script>
  // Component props
  export let type = "solid";
</script>

<button class={type == "solid" ? "btn-solid" : "btn-outline"}>
  <slot />
</button>

<style>
  button {
    padding: 10px;
    color: #fff;
    font-size: 17px;
    border-radius: 5px;
    border: 1px solid #ccc;
    cursor: pointer;
  }
  .btn-solid {
    background: #20c997;
    border-color: #4cae4c;
  }
  .btn-outline {
    color: #20c997;
    background: transparent;
    border-color: #20c997;
  }
</style>

該元件的使用方法如下(可忽略):

import Button from "./Button.svelte";

<Button type="outline">Click me</Button>

將自定義元件轉成通用元件

我們需要將自定義 Svelte 元件轉成通用的 Web Component ,這樣才可以在其他框架中直接使用。

要做這個轉換操作,我們需要在 Svelte 配置檔案中設定讓編譯器生成自定義元素。開啟 rollup.config.js 在 plugins 增加一個 compilerOptions配置項,在該配置項下增加 customElement: true 配置資訊,如下所示:

...
 plugins: [
    svelte({
      compilerOptions: {
        dev: !production,
        customElement: true,
...

然後我們需要給兩個元件分別定義一個元素名稱,開啟 Card.svelte 檔案,在檔案開頭第一行插入如下內容:

<svelte:options tag="my-card" />

上述 tag 屬性值代表元件的標籤名稱。

同樣的,開啟 Button.svelte 給第二個元件指定一個標籤名稱:

<svelte:options tag="cool-button" />

最後一步是在 main.js 中引入這兩個元件,開啟 /src/main.js 將裡面的內容替換成如下兩行:

import Button from "./Button.svelte";
import Card from "./Card.svelte";

這裡我們已經完成了兩個元件的開發步驟,下一步就是打包元件以便其他的應用可以使用這兩個元件,開啟命令列視窗進入專案所在目錄執行如下命令:

npm run build

該命令將生成兩個檔案,分別是 build.js 和 build.map.js, 檔案位於專案下的 /build 目錄。其中build.js 是打包的兩個元件,而 build.map.js  build.js原始碼對映檔案。

如果上述步驟順利完成,我們就可以將 bundle.js 拷貝到一個新目錄,然後建立一個 index.html 檔案,內容如下:

<!DOCTYPE html>
<html>
  <head>
    <title>My website</title>
    <script src="./build.js"></script>
  </head>

  <body>
    <div class="container">
      <div class="row">
        <div class="col">
          <my-card
            card_title="Red Person"
            card_desc=" Lorem ipsum dolor sit, amet consectetur.."
            card_img="https://bit.ly/34B3zHX"
          >
          </my-card>
          <!-- Image credit - Shubham Dhage on unsplash.com -->
        </div>
        <div class="col">
          <div class="border-bottom py-5">
            <cool-button> Solid Cool Button </cool-button>
            <cool-button type="outline"> Outlined Cool Button </cool-button>
          </div>
        </div>
      </div>
    </div>
  </body>
</html>

這是一個再簡單不過的 HTML 頁面了,該頁面使用了上述兩個元件,在瀏覽器中顯示為如下效果:

Components Displayed On Webpage

元件分割

在某些情況下我們不需要將多個元件打包到同一個 js 檔案中,我們希望每個元件有一個獨立的 js 檔案。要實現這個場景只需在 rollup.config.js 的 input 和 output 中進行配置即可。

在 input 的配置中我們可以指定一個數組,陣列的元素就是每個元件的檔案路徑,而 output 指定為輸出的目錄即可:

export default {
  input: ["src/Card.svelte", "./src/Button.svelte"],
  output: {
    format: "iife",
    dir: "public/build/",
  },
...

修改完成後再次執行 npm run build ,我們就可以在 build 目錄中看到兩個檔案 Button.js  Card.js 。

使用方法類似:

<script src="Button.js" type="module"></script>
<cool-button type="outline">Click Me</cool-button>

<!-- another-page.html -->
<script src="Card.js" type="module"></script>
<my-card card_title="..."></my-card>

主要缺點

到這裡我們已經掌握了使用 Svelte 開發 Web Component 的方法,這個過程非常之簡單,但是,使用 Svelte 開發 Web 元件會有一些缺點如下:

  • 元件的屬性名稱不允許使用駝峰命名法,例如你會發現使用形如 cardTitle 這樣的屬性名就無法正常工作,而駝峰命名法又是 JavaScript 推薦的命名風格。這是一個 Bug,不過如果你使用的是 Vite ,那麼有一個 workaround plugin 可以解決這個問題。
  • 如果不標記Web元件,就無法在Svelte中重用它們 - 不幸的是,您還必須標記要在自定義Web元件中使用的每個Svelte元件
    加入你有一個 Header.svelte 檔案需要作為 <my-header /> 元件輸出,但同時這個元件又依賴另外一個 Nav.svelte 檔案,而 Nav.svelte 我們不希望作為 Web 元件輸出,這個問題使得我們必須將 Nav.svelte 也作為元件輸出,否則程式就會報錯。好在這個問題現在也有解了(詳情請看  這裡,我們可以通過配置來解決這個問題,雖然看起來不是那麼的爽,就這樣吧,又不是不能用。
  • 瀏覽器的支援問題 — JavaScript customElement API 就是用來建立 Web Component 的底層 API,該 API 目前並沒有被所有的瀏覽器支援,我們需要引入 Polyfill 來解決這個檔案,詳情請看 webcomponents official 

總結

在本文中,我們學習瞭如何使用 Svelte 建立通用卡片和按鈕元件,生成捆綁檔案,拆分它們,甚至在單獨的 HTML 頁面中重用此元件。

動手試試吧?

本文翻譯自:Build web components with Svelte - LogRocket Blog