使用 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