為什麼需要Web Component?

tags: Web HTML5 JavaScript
category: Front-End
description: 為什麼需要web component?
created_at: 2021/06/29 20:00:00

cover image

假設我們先做個小練習

做兩個按鈕分別可以遞增遞減數字,像是下面這樣

See the Pen Button-Counter by 賴俊賓 (@laijunbin) on CodePen.

你會發現功能很簡單但光做一個程式碼就很長了,像是下面這樣

HTML相對單純

<div class="button-counter">
  <button class="decrement-button">-</button>
  <span class="number-span">0</span>
  <button class="increment-button">+</button>
</div>

但 javascript 就長非常多

const buttonCounter = document.querySelector('.button-counter')
const decrementButton = buttonCounter.querySelector('.decrement-button')
const incrementButton = buttonCounter.querySelector('.increment-button')
const numberSpan = buttonCounter.querySelector('.number-span')

incrementButton.addEventListener('click', () => {
  const number = parseInt(numberSpan.textContent)
  const newNumber = number + 1
  numberSpan.textContent = newNumber
})

decrementButton.addEventListener('click', () => {
  const number = parseInt(numberSpan.textContent)
  const newNumber = number - 1
  numberSpan.textContent = newNumber
})

假設我要做兩個呢?

See the Pen Single-Button-Counter-Vanilla by 賴俊賓 (@laijunbin) on CodePen.

以我的寫法來說 javacsript 的長度沒有增加很多,但是 html 的部分免不了的還是只能乖乖複製貼上

<div class="button-counter">
  <button class="decrement-button">-</button>
  <span class="number-span">0</span>
  <button class="increment-button">+</button>
</div>
<hr>
<div class="button-counter">
  <button class="decrement-button">-</button>
  <span class="number-span">0</span>
  <button class="increment-button">+</button>
</div>

JavaScript 的部分採用迴圈解決

const buttonCounters = document.querySelectorAll('.button-counter')

buttonCounters.forEach(buttonCounter => {
  const decrementButton = buttonCounter.querySelector('.decrement-button')
  const incrementButton = buttonCounter.querySelector('.increment-button')
  const numberSpan = buttonCounter.querySelector('.number-span')

  incrementButton.addEventListener('click', () => {
    const number = parseInt(numberSpan.textContent)
    const newNumber = number + 1
    numberSpan.textContent = newNumber
  })

  decrementButton.addEventListener('click', () => {
    const number = parseInt(numberSpan.textContent)
    const newNumber = number - 1
    numberSpan.textContent = newNumber
  })
})

那有沒有可能都用 JavaScript 來操縱,讓HTML短一些呢?

這時可能就希望可以寫個 <app-counter></app-counter> 之類的東西代表一組元件(component)

你的程式可能就變成下面這樣

<app-counter></app-counter>
<app-counter></app-counter>
<app-counter></app-counter>

這時的 JavaScript 變得必須要熟悉 DOM 的操作

const counters = document.querySelectorAll('app-counter')

counters.forEach(counter => {
  const buttonCounter = document.createElement('div')
  const decrementButton = Object.assign(document.createElement('button'), { textContent: '-' })
  const incrementButton = Object.assign(document.createElement('button'), { textContent: '+' })
  const numberSpan = Object.assign(document.createElement('span'), { textContent: '0' })

  incrementButton.addEventListener('click', () => {
    const number = parseInt(numberSpan.textContent)
    const newNumber = number + 1
    numberSpan.textContent = newNumber
  })

  decrementButton.addEventListener('click', () => {
    const number = parseInt(numberSpan.textContent)
    const newNumber = number - 1
    numberSpan.textContent = newNumber
  })

  buttonCounter.appendChild(decrementButton)
  buttonCounter.appendChild(numberSpan)
  buttonCounter.appendChild(incrementButton)

  counter.replaceWith(buttonCounter)
})

這時已經有 component 的概念了,只是在 HTML5 加入了一些語法糖(?)

JavaScript:

class Counter extends HTMLElement{
  constructor(){
    super()

    const decrementButton = Object.assign(document.createElement('button'), { textContent: '-' })
    const incrementButton = Object.assign(document.createElement('button'), { textContent: '+' })
    const numberSpan = Object.assign(document.createElement('span'), { textContent: '0' })

    incrementButton.addEventListener('click', () => {
      const number = parseInt(numberSpan.textContent)
      const newNumber = number + 1
      numberSpan.textContent = newNumber
    })

    decrementButton.addEventListener('click', () => {
      const number = parseInt(numberSpan.textContent)
      const newNumber = number - 1
      numberSpan.textContent = newNumber
    })

    this.appendChild(decrementButton)
    this.appendChild(numberSpan)
    this.appendChild(incrementButton)
  }
}

customElements.define('app-counter', Counter) // 註冊<app-counter>標籤

上面就是 HTML5 當中 Web Component 的基本使用方式了

前端框架都有 Component 的概念,但是不是使用這個方式實作的,因為這種方式瀏覽器支援度不是很好。但是自己玩玩也夠了

稍微利用一下也可以做出屬於自己的 Component 工具,可以帶屬性、綁事件,更不用寫那麼多 DOM 操作的語法。

我在今年寒假稍微包裝一下的範例

可以像下面這樣使用他 (基本上就跟前端框架做的事差不多)

index.html

<app-root></app-root>
<script src="./main.js"></script>

template.html

<div>hello {{ count }}</div>
<button (click)="counting()">Counting!</button>

main.js

Component.create({
    selector: 'app-root',
    template: 'template.html'
})(class {
    constructor() {
        this.count = 0;
    }

    counting() {
        this.count++;
    }
});

這樣就可以達到一個按鈕點下去後 count + 1 並渲染到畫面上了

下一篇會使用 Vue 與 React 當範例




最後更新時間: 2021年06月29日.