Web Components 入門實例教程

轉自阮一峯http://www.ruanyifeng.com/blog/2019/08/web_components.htmljavascript

組件是前端的發展方向,如今流行的 React 和 Vue 都是組件框架。css

谷歌公司因爲掌握了 Chrome 瀏覽器,一直在推進瀏覽器的原生組件,即 Web Components API。相比第三方框架,原生組件簡單直接,符合直覺,不用加載任何外部模塊,代碼量小。目前,它還在不斷髮展,但已經可用於生產環境。html

Web Components API 內容不少,本文不是全面的教程,只是一個簡單演示,讓你們看一下怎麼用它開發組件。前端

1、自定義元素

下圖是一個用戶卡片。java

本文演示如何把這個卡片,寫成 Web Components 組件,這裏是最後的完整代碼web

網頁只要插入下面的代碼,就會顯示用戶卡片。瀏覽器

<user-card></user-card> 

這種自定義的 HTML 標籤,稱爲自定義元素(custom element)。根據規範,自定義元素的名稱必須包含連詞線,用與區別原生的 HTML 元素。因此,<user-card>不能寫成<usercard>app

2、customElements.define()

自定義元素須要使用 JavaScript 定義一個類,全部<user-card>都會是這個類的實例。框架

class UserCard extends HTMLElement { constructor() { super(); } } 

上面代碼中,UserCard就是自定義元素的類。注意,這個類的父類是HTMLElement,所以繼承了 HTML 元素的特性。學習

接着,使用瀏覽器原生的customElements.define()方法,告訴瀏覽器<user-card>元素與這個類關聯。

window.customElements.define('user-card', UserCard); 

3、自定義元素的內容

自定義元素<user-card>目前仍是空的,下面在類裏面給出這個元素的內容。

class UserCard extends HTMLElement { constructor() { super(); var image = document.createElement('img'); image.src = 'https://semantic-ui.com/images/avatar2/large/kristy.png'; image.classList.add('image'); var container = document.createElement('div'); container.classList.add('container'); var name = document.createElement('p'); name.classList.add('name'); name.innerText = 'User Name'; var email = document.createElement('p'); email.classList.add('email'); email.innerText = '-email.com'; var button = document.createElement('button'); button.classList.add('button'); button.innerText = 'Follow'; container.append(name, email, button); this.append(image, container); } } 

上面代碼最後一行,this.append()this表示自定義元素實例。

完成這一步之後,自定義元素內部的 DOM 結構就已經生成了。

4、<template>標籤

使用 JavaScript 寫上一節的 DOM 結構很麻煩,Web Components API 提供了<template>標籤,能夠在它裏面使用 HTML 定義 DOM。

<template id="userCardTemplate"> <img src="https://semantic-ui.com/images/avatar2/large/kristy.png" class="image"> <div class="container"> <p class="name">User Name</p> <p class="email">-email.com</p> <button class="button">Follow</button> </div> </template> 

而後,改寫一下自定義元素的類,爲自定義元素加載<template>

class UserCard extends HTMLElement { constructor() { super(); var templateElem = document.getElementById('userCardTemplate'); var content = templateElem.content.cloneNode(true); this.appendChild(content); } } 

上面代碼中,獲取<template>節點之後,克隆了它的全部子元素,這是由於可能有多個自定義元素的實例,這個模板還要留給其餘實例使用,因此不能直接移動它的子元素。

到這一步爲止,完整的代碼以下。

<body> <user-card></user-card> <template>...</template> <script> class UserCard extends HTMLElement { constructor() { super(); var templateElem = document.getElementById('userCardTemplate'); var content = templateElem.content.cloneNode(true); this.appendChild(content); } } window.customElements.define('user-card', UserCard); </script> </body> 

5、添加樣式

自定義元素尚未樣式,能夠給它指定全局樣式,好比下面這樣。

user-card { /* ... */ } 

可是,組件的樣式應該與代碼封裝在一塊兒,只對自定義元素生效,不影響外部的全局樣式。因此,能夠把樣式寫在<template>裏面。

<template id="userCardTemplate"> <style> :host { display: flex; align-items: center; width: 450px; height: 180px; background-color: #d4d4d4; border: 1px solid #d5d5d5; box-shadow: 1px 1px 5px rgba(0, 0, 0, 0.1); border-radius: 3px; overflow: hidden; padding: 10px; box-sizing: border-box; font-family: 'Poppins', sans-serif; } .image { flex: 0 0 auto; width: 160px; height: 160px; vertical-align: middle; border-radius: 5px; } .container { box-sizing: border-box; padding: 20px; height: 160px; } .container > .name { font-size: 20px; font-weight: 600; line-height: 1; margin: 0; margin-bottom: 5px; } .container > .email { font-size: 12px; opacity: 0.75; line-height: 1; margin: 0; margin-bottom: 15px; } .container > .button { padding: 10px 25px; font-size: 12px; border-radius: 5px; text-transform: uppercase; } </style> <img src="https://semantic-ui.com/images/avatar2/large/kristy.png" class="image"> <div class="container"> <p class="name">User Name</p> <p class="email">-email.com</p> <button class="button">Follow</button> </div> </template> 

上面代碼中,<template>樣式裏面的:host僞類,指代自定義元素自己。

6、自定義元素的參數

<user-card>內容如今是在<template>裏面設定的,爲了方便使用,把它改爲參數。

<user-card image="https://semantic-ui.com/images/avatar2/large/kristy.png" name="User Name" email="-email.com" ></user-card> 

<template>代碼也相應改造。

<template id="userCardTemplate"> <style>...</style> <img class="image"> <div class="container"> <p class="name"></p> <p class="email"></p> <button class="button">Follow John</button> </div> </template> 

最後,改一下類的代碼,把參數加到自定義元素裏面。

class UserCard extends HTMLElement { constructor() { super(); var templateElem = document.getElementById('userCardTemplate'); var content = templateElem.content.cloneNode(true); content.querySelector('img').setAttribute('src', this.getAttribute('image')); content.querySelector('.container>.name').innerText = this.getAttribute('name'); content.querySelector('.container>.email').innerText = this.getAttribute('email'); this.appendChild(content); } } window.customElements.define('user-card', UserCard); 

7、Shadow DOM

咱們不但願用戶可以看到<user-card>的內部代碼,Web Component 容許內部代碼隱藏起來,這叫作 Shadow DOM,即這部分 DOM 默認與外部 DOM 隔離,內部任何代碼都沒法影響外部。

自定義元素的this.attachShadow()方法開啓 Shadow DOM,詳見下面的代碼。

class UserCard extends HTMLElement { constructor() { super(); var shadow = this.attachShadow( { mode: 'closed' } ); var templateElem = document.getElementById('userCardTemplate'); var content = templateElem.content.cloneNode(true); content.querySelector('img').setAttribute('src', this.getAttribute('image')); content.querySelector('.container>.name').innerText = this.getAttribute('name'); content.querySelector('.container>.email').innerText = this.getAttribute('email'); shadow.appendChild(content); } } window.customElements.define('user-card', UserCard); 

上面代碼中,this.attachShadow()方法的參數{ mode: 'closed' },表示 Shadow DOM 是封閉的,不容許外部訪問。

至此,這個 Web Component 組件就完成了,完整代碼能夠訪問這裏。能夠看到,整個過程仍是很簡單的,不像第三方框架那樣有複雜的 API。

8、組件的擴展

在前面的基礎上,能夠對組件進行擴展。

(1)與用戶互動

用戶卡片是一個靜態組件,若是要與用戶互動,也很簡單,就是在類裏面監聽各類事件。

this.$button = shadow.querySelector('button'); this.$button.addEventListener('click', () => {  // do something }); 

(2)組件的封裝

上面的例子中,<template>與網頁代碼放在一塊兒,其實能夠用腳本把<template>注入網頁。這樣的話,JavaScript 腳本跟<template>就能封裝成一個 JS 文件,成爲獨立的組件文件。網頁只要加載這個腳本,就能使用<user-card>組件。

這裏就不展開了,更多 Web Components 的高級用法,能夠接着學習下面兩篇文章。

9、參考連接

(完)

相關文章
相關標籤/搜索