在前兩篇文章中已經介紹了 Web Component 的 Custom Element 與 Shadow DOM,接下來要介紹是 Template。
你是否還記得之前的範例,直接在 Shadow root 使用 innerHTML 加入內容:
shadowRoot.innerHTML = `... <div class="spinner"></div>`
但是使用這種方式來定義元件內容,可能會有以下問題:
所以接下來我們需要用更有效率也更好讀的做法: template
, 來開發 Web Component。
<template>
是 在瀏覽器裡預先解析、但不會渲染的 DOM 樣板,也就是建立一組不呈現到頁面上的 HTML 元素。
就如標籤字面上所說的,你可以把它當成元件內部的範本
,讓你更方便的複製其中的內容。
如果你有寫過 angular,他其實跟 ng-template
的概念是一樣的。
而 React 沒有 <template>
的概念,因為 JSX 本身就已經是模板函數化。
可以使用 template
標籤,或是使用 document.createElement('template')
template
標籤<template>
<style></style>
<div class="spinner"></div>
</template>
document.createElement('template')
template
裡的內容不會直接渲染,所以 <style>
也不會立刻影響頁面(可以做到局部封裝)。spinner.js
我們建立一個 renderTemplate()
的函式
renderTemplate = function() {
const spinnerTemplate = document.createElement('template');
spinnerTemplate.innerHTML = `
<style>
.spinner {
width: 40px;
height: 40px;
border: 4px solid #e8e5e5;
border-top-color: #926dec;
border-radius: 50%;
animation: spin 1s linear infinite;
}
@keyframes spin {
to { transform: rotate(360deg); }
}
</style>
<div class="spinner"></div>
`;
return spinnerTemplate;
}
template.content.cloneNode(true)
,將模板的內容複製出來。注意:如果只使用
template.cloneNode(true)
是看不到任何東西的,記得要拿content
內的東西
true
): 深拷貝(deep clone):會把整棵子樹都複製(包含 <style>
、<button>
...)。false
): 淺拷貝(shallow clone):只複製最外層的空 fragment(文件片段),沒有子節點,會什麼都看不到。shadowRoot.appenChild(cloneNode)
。spinner.js
class CatSpinner extends HTMLElement {
constructor() {
super();
const shadowRoot = this.attachShadow({ mode: 'open' });
const cloneNode = this.renderTemplate().content.cloneNode(true);
shadowRoot.appendChild(cloneNode);
}
}
customElements.define('cat-spinner', CatSpinner);
index.html
<body>
<div style="display: flex; gap: 12px; padding: 20px">
<h2>Web Component</h2>
<cat-spinner></cat-spinner>
</div>
<script src="spinner.js"></script>
</body>
這時你會看見剛剛有加入樣式的按鈕就呈現在畫面上了。
(成功找到上傳 gif 的方法了~spinner 動起來啦~(ノ>ω<)ノ)
如果你直接
shadow.appendChild(template.content)
,它會把原本的內容搬走,下次再用就空了。
所以要先複製一份出來再插入,才能多次重用。
for (let i = 0; i < 3; i++) {
shadowRoot.appendChild(btnTemplate.content);
}
當你這樣做的時候,會發現畫面上只出現一個元件!