亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 編程 > HTML > 正文

你知道原生HTML組件是什么嗎?原生HTML組件的介紹

2020-03-22 19:40:12
字體:
來源:轉載
供稿:網友
本篇文章給大家帶來的內容是關于你知道原生HTML組件是什么嗎?原生HTML組件的介紹,有一定的參考價值,有需要的朋友可以參考一下,希望對你有所幫助。

嘿!看看這幾年啊,Web 前端的發展可是真快啊!

想想幾年前,HTML 是前端開發者的基本技能,通過各式各樣的標簽就可以搭建一個可用的網站,基本交互也不是問題。如果再來點 CSS,嗯,金黃酥脆,美味可口。這時候再撒上幾把 JavaScript,簡直讓人欲罷不能。

隨著需求的增長,HTML 的結構越來越復雜,大量重復的代碼使得頁面改動起來異常困難,這也就孵化了一批批模版工具,將公共的部分抽取出來變為公共組件。再后來,隨著 JavaScript 的性能提升,JavaScript 的地位越來越高,不再只是配菜了,前端渲染的出現降低了服務端解析模版的壓力,服務端只要提供靜態文件和 API 接口就行了嘛。再然后,前端渲染工具又被搬回了服務端,后端渲染出現了(黑人問號???)

總之,組件化使得復雜的前端結構變得清晰,各個部分獨立起來,高內聚低耦合,使得維護成本大大降低。

那么,你有聽說過原生 HTML 組件嗎?

1754597496-5bc7f1ee5c154_articlex.jpg

四大 Web 組件標準

在說原生 HTML 組件之前,要先簡單介紹一下四大 Web 組件標準,四大 Web 組件標準分別為:HTML Template、Shadow DOM、Custom Elements 和 HTML Imports。實際上其中一個已經被廢棄了,所以變成“三大”了。

HTML Template 相信很多人都有所耳聞,簡單的講也就是 HTML5 中的 template 標簽,正常情況下它無色無味,感知不到它的存在,甚至它下面的 img 都不會被下載,script 都不會被執行。 template 就如它的名字一樣,它只是一個模版,只有到你用到它時,它才會變得有意義。

Shadow DOM 則是原生組件封裝的基本工具,它可以實現組件與組件之間的獨立性。

Custom Elements 是用來包裝原生組件的容器,通過它,你就只需要寫一個標簽,就能得到一個完整的組件。

HTML Imports 則是 HTML 中類似于 ES6 Module 的一個東西,你可以直接 import 另一個 html 文件,然后使用其中的 DOM 節點。但是,由于 HTML Imports 和 ES6 Module 實在是太像了,并且除了 Chrome 以外沒有瀏覽器愿意實現它,所以它已經被廢棄并不推薦使用了。未來會使用 ES6 Module 來取代它,但是現在貌似還沒有取代的方案,在新版的 Chrome 中這個功能已經被刪除了,并且在使用的時候會在 Console 中給出警告。警告中說使用 ES Modules 來取代,但是我測試在 Chrome 71 中 ES Module 會強制檢測文件的 MIME 類型必須為 JavaScript 類型,應該是暫時還沒有實現支持。

2508713701-5bc7f09060787_articlex.png

Shadow DOM

要說原生 HTML 組件,就要先聊聊 Shadow DOM 到底是個什么東西。

大家對 DOM 都很熟悉了,在 HTML 中作為一個最基礎的骨架而存在,它是一個樹結構,樹上的每一個節點都是 HTML 中的一部分。DOM 作為一棵樹,它擁有著上下級的層級關系,我們通常使用“父節點”、“子節點”、“兄弟節點”等來進行描述(當然有人覺得這些稱謂強調性別,所以也創造了一些性別無關的稱謂)。子節點在一定程度上會繼承父節點的一些東西,也會因兄弟節點而產生一定的影響,比較明顯的是在應用 CSS Style 的時候,子節點會從父節點那里繼承一些樣式。

而 Shadow DOM,也是 DOM 的一種,所以它也是一顆樹,只不過它是長在 DOM 樹上的一棵特殊的紫薯,啊不,子樹。

什么?DOM 本身不就是由一棵一棵的子樹組成的嗎?這個 Shadow DOM 有什么特別的嗎?

Shadow DOM 的特別之處就在于它致力于創建一個相對獨立的一個空間,雖然也是長在 DOM 樹上的,但是它的環境卻是與外界隔離的,當然這個隔離是相對的,在這個隔離空間中,你可以選擇性地從 DOM 樹上的父節點繼承一些屬性,甚至是繼承一棵 DOM 樹進來。

利用 Shadow DOM 的隔離性,我們就可以創造原生的 HTML 組件了。

實際上,瀏覽器已經通過 Shadow DOM 實現了一些組件了,只是我們使用過卻沒有察覺而已,這也是 Shadow DOM 封裝的組件的魅力所在:你只管寫一個 HTML 標簽,其他的交給我。(是不是有點像 React 的 JSX ???)

我們來看一看瀏覽器利用 Shadow DOM 實現的一個示例吧,那就是 video 標簽:

 video controls src= ./video.mp4 width= 400 height= 300 /video 

我們來看一下瀏覽器渲染的結果:

3986383008-5bc7f0af9a354_articlex.png

等一下!不是說 Shadow DOM 嗎?這和普通 DOM 有啥區別???

在 Chrome 中,Elements 默認是不顯示內部實現的 Shadow DOM 節點的,需要在設置中啟用:

4254440722-5bc7f0c9331b5_articlex.png

3551890025-5bc7f0d516ea6_articlex.png

注:瀏覽器默認隱藏自身的 Shadow DOM 實現,但如果是用戶通過腳本創造的 Shadow DOM,是不會被隱藏的。

然后,我們就可以看到 video 標簽的真面目了:

925947494-5bc7f0fadca05_articlex.png

在這里,你可完全像調試普通 DOM 一樣隨意調整 Shadow DOM 中的內容(反正和普通 DOM 一樣,刷新一下就恢復了)。

我們可以看到上面這些 shadow DOM 中的節點大多都有 pseudo 屬性,根據這個屬性,你就可以在外面編寫 CSS 樣式來控制對應的節點樣式了。比如,將上面這個 pseudo= -webkit-media-controls-overlay-play-button 的 input 按鈕的背景色改為橙色:

video::-webkit-media-controls-overlay-play-button { background-color: orange;}

2935846866-5bc7f11a17342_articlex.png

由于 Shadow DOM 實際上也是 DOM 的一種,所以在 Shadow DOM 中還可以繼續嵌套 Shadow DOM,就像上面那樣。

瀏覽器中還有很多 Element 都使用了 Shadow DOM 的形式進行封裝,比如 input 、 select 、 audio 等,這里就不一一展示了。

由于 Shadow DOM 的隔離性,所以即便是你在外面寫了個樣式:div { background-color: red !important; },Shadow DOM 內部的 div 也不會受到任何影響

也就是說,寫樣式的時候,該用 id 的時候就用 id,該用 class 的時候就用 class,一個按鈕的 class 應該寫成 .button 就寫成 .button。完全不用考慮當前組件中的 id、class 可能會與其他組件沖突,你只要確保一個組件內部不沖突就好——這很容易做到。

這解決了現在絕大多數的組件化框架都面臨的問題:Element 的 class(className) 到底怎么寫?用前綴命名空間的形式會導致 class 名太長,像這樣:.header-nav-list-sublist-button-icon;而使用一些 CSS-in-JS 工具,可以創造一些唯一的 class 名稱,像這樣:.Nav__welcomeWrapper___lKXTg,這樣的名稱仍舊有點長,還帶了冗余信息。

ShadowRoot

ShadowRoot 是 Shadow DOM 下面的根,你可以把它當做 DOM 中的 body 一樣看待,但是它不是 body ,所以你不能使用 body 上的一些屬性,甚至它不是一個節點。

你可以通過 ShadowRoot 下面的 appendChild、querySelectorAll 之類的屬性或方法去操作整個 Shadow DOM 樹。

對于一個普通的 Element,比如 p ,你可以通過調用它上面的 attachShadow 方法來創建一個 ShadowRoot(還有一個 createShadowRoot 方法,已經過時不推薦使用),attachShadow 接受一個對象進行初始化:{ mode: open },這個對象有一個 mode 屬性,它有兩個取值: open 和 closed ,這個屬性是在創造 ShadowRoot 的時候需要初始化提供的,并在創建 ShadowRoot 之后成為一個只讀屬性。

mode: open 和 mode: closed 有什么區別呢?在調用 attachShadow 創建 ShadowRoot 之后,attachShdow 方法會返回 ShadowRoot 對象實例,你可以通過這個返回值去構造整個 Shadow DOM。當 mode 為 open 時,在用于創建 ShadowRoot 的外部普通節點(比如 p )上,會有一個 shadowRoot 屬性,這個屬性也就是創造出來的那個 ShadowRoot,也就是說,在創建 ShadowRoot 之后,還是可以在任何地方通過這個屬性再得到 ShadowRoot,繼續對其進行改造;而當 mode 為 closed 時,你將不能再得到這個屬性,這個屬性會被設置為 null,也就是說,你只能在 attachShadow 之后得到 ShadowRoot 對象,用于構造整個 Shadow DOM,一旦你失去對這個對象的引用,你就無法再對 Shadow DOM 進行改造了。

可以從上面 Shadow DOM 的截圖中看到 #shadow-root (user-agent) 的字樣,這就是 ShadowRoot 對象了,而括號中的 user-agent 表示這是瀏覽器內部實現的 Shadow DOM,如果使用通過腳本自己創建的 ShadowRoot,括號中會顯示為 open 或 closed 表示 Shadow DOM 的 mode。

3465801305-5bc7f119096b3_articlex.png

瀏覽器內部實現的 user-agent 的 mode 為 closed,所以你不能通過節點的 ShadowRoot 屬性去獲得其 ShadowRoot 對象,也就意味著你不能通過腳本對這些瀏覽器內部實現的 Shadow DOM 進行改造。

HTML Template

有了 ShadowRoot 對象,我們可以通過代碼來創建內部結構了,對于簡單的結構,也許我們可以直接通過 document.createElement 來創建,但是稍微復雜一些的結構,如果全部都這樣來創建不僅麻煩,而且代碼可讀性也很差。當然也可以通過 ES6 提供的反引號字符串(const template = `......`;)配合 innerHTML 來構造結構,利用反引號字符串中可以任意換行,并且 HTML 對縮進并不敏感的特性來實現模版,但是這樣也是不夠優雅,畢竟代碼里大段大段的 HTML 字符串并不美觀,即便是單獨抽出一個常量文件也是一樣。

這個時候就可以請 HTML Template 出場了。我們可以在 html 文檔中編寫 DOM 結構,然后在 ShadowRoot 中加載過來即可。

HTML Template 實際上就是在 html 中的一個 template 標簽,正常情況下,這個標簽下的內容是不會被渲染的,包括標簽下的 img、style、script 等都是不會被加載或執行的。你可以在腳本中使用 getElementById 之類的方法得到 template 標簽對應的節點,但是卻無法直接訪問到其內部的節點,因為默認他們只是模版,在瀏覽器中表現為 #document-fragment,字面意思就是“文檔片段”,可以通過節點對象的 content 屬性來訪問到這個 document-fragment 對象。

2033065598-5bc7f18543064_articlex.png

通過 document-fragment 對象,就可以訪問到 template 內部的節點了,通過 document.importNode 方法,可以將 document-fragment 對象創建一份副本,然后可以使用一切 DOM 屬性方法替換副本中的模版內容,最終將其插入到 DOM 或是 Shadow DOM 中。

 div id= div /div  template id= temp  div id= title /div  /template 
const template = document.getElementById( temp const copy = document.importNode(template.content, true);copy.getElementById( title ).innerHTML = Hello World! const div = document.getElementById( div const shadowRoot = div.attachShadow({ mode: closed });shadowRoot.appendChild(copy);

HTML Imports

有了 HTML Template,我們已經可以方便地創造封閉的 Web 組件了,但是目前還有一些不完美的地方:我們必須要在 html 中定義一大批的 template ,每個組件都要定義一個 template 。

此時,我們就可以用到已經被廢棄的 HTML Imports 了。雖然它已經被廢棄了,但是未來會通過 ES6 Modules 的形式再進行支持,所以理論上也只是換個加載形式而已。

通過 HTML Imports,我們可以將 template 定義在其他的 html 文檔中,然后再在需要的 html 文檔中進行導入(當然也可以通過腳本按需導入),導入后,我們就可以直接使用其中定義的模版節點了。

已經廢棄的 HTML Imports 通過 link 標簽實現,只要指定 rel= import 就可以了,就像這樣: link rel= import href= ./templates.html ,它可以接受 onload 和 onerror 事件以指示它已經加載完成。當然也可以通過腳本來創建 link 節點,然后指定 rel 和 href 來按需加載。Import 成功后,在 link 節點上有一個 import 屬性,這個屬性中存儲的就是 import 進來的 DOM 樹啦,可以 querySelector 之類的,并通過 cloneNode 或 document.importNode 方法創建副本后使用。

未來新的 HTML Imports 將會以 ES6 Module 的形式提供,可以在 JavaScript 中直接 import * as template from ./template.html ,也可以按需 import,像這樣:const template = await import( ./template.html 。不過目前雖然瀏覽器都已經支持 ES6 Modules,但是在 import 其他模塊時會檢查服務端返回文件的 MIME 類型必須為 JavaScript 的 MIME 類型,否則不允許加載。

Custom Elements

有了上面的三個組件標準,我們實際上只是對 HTML 進行拆分而已,將一個大的 DOM 樹拆成一個個相互隔離的小 DOM 樹,這還不是真正的組件。

要實現一個真正的組件,我們就需要用到 Custom Elements 了,就如它的名字一樣,它是用來定義原生組件的。

Custom Elements 的核心,實際上就是利用 JavaScript 中的對象繼承,去繼承 HTML 原生的 HTMLElement 類(或是具體的某個原生 Element 類,比如 HTMLButtonElement),然后自己編寫相關的生命周期函數,處理成員屬性以及用戶交互的事件。

看起來這和現在的 React 很像,在 React 中,你可以這樣創造一個組件:class MyElement extends React.Component { ... },而使用原生 Custom Elements,你需要這樣寫:class MyElement extends HTMLElement { ... }。

Custom Elements 的生命周期函數并不多,但是足夠使用。這里我將 Custom Elements 的生命周期函數與 React 進行一個簡單的對比:

constructor(): 構造函數,用于初始化 state、創建 Shadow DOM、監聽事件之類。

對應 React 中 Mounting 階段的大半部分,包括:constructor(props)、static getDerivedStateFromProps(props, state) 和 render()。

在 Custom Elements 中,constructor() 構造函數就是其原本的含義:初始化,和 React 的初始化類似,但它沒有像 React 中那樣將其拆分為多個部分。在這個階段,組件僅僅是被創建出來(比如通過 document.createElement()),但是還沒有插入到 DOM 樹中。

connectedCallback(): 組件實例已被插入到 DOM 樹中,用于進行一些展示相關的初始化操作。

對應 React 中 Mounting 階段的最后一個生命周期:componentDidMount()。

在這個階段,組件已經被插入到 DOM 樹中了,或是其本身就在 html 文件中寫好在 DOM 樹上了,這個階段一般是進行一些展示相關的初始化,比如加載數據、圖片、音頻或視頻之類并進行展示。

attributeChangedCallback(attrName, oldVal, newVal): 組件屬性發生變化,用于更新組件的狀態。

對應 React 中的 Updating 階段:static getDerivedStateFromProps(props, state)、shouldComponentUpdate(nextProps, nextState)、render()、getSnapshotBeforeUpdate(prevProps, prevState) 和 componentDidUpdate(prevProps, prevState, snapshot)。

當組件的屬性(React 中的 props)發生變化時觸發這個生命周期,但是并不是所有屬性變化都會觸發,比如組件的 class、style 之類的屬性發生變化一般是不會產生特殊交互的,如果所有屬性發生變化都觸發這個生命周期的話,會使得性能造成較大的影響。所以 Custom Elements 要求開發者提供一個屬性列表,只有當屬性列表中的屬性發生變化時才會觸發這個生命周期函數。

這個屬性列表通過組件類上的一個靜態只讀屬性來聲明,在 ES6 Class 中使用一個 getter 函數來實現,只實現 getter 而不實現 setter,getter 返回一個常量,這樣就是只讀的了。像這樣:

class AwesomeElement extends HTMLElement { static get observedAttributes() { return [ awesome }

disconnectedCallback(): 組件被從 DOM 樹中移除,用于進行一些清理操作。

對應 React 中的 Unmounting 階段:componentWillUnmount()。

adoptedCallback(): 組件實例從一個文檔被移動到另一個文檔。

這個生命周期是原生組件獨有的,React 中沒有類似的生命周期。這個生命周期函數也并不常用到,一般在操作多個 document 的時候會遇到,調用 document.adoptNode() 函數轉移節點所屬 document 時會觸發這個生命周期。

在定義了自定義組件后,我們需要將它注冊到 HTML 標簽列表中,通過 window.customElements.define() 函數即可實現,這個函數接受兩個必須參數和一個可選參數。第一個參數是注冊的標簽名,為了避免和 HTML 自身的標簽沖突,Custom Elements 要求用戶自定義的組件名必須至少包含一個短杠 -,并且不能以短杠開頭,比如 my-element、awesome-button 之類都是可以的。第二個參數是注冊的組件的 class,直接將繼承的子類類名傳入即可,當然也可以直接寫一個匿名類:

window.customElements.define( my-element , class extends HTMLElement {});

注冊之后,我們就可以使用了,可以直接在 html 文檔中寫對應的標簽,比如: my-element /my-element ,也可以通過 document.createElement( my-element ) 來創建,用法與普通標簽幾乎完全一樣。但要注意的是,雖然 html 標準中說部分標簽可以不關閉或是自關閉( br 或是 br / ),但是只有規定的少數幾個標簽允許自關閉,所以,在 html 中寫 Custom Elements 的節點時必須帶上關閉標簽。

由于 Custom Elements 是通過 JavaScript 來定義的,而一般 js 文件都是通過 script 標簽外聯的,所以 html 文檔中的 Custom Elements 在 JavaScript 未執行時是處于一個默認的狀態,瀏覽器默認會將其內容直接顯示出來。為了避免這樣的情況發生,Custom Elements 在被注冊后都會有一個 :defined CSS 偽類而在注冊前沒有,所以我們可以通過 CSS 選擇器在 Custom Elements 注冊前將其隱藏起來,比如:

my-element:not(:defined) { display: none;}

或者 Custom Elements 也提供了一個函數來檢測指定的組件是否已經被注冊:customElements.whenDefined(),這個函數接受一個組件名參數,并返回一個 Promise,當 Promise 被 resolve 時,就表示組件被注冊了。

這樣,我們就可以放心的在加載 Custom Elements 的 JavaScript 的 script 標簽上使用 async 屬性來延遲加載了(當然,如果是使用 ES6 Modules 形式的話默認的加載行為就會和 defer 類似)。

1246191307-5bc7f1ac6b9fe_articlex.png

Custom Elements + Shadow DOM

使用 Custom Elements 來創建組件時,通常會與 Shadow DOM 進行結合,利用 Shadow DOM 的隔離性,就可以創造獨立的組件。

通常在 Custom Elements 的 constructor() 構造函數中去創建 Shadow DOM,并對 Shadow DOM 中的節點添加事件監聽、對特定事件觸發原生 Events 對象。

正常編寫 html 文檔時,我們可能會給 Custom Elements 添加一些子節點,像這樣: my-element h1 Title /h1 p Content /p /my-element ,而我們創建的 Shadow DOM 又擁有其自己的結構,怎樣將這些子節點放置到 Shadow DOM 中正確的位置上呢?

在 React 中,這些子節點被放置在 props 的 children 中,我們可以在 render() 時選擇將它放在哪里。而在 Shadow DOM 中有一個特殊的標簽: slot ,這個標簽的用處就如同其字面意思,在 Shadow DOM 上放置一個“插槽”,然后 Custom Elements 的子節點就會自動放置到這個“插槽”中了。

有時我們需要更加精確地控制子節點在 Shadow DOM 中的位置,而默認情況下,所有子節點都會被放置在同一個 slot 標簽下,即便是你寫了多個 slot 。那怎樣更精確地對子節點進行控制呢?

默認情況下, slot Fallback /slot 這樣的是默認的 slot ,只有第一個默認的 slot 會有效,將所有子節點全部放進去,如果沒有可用的子節點,將會顯示默認的 Fallback 內容(Fallback 可以是一棵子 DOM 樹)。

slot 標簽有一個 name 屬性,當你提供 name 后,它將變為一個“有名字的 slot ”,這樣的 slot 可以存在多個,只要名字各不相同。此時他們會自動匹配 Custom Elements 下帶 slot 屬性并且 slot 屬性與自身 name 相同的子節點,像這樣

 template id= list  div  h1 Others /h1  slot /slot  /div  div  h1 Animals /h1  slot name= animal /slot  /div  div  h1 Fruits /h1  slot name= fruit /slot  /div  /template  my-list  div slot= animal Cat /div  div slot= fruit Apple /div  div slot= fruit Banana /div  div slot= other flower /div  div pencil /div  div slot= animal Dog /div  div slot= fruit peach /div  div red /div  /my-list 
class MyList extends HTMLElement { constructor() { super(); const root = this.attachShadow({ mode: open }); const template = document.getElementById( list  root.appendChild(document.importNode(template.content, true));customElements.define( my-list , MyList);

這樣就可以得到如圖所示的結構,#shadow-root (open) 表示這是一個開放的 Shadow DOM,下面的節點是直接從 template 中 clone 過來的,瀏覽器自動在三個 slot 標簽下放置了幾個灰色的 div 節點,實際上這些灰色的 div 節點表示的是到其真實節點的“引用”,鼠標移動到他們上會顯示一個 reveal 鏈接,點擊這個鏈接即可跳轉至其真實節點。

1523399701-5bc7f1ce6b75a_articlex.png

這里我們可以看到,雖然 my-list 下的子節點是亂序放置的,但是只要是給定了 slot 屬性,就會被放置到正確的 slot 標簽下。注意觀察其中有一個 div slot= other flower /div ,這個節點由于指定了 slot= other ,但是卻找不到匹配的 slot 標簽,所以它不會被顯示在結果中。

在為 Custom Elements 下的 Shadow DOM 設置樣式的時候,我們可以直接在 Shadow DOM 下放置 style 標簽,也可以放置 link rel= stylesheet ,Shadow DOM 下的樣式都是局部的,所以不用擔心會影響到 Shadow DOM 的外部。并且由于這些樣式僅影響局部,所以對性能也有很大的提升。

在 Shadow DOM 內部的樣式中,也有一些特定的選擇器,比如 :host 選擇器,代表著 ShadowRoot,這類似于普通 DOM 中的 :root,并且它可以與其他偽類組合使用,比如當鼠標在組件上時::host(:hover),當組件擁有某個 class 時::host(.awesome),當組件擁有 disabled 屬性時::host([disabled])……但是 :host 是擁有繼承屬性的,所以如果在 Custom Elements 外部定義了某些樣式,將會覆蓋 :host 中的樣式,這樣就可以輕松地實現各式各樣的“主題風格”了。

為了實現自定義主題,我們還可以使用 Shadow DOM 提供的 :host-context() 選擇器,這個選擇器允許檢查 Shadow DOM 的任何祖先節點是否包含指定選擇器。比如如果在最外層 DOM 的 html 或 body 上有一個 class:.night,則 Shadow DOM 內就可以使用 :host-context(.night) 來指定一個夜晚的主題。這樣可以實現主題樣式的繼承。

還有一種樣式的定義方式是利用 CSS 變量。我們在 Shadow DOM 中使用變量來指定樣式,比如:background-color: var(--bg-colour, #0F0);,這樣就可以在 Shadow DOM 外面指定 --bg-colour 變量來設置樣式了,如果沒有指定變量,將使用默認的樣式顏色 #0F0。

有時我們需要在 Shadow DOM 內部使用完全自定義的樣式,比如字體樣式、字體大小,如果任由其繼承可能導致布局錯亂,而每次在組件外面指定樣式又略顯麻煩,并且也破壞了組件的封裝性。所以,Shadow DOM 提供了一個 all 屬性,只要指定 :host{ all: initial; } 就可以重置所有繼承的屬性。

Demo

Web Components 的 Demo 在網上已經有很多了,這是我 2 年前初次接觸 ES6 與 Web Components 的時候寫的一個 Demo:https://github.com/jinliming2/Calendar-js,一個日歷,當時還是 v0 的規范,并且在 Firefox 下還存在會導致 Firefox 崩潰的 Bug(感覺是 Firefox 在實現 Shadow DOM 時的 Bug)。目前這個 Demo 已經不能在 Firefox 下運行了,因為 Firefox 已經刪除了 v0 規范,開始實行 v1 標準了,所以近期我可能會重構一下這個 Demo。

以上就是你知道原生HTML組件是什么嗎?原生HTML組件的介紹的詳細內容,html教程

鄭重聲明:本文版權歸原作者所有,轉載文章僅為傳播更多信息之目的,如作者信息標記有誤,請第一時間聯系我們修改或刪除,多謝。

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
不卡中文字幕av| 国产在线拍揄自揄视频不卡99| 国产成人精品一区二区在线| 亚洲精品一区二三区不卡| 中国日韩欧美久久久久久久久| 精品福利视频导航| **欧美日韩vr在线| 奇米影视亚洲狠狠色| 亚洲美女性生活视频| 狠狠做深爱婷婷久久综合一区| 欧美性开放视频| 日本精品久久久久影院| 欧美性猛交丰臀xxxxx网站| 色偷偷av一区二区三区| 91福利视频网| 亚洲人成网站免费播放| 亚洲欧洲成视频免费观看| 亚洲一区二区三区四区在线播放| 欧美一性一乱一交一视频| 亚洲综合小说区| 日韩av免费在线观看| 91精品在线影院| 亚洲欧美精品在线| 久久久中精品2020中文| 亚洲综合在线中文字幕| 在线精品国产成人综合| 日韩三级成人av网| 午夜精品久久久久久久久久久久| 国产精品美女久久久久久免费| 日韩av一区二区在线观看| 2025国产精品视频| 精品国产91久久久| 国产视频久久久久久久| 欧美精品情趣视频| 欧美大片欧美激情性色a∨久久| 亚洲人成免费电影| 国内伊人久久久久久网站视频| 欧美色播在线播放| 国产精品视频yy9099| 国产精品福利片| 91理论片午午论夜理片久久| 一区二区三区亚洲| 日韩小视频在线观看| 国产成人jvid在线播放| 成人午夜在线影院| 中文字幕久久精品| 国产亚洲a∨片在线观看| 国产精品成人一区二区三区吃奶| 成人在线国产精品| 日韩精品免费看| 亚洲香蕉成视频在线观看| 亚洲成年网站在线观看| 免费不卡在线观看av| 国产激情综合五月久久| 国产在线观看精品| 欧美国产在线电影| 久久久久久97| 国产成人精品电影久久久| 97久久国产精品| 欧美一区二三区| 日本久久久久久久久久久| 久久夜精品va视频免费观看| 亚洲跨种族黑人xxx| 国内精品伊人久久| 亚洲精品大尺度| 播播国产欧美激情| 欧美俄罗斯性视频| 国产精品激情av在线播放| 4444欧美成人kkkk| 亚洲精品国产suv| 久久亚洲国产精品成人av秋霞| 亚洲欧美精品一区| 一本大道久久加勒比香蕉| 91免费欧美精品| 欧美猛男性生活免费| 欧美成人精品一区| 日韩电影在线观看免费| 亚洲午夜小视频| 韩国一区二区电影| 亚洲aa中文字幕| 日韩成人av网址| 最近免费中文字幕视频2019| 国产精品成人aaaaa网站| 隔壁老王国产在线精品| 日韩亚洲欧美中文高清在线| 国产日韩欧美中文| 情事1991在线| 欧美不卡视频一区发布| 欧美成人精品在线视频| 国产精品成久久久久三级| 97视频免费观看| 久久久精品视频成人| 国产成人精品在线播放| 亚洲第一中文字幕| 久久在线观看视频| 成人精品一区二区三区电影免费| 日韩小视频在线观看| 欧洲s码亚洲m码精品一区| 色哟哟亚洲精品一区二区| 久久久国产一区二区三区| 精品在线小视频| 中文字幕亚洲欧美日韩2019| 国产成人精品久久二区二区| 日韩精品免费电影| 九色精品美女在线| 热久久免费国产视频| 一区二区三区日韩在线| 精品日韩美女的视频高清| 午夜精品蜜臀一区二区三区免费| 97高清免费视频| 久久久亚洲欧洲日产国码aⅴ| 亚洲一区二区三区久久| 亚洲性视频网站| 亚洲欧洲成视频免费观看| 日韩黄色在线免费观看| 日韩欧美在线国产| 国产精品视频永久免费播放| 国产精品99蜜臀久久不卡二区| 国产一区二区久久精品| 亚洲最大成人免费视频| 亚洲电影成人av99爱色| 欧美性猛交xxxx乱大交蜜桃| 午夜精品久久久久久久99黑人| 欧美日韩一区二区三区| 国产在线98福利播放视频| 日韩av影片在线观看| 亚洲伊人一本大道中文字幕| 国产精品免费观看在线| 91国产视频在线| 丝袜亚洲欧美日韩综合| 成人免费在线视频网站| 亚洲www在线| 国产精品爽黄69| 伊是香蕉大人久久| 久久av红桃一区二区小说| 欧美二区乱c黑人| 国产成人精品一区二区| 久久精品2019中文字幕| 亚洲性线免费观看视频成熟| 在线激情影院一区| 一区二区三区回区在观看免费视频| 国产精品久久久久9999| 国模gogo一区二区大胆私拍| 亚洲自拍欧美色图| 日韩小视频在线观看| 国产亚洲精品综合一区91| 国产精品视频免费观看www| 91国在线精品国内播放| 深夜成人在线观看| 成人网在线观看| 欧美国产精品va在线观看| 欧美激情久久久久| 91精品久久久久久综合乱菊| 91精品久久久久久久久久久久久| www.日韩视频| 成人女保姆的销魂服务| 国产成人一区三区| 91久久精品美女高潮| 亚洲va电影大全| 亚洲欧美成人在线| 中文字幕视频在线免费欧美日韩综合在线看| 成人精品福利视频| 久久久精品一区二区三区| 亚洲91av视频|