<font id="zqva1"></font>
<rt id="zqva1"></rt>
  • <tt id="zqva1"></tt>
    <cite id="zqva1"></cite>

    <cite id="zqva1"><noscript id="zqva1"></noscript></cite>
      <rp id="zqva1"><meter id="zqva1"></meter></rp>

        <cite id="zqva1"></cite>
          <b id="zqva1"></b>
          <rp id="zqva1"></rp>
          <cite id="zqva1"></cite>

          <rt id="zqva1"></rt>

        1. <rp id="zqva1"></rp>

          使用純粹的JS構建 Web Component

          時間:?2017-12-19閱讀:?1003標簽:?Component
          原文鏈接:https://ayushgp.github.io/htm...
          譯者:阿里云 - 也樹

          Web Component 出現有一陣子了。 Google 費了很大力氣去推動它更廣泛的應用,但是除 Opera 和 Chrome 以外的多數主流瀏覽器對它的支持仍然不夠理想。

          但是通過 polyfill,你可以從現在開始構建你自己的 Web Component,你可以在這里找到相關支持:https://www.webcomponents.org/polyfills

          在這篇文章中,我會演示如何創建帶有樣式,擁有交互功能并且在各自文件中優雅組織的 HTML 標簽。

          介紹

          Web Component 是一系列 web 平臺的 API,它們可以允許你創建全新可定制、可重用并且封裝的 HTML 標簽,從而在普通網頁及 web 應用中使用。

          定制的組件基于 Web Component 標準構建,可以在現在瀏覽器上使用,也可以和任意與 HTML 交互的 JavaScript 庫和框架配合使用。

          用于支持 Web Component 的特性正逐漸加入 HTML 和 DOM 的規范,web 開發者使用封裝好樣式和定制行為的新元素來拓展 HTML 會變得輕而易舉。

          它賦予了僅僅使用純粹的JS/HTML/CSS就可以創建可重用組件的能力。如果 HTML 不能滿足需求,我們可以創建一個可以滿足需求的 Web Component。

          舉個例子,你的用戶數據和一個 ID 有關,你希望有一個可以填入用戶 ID 并且可以獲取相應數據的組件。HTML 可能是下面這個樣子:

          <user-card user-id="1"></user-card>
          

          這是一個 Web Component 最基本的應用。下面的教程將會聚焦在如何構建這個用戶卡片組件。

          Web Component 的四個核心概念

          HTML 和 DOM 標準定義了四種新的標準來幫助定義 Web Component。這些標準如下:

          1. 定制元素(Custom Elements): web 開發者可以通過定制元素創建新的 HTML 標簽、增強已有的 HTML 標簽或是二次開發其它開發者已經完成的組件。這個 API 是 Web Component 的基石。
          2. HTML 模板(HTML Templates): HTML 模板定義了新的元素,描述一個基于 DOM 標準用于客戶端模板的途徑。模板允許你聲明標記片段,它們可以被解析為 HTML。這些片段在頁面開始加載時不會被用到,之后運行時會被實例化。
          3. Shadow DOM: Shadow DOM 被設計為構建基于組件的應用的一個工具。它可以解決 web 開發的一些常見問題,比如允許你把組件的 DOM 和作用域隔離開,并且簡化 CSS 等等。
          4. HTML 引用(HTML Imports): HTML 模板(HTML Templates)允許你創建新的模板,同樣的,HTML 引用(HTML imports)允許你從不同的文件中引入這些模板。通過獨立的HTML文件管理組件,可以幫助你更好的組織代碼。

          定義定制元素

          我們首先需要聲明一個類,定義元素如何表現。這個類需要繼承 HTMLElement 類,但讓我們先繞過這部分,先來討論定制元素的生命周期方法。你可以使用下面的生命周期回調函數:

          • connectedCallback — 每當元素插入 DOM 時被觸發。
          • disconnectedCallback — 每當元素從 DOM 中移除時被觸發。
          • attributeChangedCallback — 當元素上的屬性被添加、移除、更新或取代時被觸發。

          在 UserCard 文件夾下創建 UserCard.js:

          class UserCard extends HTMLElement {
            constructor() {
              super();
              this.addEventListener('click', e => {
                this.toggleCard();
              });
            }
          
            toggleCard() {
              console.log("Element was clicked!");
            }
          }
          
          customElements.define('user-card', UserCard);
          

          這個例子里我們已經創建了一個定義了定制元素行為的類。customElements.define('user-card', UserCard); 函數調用告知 DOM 我們已經創建了一個新的定制元素叫 user-card,它的行為被 UserCard 類定義。現在可以在我們的 HTML 里使用 user-card 元素了。

          我們會用到 https://jsonplaceholder.typicode.com/ 的 API 來創建我們的用戶卡片。下面是數據的樣例:

          {
            id: 1,
            name: "Leanne Graham",
            username: "Bret",
            email: "Sincere@april.biz",
            address: {
              street: "Kulas Light",
              suite: "Apt. 556",
              city: "Gwenborough",
              zipcode: "92998-3874",
              geo: {
                lat: "-37.3159",
                lng: "81.1496"
              }
            },
            phone: "1-770-736-8031 x56442",
            website: "hildegard.org"
          }
          

          創建模板

          現在,讓我們創建一個將在屏幕上渲染的模板。創建一個名為 UserCard.html 的新文件,內容如下:

          <template id="user-card-template">
            <div>
              <h2>
                <span></span> (
                <span></span>)
              </h2>
              <p>Website: <a></a></p>
              <div>
                <p></p>
              </div>
              <button class="card__details-btn">More Details</button>
            </div>
          </template>
          <script src="/UserCard/UserCard.js"></script>
          

          注意:我們在類名前加了一個 card__ 前綴。在較早版本的瀏覽器中,我們不能使用 shadow DOM 來隔離組件 DOM。這樣當我們為組件編寫樣式時,可以避免意外的樣式覆蓋。

          編寫樣式

          我們創建好了卡片的模板,現在來用 CSS 裝飾它。創建一個 UserCard.css 文件,內容如下:

          .card__user-card-container {
            text-align: center;
            display: inline-block;
            border-radius: 5px;
            border: 1px solid grey;
            font-family: Helvetica;
            margin: 3px;
            width: 30%;
          }
          
          .card__user-card-container:hover {
            box-shadow: 3px 3px 3px;
          }
          
          .card__hidden-content {
            display: none;
          }
          
          .card__details-btn {
            background-color: #dedede;
            padding: 6px;
            margin-bottom: 8px;
          }
          

          現在,在 UserCard.html 文件的最前面引入這個 CSS 文件:

          <link rel="stylesheet" href="/UserCard/UserCard.css">
          

          樣式已經就緒,接下來可以繼續完善我們組件的功能。

          connectedCallback

          現在我們需要定義創建元素并且添加到 DOM 中會發生什么。注意這里 constructor 和 connectedCallback 方法的區別。

          constructor 方法是元素被實例化時調用,而 connectedCallback 方法是每次元素插入 DOM 時被調用。connectedCallback 方法在執行初始化代碼時是很有用的,比如獲取數據或渲染。

          小貼士: 在 UserCard.js 的頂部,定義一個常量 currentDocument。它在被引入的 HTML 腳本中是必要的,允許這些腳本有途徑操作引入模板的 DOM。像下面這樣定義:

          const currentDocument = document.currentScript.ownerDocument;
          

          接下來定義我們的 connectedCallback 方法:

          // 元素插入 DOM 時調用
          connectedCallback() {
            const shadowRoot = this.attachShadow({mode: 'open'});
          
            // 選取模板并且克隆它。最終將克隆后的節點添加到 shadowDOM 的根節點。
            // 當前文檔需要被定義從而獲取引入 HTML 的 DOM 權限。
            const template = currentDocument.querySelector('#user-card-template');
            const instance = template.content.cloneNode(true);
            shadowRoot.appendChild(instance);
          
            // 從元素中選取 user-id 屬性
            // 注意我們要像這樣指定卡片: 
            // <user-card user-id="1"></user-card>
            const userId = this.getAttribute('user-id');
          
            // 根據 user ID 獲取數據,并且使用返回的數據渲染
            fetch(`https://jsonplaceholder.typicode.com/users/${userId}`)
                .then((response) => response.text())
                .then((responseText) => {
                    this.render(JSON.parse(responseText));
                })
                .catch((error) => {
                    console.error(error);
                });
          }
          

          渲染用戶數據

          我們已經定義好了 connectedCallback 方法,并且把克隆好的模板綁定到了 shadow root 上。現在我們需要填充模板內容,然后在 fetch 方法獲取數據后觸發 render 方法。下面來編寫 render 和 toggleCard 方法。

          render(userData) {
            // 使用操作 DOM 的 API 來填充卡片的不同區域
            // 組件的所有元素都存在于 shadow dom 中,所以我們使用了 this.shadowRoot 這個屬性來獲取 DOM
            // DOM 只可以在這個子樹種被查找到
            this.shadowRoot.querySelector('.card__full-name').innerHTML = userData.name;
            this.shadowRoot.querySelector('.card__user-name').innerHTML = userData.username;
            this.shadowRoot.querySelector('.card__website').innerHTML = userData.website;
            this.shadowRoot.querySelector('.card__address').innerHTML = `<h4>Address</h4>
              ${userData.address.suite}, <br />
              ${userData.address.street},<br />
              ${userData.address.city},<br />
              Zipcode: ${userData.address.zipcode}`
          }
          
          toggleCard() {
            let elem = this.shadowRoot.querySelector('.card__hidden-content');
            let btn = this.shadowRoot.querySelector('.card__details-btn');
            btn.innerHTML = elem.style.display == 'none' ? 'Less Details' : 'More Details';
            elem.style.display = elem.style.display == 'none' ? 'block' : 'none';
          }
          

          既然組件已經完成,我們就可以把它用在任意項目中了。為了繼續教程,我們需要創建一個 index.html 文件,然后寫入下面的代碼:

          <html>
          
          <head>
            <title>Web Component</title>
          </head>
          
          <body>
            <user-card user-id="1"></user-card>
            <script src="https://cdnjs.cloudflare.com/ajax/libs/webcomponentsjs/1.0.14/webcomponents-hi.js"></script>
            <link rel="import" href="./UserCard/UserCard.html">
          </body>
          
          </html>
          

          因為并不是所有瀏覽器都支持 Web Component,我們需要引入 webcomponents.js 這個文件。注意我們用到 HTML 引用語句來引入我們的組件。

          為了運行這些代碼,你需要創建一個靜態文件服務器。如果你不清楚如何創建,你可以使用像 static-server 或者 json-server 這樣的簡易靜態服務。教程里,我們安裝 static-server:

          $ npm install -g static-server
          

          接著在你的項目目錄下,使用下面的命令運行服務器:

          $ static-server
          

          打開你的瀏覽器并訪問localhost:3000,你就可以看到我們剛剛創建的組件了。

          小貼士和技巧

          還有很多關于 Web Component 的東西沒有在這篇短文中寫到,我想簡單的陳述一些開發 Web Component 的小貼士和技巧。

          組件的命名

          • 定制元素的名稱必須包含一個短橫線。所以 <my-tabs> 和 <my-amazing-website> 是合法的名稱, 而<foo> 和 <foo_bar> 不行。
          • 在 HTML 添加新標簽時需要確保向前兼容,不能重復注冊同一個標簽。
          • 定制元素標簽不能是自閉合的,因為 HTML 只允許一部分元素可以自閉合。需要寫成像 <app-drawer></app-drawer> 這樣的閉合標簽形式。

          拓展組件

          創建組件時可以使用繼承的方式。舉個例子,如果想要為兩種不同的用戶創建一個 UserCard,你可以先創建一個基本的 UserCard 然后將它拓展為兩種特定的用戶卡片。想要了解更多組件繼承的知識,可以查看Google web developers’ article

          Lifecycle Callbacks生命周期回調函數

          我們創建了當元素加入 DOM 后自動觸發的 connectedCallback 方法。我們同樣有元素從 DOM 中移除時觸發的 disconnectedCallback 方法。 attributesChangedCallback(attribute, oldval, newval)方法會在我們改變定制組件的屬性時被觸發。

          組件元素是類的實例

          既然組件元素是類的實例,就可以在這些類中定義公用方法。這些公用方法可以用來允許其它定制組件/腳本來和這些組件產生交互,而不是只能改變這些組件的屬性。

          定義私有方法

          可以通過多種方式定義私有方法。我傾向于使用(立即執行函數),因為它們易寫和易理解。舉個例子,如果你創建的組件有非常復雜的內部功能,你可以像下面這樣做:

          (function() {
          
            // 使用第一個self參數來定義私有函數
            // 當調用這些函數時,從類中傳遞參數
            function _privateFunc(self, otherArgs) { ... }
          
            // 現在函數只可以在你的類的作用域中可用
            class MyComponent extends HTMLElement {
              ...
          
              // 定義下面這樣的函數可以讓你有途徑和這個元素交互
              doSomething() {
                ...
                _privateFunc(this, args)
              }
              ...
            }
          
            customElements.define('my-component', MyComponent);
          })()
          

          凍結類

          為了防止新的屬性被添加,需要凍結你的類。這樣可以防止類的已有屬性被移除,或者已有屬性的可枚舉、可配置或可寫屬性被改變,同樣也可以防止原型被修改。你可以使用下面的方法:

          class MyComponent extends HTMLElement { ... }
          const FrozenMyComponent = Object.freeze(MyComponent);
          customElements.define('my-component', FrozenMyComponent);
          

          注意: 凍結類會阻止你在運行時添加補丁并且會讓你的代碼難以調試。

          結論

          這篇關于 Web Component 的教程作用非常有限。這可以部分歸咎于對 Web Component 的影響很大的 React。我希望這篇文章可以提供給你足夠的信息來讓你嘗試不添加任何依賴來構建自己的定制組件。你可以在 定制組件 API 規范(Custom components API spec) 找到更多關于 Web Component 的信息。

          你可以在這里閱讀第二部分的教程:使用純粹的JS構建 Web Component - Part 2!

          站長推薦

          1.阿里云: 本站目前使用的是阿里云主機,安全/可靠/穩定。點擊領取2000元代金券、了解最新阿里云產品的各種優惠活動點擊進入

          2.騰訊云: 提供云服務器、云數據庫、云存儲、視頻與CDN、域名等服務。騰訊云各類產品的最新活動,優惠券領取點擊進入

          3.廣告聯盟: 整理了目前主流的廣告聯盟平臺,如果你有流量,可以作為參考選擇適合你的平臺點擊進入

          鏈接: http://www.modern-decoration.com.cn/article/detial/258

          內容以共享、參考、研究為目的,不存在任何商業目的。其版權屬原作者所有,如有侵權或違規,請與小編聯系!情況屬實本人將予以刪除!

          文章投稿關于web前端網站點搜索站長推薦網站地圖站長QQ:522607023

          小程序專欄: 土味情話心理測試腦筋急轉彎幽默笑話段子句子語錄成語大全運營推廣

          国产精品高清视频免费 - 视频 - 在线观看 - 影视资讯 - 唯爱网