使用 Lit 建立自訂 HTML 元件元素

Lit 是 Google 開發,現在的版本是 Lit 3.0 (不支援 IE 11),用來建立自訂 Web 元件的函式庫, 例如建立一個自訂的 HTML 元素: <my-web-component></my-web-component> ,可以很簡單的直接加到 HTML 中使用。

使用 Vite 建立專案

使用 Vite 建立一個使用 TypeScript 的 Lit 專案,名稱為 lit-test
    
npm create vite@latest lit-test
    

framework 選擇 Lit
variant 選擇 Lit

然後就是進入到專案資料夾、安裝套件、啟動
    
cd lit-test
npm i
run dev
    

筆者自行安裝 Lit 後在使用執行時使用 TypeScript 遇到許多問題,花了好幾個小時沒有辦法解決(筆者好像很常花很多時間在解決奇怪的小問題😅),後來使用 Vite 就完全沒有遇到問題了,等筆者有空的時候再繼續研究看看,如果有解決再來寫一篇和大家報告。

安裝 Lit 套件

在上一部我們使用 vite 建立專案的時候 variant 選擇 Lit 時就已經自動安裝好 Lit 了,如果不是使用上面的步驟建立專案的話可以使用下面的指令安裝 Lit 套件:
    
npm i lit
    

使用 Lit 建立第一個元件

假設我們要建立一個可以在 HTML 中使用的 <my-web-component></my-web-component> 元件 (名稱要使用 kebab-case 這樣的命名方式),只要:
  1. 建立一個 .ts 檔案(名稱習慣和元件名稱一樣,並且也是使用 kebab-case 的命名方式)
  2. 在 .ts 檔案中建立一個類別(名稱和元件名稱一樣,只是使用 PascalCase 的命名方式)
  3. 在 .ts 檔案中建立一個類別(名稱和元件名稱一樣,只是使用 PascalCase 的命名方式),繼承 LitElement
  4. 使用 customElement 裝飾器標記元件名稱(用 kebab-case 的命名方式)
  5. 建立 render 方法,回傳元件要顯示的內容
    
import {LitElement} from 'lit';
import {customElement} from 'lit/decorators.js';

@customElement('my-web-component')
export class MyWebComponent extends LitElement {
    render() {
        return "這是一個元件";
    }
}
    

然後就可以在網頁中使用了:
    
<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8"/>
    <title>Ruyut Lit Test</title>

    <script type="module" src="./my-web-component.ts"></script>
</head>
<body>

<my-web-component></my-web-component>

</body>
</html>

    

目前這個元件的用途就是顯示「這是一個元件」這一串文字。

如果無法顯示很可能是路徑位置不太一樣,目前主要專案結構如下:
	
lit-test/
|-- node_modules/
|-- index.html
|-- package.json
|-- my-web-component.ts
`-- tsconfig.json
	

功能介紹

上面的範例中只是顯示文字,還可以顯示 HTML 元素
    
import {html, LitElement} from 'lit';
import {customElement} from 'lit/decorators.js';

@customElement('my-web-component')
export class MyWebComponent extends LitElement {
    render() {
        return html`<b><span style="color: #990000;">注意!</span></b>`;
    }
}
    

也可以自訂 CSS ,只是要記得在 css 區塊中要使用 /**/ 的方式寫註解,不然 CSS 可能會無效。這樣兩個寫在一起的方式有點容易混淆,筆者就搞錯好幾次
    
import {css, html, LitElement} from 'lit';
import {customElement} from 'lit/decorators.js';

@customElement('my-web-component')
export class MyWebComponent extends LitElement {

    static styles = css`
        /* host 代表整個元件 */
        :host {
            color: #990000;
        }
        span {
            font-weight: bold;
        }
    `;

    render() {
        return html`12345 <span>注意!</span>`;
    }
}
    

建立方法、按鈕事件、變數

    
import {css, html, LitElement} from 'lit'
import {customElement, property} from 'lit/decorators.js'

@customElement('my-web-component')
export class MyWebComponent extends LitElement {

    static styles = css`
        span {
            color: #990000;
            font-weight: bold;
        }
    `;

    @property({type: Number})
    count = 0

    private _decrement() {
        this.count--
    }

    private _increment() {
        this.count++
    }

    render() {
        return html`
            <button @click=${this._decrement}>-</button>
            <span>${this.count}</span>
            <button @click=${this._increment}>+</button>
        `;
    }
}
    

在外部 HTML 也可以很簡單的傳入參數、更新參數:
    
<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8"/>
    <title>Ruyut Lit Test</title>

    <script type="module" src="./my-web-component.ts"></script>
</head>
<body>

<my-web-component id="myComponent" count="10"></my-web-component>
<button id="myButton">Reset</button>

<script>
    const myComponent = document.getElementById('myComponent');
    const myButton = document.getElementById('myButton');

    myButton.addEventListener('click', () => {
        myComponent.count = 0;
    });
</script>

</body>
</html>

    

外部監聽事件

也可以使用 CustomEvent 建立事件,讓外部監聽:
    
import {css, html, LitElement} from 'lit'
import {customElement, property} from 'lit/decorators.js'

@customElement('my-web-component')
export class MyWebComponent extends LitElement {

    static styles = css`
        span {
            color: #990000;
            font-weight: bold;
        }
    `;

    @property({type: Number})
    count = 0

    private _decrement() {
        this.count--
        const event = new CustomEvent('onDecrement', {
            detail: {
                count: this.count
            }
        });
        this.dispatchEvent(event);
    }

    private _increment() {
        this.count++
    }

    render() {
        return html`
            <button @click=${this._decrement}>-</button>
            <span>${this.count}</span>
            <button @click=${this._increment}>+</button>
        `;
    }
}
    

    
<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8"/>
    <title>Ruyut Lit Test</title>

    <script type="module" src="./my-web-component.ts"></script>
</head>
<body>

<my-web-component id="myComponent" count="10"></my-web-component>
<button id="myButton">Reset</button>

<script>
    const myComponent = document.getElementById('myComponent');

    myComponent.addEventListener('onDecrement', (e) => {
        console.log('count', e.detail.count);
    });
</script>

</body>
</html>

    


註: 筆者沒有找到一個頁面直接寫 Lit 是 Google 開發的,不過倒是有在這個影片中看到他說他是 Google lit 團隊中的一個開發人員

參考資料:
lit.dev

留言

張貼留言

如果有任何問題、建議、想說的話或文章題目推薦,都歡迎留言或來信: a@ruyut.com