国产秋霞理论久久久电影-婷婷色九月综合激情丁香-欧美在线观看乱妇视频-精品国avA久久久久久久-国产乱码精品一区二区三区亚洲人-欧美熟妇一区二区三区蜜桃视频

Web Component -- 即將爆發(fā)的原生的 UI 組件化標(biāo)準(zhǔn)

共 28638字,需瀏覽 58分鐘

 ·

2023-10-16 02:49

f2cd9769613f03d5d59e6ecaf301ea1b.webp

Web Component 概述

Web Component 是一種用于構(gòu)建可復(fù)用用戶(hù)界面組件的技術(shù),開(kāi)發(fā)者可以創(chuàng)建自定義的 HTML 標(biāo)簽,并將其封裝為包含邏輯和樣式的獨(dú)立組件,從而在任何 Web 應(yīng)用中重復(fù)使用。

每個(gè) Web Component 都具有自己的 DOM 和樣式隔離,避免了全局 CSS 和 JavaScript 的沖突問(wèn)題。它還支持自定義事件和屬性,可以與其他組件進(jìn)行通信和交互。

不同于 Vue/React 等社區(qū)或廠商的組件化開(kāi)發(fā)方案,Web Component 被定義在標(biāo)準(zhǔn)的 HTML 和 DOM 標(biāo)準(zhǔn)中。它由一組相關(guān)的 Web 平臺(tái) API 組成,也可以與現(xiàn)有的前端框架和庫(kù)配合使用。

Web Component 的兼容性良好,可以在現(xiàn)代瀏覽器中直接使用,也可以通過(guò) polyfill 兼容到舊版瀏覽器(IE11 理論上可以兼容,出于初步調(diào)研的考慮,本文不對(duì)兼容性作過(guò)多探討)。

同類(lèi)組件化方案比較

Pros 技術(shù) Cons
可以異構(gòu) Micro Frontend 需要主應(yīng)用、對(duì)子應(yīng)用有侵入、樣式統(tǒng)一困難
模塊級(jí)的多項(xiàng)目在運(yùn)行時(shí)共享 Module Federation 主要依賴(lài)webpack5,既有項(xiàng)目改造成本未知;實(shí)現(xiàn)異構(gòu)引用需要借助其他插件
模塊級(jí)動(dòng)態(tài)共享 Vue :is + 動(dòng)態(tài)import 依賴(lài)vue技術(shù)棧
可以異構(gòu)、完全解耦、對(duì)原有開(kāi)發(fā)方法改造極小 Web Compnent IE兼容性?xún)H11可通過(guò)Polyfill支持

TL;DR

6225ac21c419f79dbcbd9dd4c1bb44aa.webp 實(shí)例:用異構(gòu)系統(tǒng)共建 web components
https://gitee.com/tonylua/web-component-test1/tree/master

Web Component 關(guān)鍵特性

Custom Elements(自定義元素)

是 Web 標(biāo)準(zhǔn)中的一項(xiàng)功能,它允許開(kāi)發(fā)者自定義新的 HTML 元素,開(kāi)發(fā)者可以使用 JavaScript 和 DOM API,使新元素具有自定義的行為和功能

4.13.1.1 Creating an autonomous custom element

This section is non-normative.

For the purposes of illustrating how to create an autonomous custom element, let's define a custom element that encapsulates rendering a small icon for a country flag. Our goal is to be able to use it like so:

        
        <flag-icon country="nl"></flag-icon>

To do this, we first declare a class for the custom element, extending HTMLElement:

        
        class FlagIcon extends HTMLElement {
  constructor() {
    super();
    this._countryCode = null;
  }

  static observedAttributes = ["country"];

  attributeChangedCallback(name, oldValue, newValue) {
    // name will always be "country" due to observedAttributes
    this._countryCode = newValue;
    this._updateRendering();
  }
  connectedCallback() {
    this._updateRendering();
  }

  get country() {
    return this._countryCode;
  }
  set country(v) {
    this.setAttribute("country", v);
  }

  _updateRendering() {
    ...
  }
}

We then need to use this class to define the element:

        
        customElements.define("flag-icon", FlagIcon);
  • 繼承自基類(lèi) HTMLElement
  • 自定義的元素名稱(chēng)需符合 DOMString 標(biāo)準(zhǔn),簡(jiǎn)單來(lái)說(shuō)就是必須帶短橫線
  • 其中 observedAttributes 聲明的屬性才能被 attributeChangedCallback() 監(jiān)聽(tīng)
  • 完整生命周期方法說(shuō)明為:
      
      class MyCustomElement extends HTMLElement {
  constructor() {
    super();
    // 在構(gòu)造函數(shù)中進(jìn)行初始化操作
    // 用 this.appendChild(...) 等掛載到dom中
    // 用 addEventListener() 綁定事件到 this.xxx 上
  }
  connectedCallback() {
    // 元素被插入到文檔時(shí)觸發(fā),等價(jià)于 vue 的 mounted
  }
  disconnectedCallback() {
    // 元素從文檔中移除時(shí)觸發(fā),等價(jià)于 vue 的 beforeDestory / destoyed
  }
  attributeChangedCallback(attributeName, oldValue, newValue) {
    // 元素的屬性被添加、移除或更改時(shí)觸發(fā),等價(jià)于 vue 的 beforeUpdate / updated
  }
}

除了繼承 HTMLElement,也可以繼承其既有子類(lèi),并在使用是采用原生標(biāo)簽(被繼承類(lèi)) + is 語(yǔ)法,如:

      
      // Create a class for the element
class WordCount extends HTMLParagraphElement {
  constructor() {
    // Always call super first in constructor
    super();

    // Constructor contents omitted for brevity
    // …
  }
}

// Define the new element
customElements.define("word-count", WordCount, { extends: "p" });
      
      <p is="word-count"></p>

Shadow DOM

DOM 編程模型令人詬病的一個(gè)方面就是缺乏封裝,不同組件之間的邏輯和樣式很容易互相污染。

鑒于這個(gè)原因,Web components 的一個(gè)重要屬性就是封裝——可以將標(biāo)記結(jié)構(gòu)、樣式和行為隱藏起來(lái),并與頁(yè)面上的其他代碼相隔離。其中,Shadow DOM 接口是關(guān)鍵所在,它可以將一個(gè)隱藏的、獨(dú)立的 DOM 附加到一個(gè)元素上

Shadow DOM 是 DOM nodes 的附屬樹(shù)。這種 Shadow DOM 子樹(shù)可以與某宿主元素相關(guān)聯(lián),但并不作為該元素的普通子節(jié)點(diǎn),而是會(huì)形成其自有的作用域;Shadow DOM 中的根及其子節(jié)點(diǎn)也不可見(jiàn)。

相比于以前為了實(shí)現(xiàn)封裝而只能使用 <iframe> 實(shí)現(xiàn)的情況,Shadow DOM 無(wú)疑是一種更優(yōu)雅的創(chuàng)建隔離 DOM 樹(shù)的方法。

Shadow DOM 允許將隱藏的 DOM 樹(shù)附加到常規(guī)的 DOM 樹(shù)中——它以 shadow root 節(jié)點(diǎn)為起始根節(jié)點(diǎn),在這個(gè)根節(jié)點(diǎn)的下方,可以是任意元素,和普通的 DOM 元素一樣。

e41c7403822e209ba44c0beffb5a9cc7.webp

這里,有一些 Shadow DOM 特有的術(shù)語(yǔ)需要我們了解:

  • Shadow host:一個(gè)常規(guī) DOM 節(jié)點(diǎn),Shadow DOM 會(huì)被附加到這個(gè)節(jié)點(diǎn)上。
  • Shadow tree:Shadow DOM 內(nèi)部的 DOM 樹(shù)。
  • Shadow boundary:Shadow DOM 結(jié)束的地方,也是常規(guī) DOM 開(kāi)始的地方。
  • Shadow root: Shadow tree 的根節(jié)點(diǎn)。

你可以使用同樣的方式來(lái)操作 Shadow DOM,就和操作常規(guī) DOM 一樣——例如添加子節(jié)點(diǎn)、設(shè)置屬性,以及為節(jié)點(diǎn)添加自己的樣式(例如通過(guò) element.style 屬性),或者為整個(gè) Shadow DOM 添加樣式(例如在 <style> 元素內(nèi)添加樣式)。不同的是,Shadow DOM 內(nèi)部的元素始終不會(huì)影響到它外部的元素(除了 :focus-within),這為封裝提供了便利。

注意,不管從哪個(gè)方面來(lái)看,Shadow DOM 都不是一個(gè)新事物——在過(guò)去的很長(zhǎng)一段時(shí)間里,瀏覽器用它來(lái)封裝一些元素的內(nèi)部結(jié)構(gòu)。以一個(gè)有著默認(rèn)播放控制按鈕的 <video> 元素為例。你所能看到的只是一個(gè) <video> 標(biāo)簽,實(shí)際上,在它的 Shadow DOM 中,包含了一系列的按鈕和其他控制器。Shadow DOM 標(biāo)準(zhǔn)允許你為你自己的元素(custom element)維護(hù)一組 Shadow DOM。

基本用法

可以使用 Element.attachShadow() 方法來(lái)將一個(gè) shadow root 附加到任何一個(gè)元素上。它接受一個(gè)配置對(duì)象作為參數(shù),該對(duì)象有一個(gè) mode 屬性,值可以是 open 或者 closed

        
        let shadow = elementRef.attachShadow({ mode: "open" });
let shadow = elementRef.attachShadow({ mode: "closed" });

open 表示可以通過(guò)頁(yè)面內(nèi)的 JavaScript 方法來(lái)獲取 Shadow DOM,例如使用 Element.shadowRoot 屬性:

        
        let myShadowDom = myCustomElem.shadowRoot;

如果你將一個(gè) Shadow root 附加到一個(gè) Custom element 上,并且將 mode 設(shè)置為 closed,那么就不可以從外部獲取 Shadow DOM 了——myCustomElem.shadowRoot 將會(huì)返回 null。瀏覽器中的某些內(nèi)置元素就是如此,例如<video>,包含了不可訪問(wèn)的 Shadow DOM。

如果你想將一個(gè) Shadow DOM 附加到 custom element 上,可以在 custom element 的構(gòu)造函數(shù)中添加如下實(shí)現(xiàn)(目前,這是 shadow DOM 最實(shí)用的用法):

        
        let shadow = this.attachShadow({ mode: "open" });

將 Shadow DOM 附加到一個(gè)元素之后,就可以使用 DOM APIs 對(duì)它進(jìn)行操作,就和處理常規(guī) DOM 一樣。

        
        var para = document.createElement('p');
shadow.appendChild(para);
etc.

注意:

  • 要使用 Chrome 調(diào)試器檢查 Shadow DOM,需要選中調(diào)試器的 Preferences / Elmenets 下的 show user agent shadow DOM 框*;比如對(duì)于上文提到的 <video>,在打開(kāi)該調(diào)試選項(xiàng)后,就能在元素面板中看到 <video> 下掛載的 shadow tree
  • 一些比較舊的資料中會(huì)出現(xiàn) attachShadow() 的前身 createShadowRoot(),語(yǔ)義基本相同;createShadowRoot()已經(jīng)被廢棄,它是在 Shadow DOM v0 規(guī)范中引入的。Shadow DOM 的最新版本是 v1,是 Web 標(biāo)準(zhǔn)的一部分。

HTML templates 和 slot

<template> 元素允許開(kāi)發(fā)者在 HTML 中定義一個(gè)模板,其中可以包含任意的 HTML 結(jié)構(gòu)、文本和變量占位符。此元素及其內(nèi)容不會(huì)在 DOM 中呈現(xiàn),但仍可使用 JavaScript 去引用它。

        
        <template id="my-paragraph">
  <p>My paragraph</p>
</template>

上面的代碼不會(huì)展示在你的頁(yè)面中,直到你用 JavaScript 獲取它的引用,然后添加到 DOM 中,如下面的代碼:

        
        let template = document.getElementById("my-paragraph");
let templateContent = template.content;
document.body.appendChild(templateContent);

模板(Template)本身就是有用的,而與 web 組件(web component)一起使用效果更好。我們定義一個(gè) web 組件使用模板作為陰影(shadow)DOM 的內(nèi)容,叫它 <my-paragraph>

        
        customElements.define(
  "my-paragraph",
  class extends HTMLElement {
    constructor() {
      super();
      let template = document.getElementById("my-paragraph");
      let templateContent = template.content;

      const shadowRoot = this.attachShadow({ mode: "open" });
      shadowRoot.appendChild(templateContent.cloneNode(true));
    }
  },
);

使用 <slot> 則能進(jìn)一步展示不同的自定義內(nèi)容:

        
        <template id="my-paragraph">
  <p><slot name="my-text">My default text</slot></p>
</template>

...

        
        <my-paragraph>
  <ul slot="my-text">
    <li>Let's have some different text!</li>
    <li>In a list!</li>
  </ul>
</my-paragraph>

CSS Scoping(局部作用域的 CSS)

The CSS scoping module defines the CSS scoping and encapsulation mechanisms, focusing on the Shadow DOM scoping mechanism.

根據(jù) Shadow DOM 作用域機(jī)制,CSS scoping 模塊定義了 CSS 作用域和封裝機(jī)制

CSS styles are either global in scope or scoped to a shadow tree. Globally scoped styles apply to all the elements in the node tree that match the selector, including custom elements in that tree, but not to the shadow trees composing each custom element. Selectors and their associated style definitions don't bleed between scopes.

CSS 樣式分為全局和 shadow tree 局部?jī)煞N。全局樣式應(yīng)用于節(jié)點(diǎn)樹(shù)中與選擇器匹配的所有元素,包括該樹(shù)中的自定義元素,但不應(yīng)用于組成每個(gè)自定義元素的shadow tree。選擇器及其關(guān)聯(lián)的樣式定義也不會(huì)在作用域之間流通。

Within the CSS of a shadow tree, selectors don't select elements outside the tree, either in the global scope or in other shadow trees. Each custom element has its own shadow tree, which contains all the components that make up the custom element (but not the custom element, or "host", itself).

在 shadow tree 的 CSS 中,選擇器不會(huì)影響樹(shù)外部的元素 -- 無(wú)論是全局作用域還是其他 shadow tree。每個(gè)自定義元素都有自己的 shadow tree,它包含組成自定義元素的所有組件(但不包含自定義元素或“宿主”本身)。

:host 偽類(lèi)

在 shadow DOM  內(nèi)部,要想為“宿主” shadow host 本身添加樣式,可以用 CSS 選擇器 :host

      
      :host {
  /* ... */
}

:host 選擇器還有一種函數(shù)式的用法,接收一個(gè)選擇器參數(shù),該參數(shù)表示 shadow host 本身具備特定的狀態(tài)或樣式時(shí)才生效,如:

      
      :host(:hover) {
  background-color: #ccc;
}

:host(.active) {
  color: red;
}

:host(.footer) { // 宿主元素包含footer樣式名時(shí)
  color : red; 
}

:host-context 偽類(lèi)

:host(selector) 用法類(lèi)似的還有 :host-context() 偽類(lèi),但所謂 context 的語(yǔ)意指的是,作為其參數(shù)的選擇器指向的是 shadow host 宿主元素的上下文環(huán)境,也就是其作為哪個(gè)祖先元素的后代時(shí)才生效,如:

      
      // 當(dāng)宿主是 h1 后代時(shí)
:host-context(h1) {
  font-weight: bold;
}

// 當(dāng) .dark-theme 類(lèi)應(yīng)用于主文檔 body 時(shí)
:host-context(body.dark-theme) p {
  color: #fff;
}

::part 偽元素

用于在父頁(yè)面指定 shadow DOM 內(nèi)部使用了對(duì)應(yīng) part 屬性元素的樣式:

      
      <html>
<head>
 <template id="template">
  My host element!
  <span part="sp">xxx</span>
 </template>
 <style>
  #host::part(sp) {
   background-color: aqua;
  }
 </style>
</head>
<body>
 <div id="host"></div>
 <script type="text/javascript">
  var template = document.querySelector('#template')
  var root = document.querySelector('#host').attachShadow({ mode: "open" });
  root.appendChild(template.content);
 </script>
</body>
</html>

::part() 在遵循 Shadow DOM 封裝性的同時(shí),提供了一個(gè)安全指定內(nèi)部樣式的途徑。

但這不是唯一的手段,另一種“穿透”方法是通過(guò) CSS 自定義變量

      
      <html>
<head>
   <template id="template">
    <style>
     span {
      background-color: var(--sp-color, red);
     }
    </style>
    My host element will have a blue border!
    <span part="sp">xxx</span>
   </template>
   <style>
    #host {
     --sp-color: blue; // 生效
    }
   </style>
</head>
<body>
   <div id="host"></div>
   <script type="text/javascript">
    var template = document.querySelector('#template')
    var root = document.querySelector('#host').attachShadow({ mode: "open" });
    root.appendChild(template.content);
   </script>
</body>
</html>

::slotted 偽元素

在自定義組件內(nèi)部指定該樣式后,僅有 被外部成功填充的slot 才會(huì)被匹配到,使用默認(rèn)值的 slot 上則不會(huì)生效。

優(yōu)先級(jí)

  • 對(duì)于“宿主”元素,外部樣式優(yōu)先級(jí)高于內(nèi)部的 :host
  • 如果要覆蓋父頁(yè)中設(shè)置的樣式,則必須在宿主元素上內(nèi)聯(lián)完成
  • 外部 ::part 樣式優(yōu)先級(jí)高于內(nèi)部定義

觀察以下例子,優(yōu)先級(jí) blur > green > red:

      
      <head>
 <template id="template">
  <style>
   :host {
    border1px solid red;
    padding10px;
    line-height50px;
   }
  
</style>
  My host element will have a blue border!
 </template>
 <style>
  #host {
   border-color: green;
  }
 
</style>
</head>
<body>
 <div id="host" style="border-color: blue;"></div>
 <script type="text/javascript">
  var template = document.querySelector('#template')
  var root = document.querySelector('#host').attachShadow({ mode"open" });
  root.appendChild(template.content);
 
</script>
</body>

Event retargeting(事件的重定向)

當(dāng) shadow DOM 中發(fā)生的事件在外部被捕獲時(shí),將會(huì)以其 host 元素作為目標(biāo)。

        
        <user-card></user-card>

<script>
customElements.define('user-card', class extends HTMLElement {
  connectedCallback() {
    this.attachShadow({mode: 'open'});
    this.shadowRoot.innerHTML = `<p>
      <button>Click me</button>
    </p>`;
    this.shadowRoot.firstElementChild.onclick =
      e => alert("Inner target: " + e.target.tagName);
  }
});

document.onclick =
  e => alert("Outer target: " + e.target.tagName);
</script>

打印出:
Inner target: BUTTON
Outer target: USER-CARD

外部文檔并不需要知道自定義組件的內(nèi)部情況 -- 從它的角度來(lái)看,事件總是發(fā)生在自定義組件上,除非事件發(fā)生在 slot 的元素上。

        
        <user-card id="userCard">
<span slot="username">John Smith</span>
</user-card>

<script>
customElements.define('user-card', class extends HTMLElement {
  connectedCallback() {
    this.attachShadow({mode: 'open'});
    this.shadowRoot.innerHTML = `<div>
      <b>Name:</b> <slot name="username"></slot>
    </div>`;

    this.shadowRoot.firstElementChild.onclick =
      e => alert("Inner target: " + e.target.tagName);
  }
});

userCard.onclick = e => alert(`Outer target: ${e.target.tagName}`);
</script>

打印出:
Inner target: BUTTON
Outer target: SPAN

從 Shadow DOM 內(nèi)部觸發(fā)事件

如果要發(fā)送自定義事件,可以使用 CustomEvent,注意要設(shè)置冒泡和 composed

      
      this._shadowRoot.dispatchEvent(
  new CustomEvent("weather-fetched", {
    bubbles: true,
    composed: true,
    detail: json,
  })
);

HTML imports

Web Component 標(biāo)準(zhǔn)中被廢棄的一個(gè)草案(有開(kāi)源替代方案),用于引入自定義組件的結(jié)構(gòu)和完整定義,從而可以直接在主頁(yè)面 html 中引用:

        
        <link rel="import" href="module-my-comp.html">

<my-comp />

Web Component 開(kāi)發(fā)框架

除了原生開(kāi)發(fā)方法,社區(qū)中大量既有/特有開(kāi)發(fā)語(yǔ)言,都可以轉(zhuǎn)譯為 Web Component

Polymer

Google 推出的 Web Components 庫(kù),支持?jǐn)?shù)據(jù)的單向和雙向綁定,兼容性較好,跨瀏覽器性能也較好;在語(yǔ)法層面,Polymer 也最接近 Web Components 的原生語(yǔ)法。

      
      import { PolymerElement, html } from '@polymer/polymer/polymer-element.js';
import '@polymer/iron-icon/iron-icon.js'// 一個(gè)圖標(biāo)庫(kù)

class IconToggle extends PolymerElement {
  static get template() {
    return html`
      <style>
        :host {
          display: inline-block;
        }
        iron-icon {
          fillvar(--icon-toggle-color, rgba(0,0,0,0));
          strokevar(--icon-toggle-outline-color, currentcolor);
        }
        :host([pressed]iron-icon {
          fillvar(--icon-toggle-pressed-color, currentcolor);
        }
      
</style>
      <!-- shadow DOM goes here -->
      <iron-icon icon="[[toggleIcon]]"></iron-icon>
    `
;
  }
  static get properties () {
    return {
      toggleIcon: {
        typeString
      },
      pressed: {
        typeBoolean,
        notifytrue,
        reflectToAttributetrue,
        valuefalse
      }
    };
  }
  constructor() {
    super();
    this.addEventListener('click'this.toggle.bind(this));
  }
  toggle() {
    this.pressed = !this.pressed;
  }
}

customElements.define('icon-toggle', IconToggle);

Lit

Google 在 2019 年宣布停止對(duì) Polymer 的進(jìn)一步開(kāi)發(fā),轉(zhuǎn)向支持 Web Components 規(guī)范更好的 Lit;這也是目前社區(qū)中被推薦較多的一個(gè)

The Polymer library is in maintenance mode. For new development, we recommend Lit. -- Google

      
      import {html, css, LitElement} from 'lit';
import {customElement, property} from 'lit/decorators.js';

@customElement('simple-greeting')
export class SimpleGreeting extends LitElement {
  static styles = css`p { color: blue }`;

  @property()
  name = 'Somebody';

  render() {
    return html`<p>Hello, ${this.name}!</p>`;
  }
}
      
      <simple-greeting name="World"></simple-greeting>

React

react 在 v17 版本之后,增加了對(duì)于在 React 組件中使用 web component 的支持:

If you render a tag with a dash, like, React will assume you want to render a custom HTML element. In React, rendering custom elements works differently from rendering built-in browser tags:

All custom element props are serialized to strings and are always set using attributes. Custom elements accept class rather than className, and for rather than htmlFor. If you render a built-in browser HTML element with an is attribute, it will also be treated as a custom element.

      
      import React, { useState }  from 'react';
import './alert.js';

export default function App() {
  const [show, setShow] = useState(true);

  return (
    <div>
      <button onClick={() => setShow(!show)}>toggle alert</button>

      <x-alert hidden={show} status="success" closable oncloseChange={() => setShow(!show)}>
        This is a Web Component in React
      </x-alert>
    </div>
  );
}

而如果想將標(biāo)準(zhǔn) react 組件包裝為 web component,可以在 react 工程中直接結(jié)合 web component 原生語(yǔ)法、使用 React 完成節(jié)點(diǎn)渲染,并導(dǎo)出成獨(dú)立組件。

比如 Github上這個(gè)例子:

      
      import * as React from "react";
import * as ReactDom from "react-dom";
import { FetchData } from "./fetch-data";

class StandaloneComponent extends HTMLElement {
  mountPoint!: HTMLSpanElement;
  name!: string;

  connectedCallback() {
    const mountPoint = document.createElement("span");
    this.attachShadow({ mode: "open" }).appendChild(mountPoint);

    const name = this.getAttribute("name");
    if (name) {
      ReactDom.render(<FetchData name={name} />, mountPoint);
    } else {
      console.error("You must declare a name!");
    }
  }
}
export default StandaloneComponent;

window.customElements.get("standalone-component") ||
  window.customElements.define("standalone-component", StandaloneComponent);

另一種更方便的方式是依靠 react 社區(qū)中的工具,常見(jiàn)的如:

  • react-web-component
  • direflow
  • react-shadow-root
  • react-to-web-component
      
      import r2wc from '@r2wc/react-to-web-component';
import Checklist from './components/checklist/Checklist';

const wcChecklist = r2wc(Checklist, { props: { items: "json" } });

customElements.define("r2w-checklist", wcChecklist);

Vue3

Polymer 是另一個(gè)由谷歌贊助的項(xiàng)目,事實(shí)上也是 Vue 的一個(gè)靈感來(lái)源。Vue 的組件可以粗略的類(lèi)比于 Polymer 的自定義元素,并且兩者具有相似的開(kāi)發(fā)風(fēng)格。最大的不同之處在于,Polymer 是基于最新版的 Web Components 標(biāo)準(zhǔn)之上,并且需要重量級(jí)的 polyfills 來(lái)幫助工作 (性能下降),瀏覽器本身并不支持這些功能。相比而言,Vue 在支持到 IE9 的情況下并不需要依賴(lài) polyfills 來(lái)工作。

...

Vue implements a content distribution API inspired by the Web Components spec draft, using the <slot> element to serve as distribution outlets for content.

-- vue2官方文檔

源自 Vue 2.x 時(shí)代對(duì)  Web Components 的關(guān)注,Vue 3 更進(jìn)一步,原生支持了將 Vue 3 組件導(dǎo)出為 Web Components

Vue 提供了一個(gè)和定義一般 Vue 組件幾乎完全一致的 defineCustomElement 方法來(lái)支持創(chuàng)建自定義元素。這個(gè)方法接收的參數(shù)和 defineComponent 完全相同。但它會(huì)返回一個(gè)繼承自 HTMLElement 的自定義元素構(gòu)造器:

        
        <my-vue-element></my-vue-element>
        
        import { defineCustomElement } from 'vue'

const MyVueElement = defineCustomElement({
  // 這里是同平常一樣的 Vue 組件選項(xiàng)
  props: {},
  emits: {},
  template: `...`,

  // defineCustomElement 特有的:注入進(jìn) shadow root 的 CSS
  styles: [`/* inlined css */`]
})

// 注冊(cè)自定義元素
// 注冊(cè)之后,所有此頁(yè)面中的 `<my-vue-element>` 標(biāo)簽
// 都會(huì)被升級(jí)
customElements.define('my-vue-element', MyVueElement)

// 你也可以編程式地實(shí)例化元素:
// (必須在注冊(cè)之后)
document.body.appendChild(
  new MyVueElement({
    // 初始化 props(可選)
  })
)

...

官方的 SFC 工具鏈支持以“自定義元素模式”導(dǎo)入 SFC (需要 @vitejs/plugin-vue@^1.4.0vue-loader@^16.5.0)。一個(gè)以自定義元素模式加載的 SFC 將會(huì)內(nèi)聯(lián)其 <style> 標(biāo)簽為 CSS 字符串,并將其暴露為組件的 styles 選項(xiàng)。這會(huì)被 defineCustomElement 提取使用,并在初始化時(shí)注入到元素的 shadow root 上。

要開(kāi)啟這個(gè)模式,只需要將你的組件文件以 .ce.vue 結(jié)尾即可:

        
        import { defineCustomElement } from 'vue'
import Example from './Example.ce.vue'

console.log(Example.styles) // ["/* 內(nèi)聯(lián) css */"]

// 轉(zhuǎn)換為自定義元素構(gòu)造器
const ExampleElement = defineCustomElement(Example)

// 注冊(cè)
customElements.define('my-example', ExampleElement)

在 Vue 3 中使用其他 Web Component 同樣簡(jiǎn)單,根據(jù)編譯環(huán)境是瀏覽器、vite 或是 vue cli 等,設(shè)置其 isCustomElement 配置函數(shù)為 (tag) => tag.includes('-') 后基本就能正常使用了;詳見(jiàn)官方文檔。

Vue 2

Vue 2 中并不具備 Vue 3 中 defineCustomElement 那樣的方法。

webpack

對(duì)于大部分基于原生 webpack 的 Vue 2 項(xiàng)目,可以用開(kāi)源插件 vue-custom-element 達(dá)到和 defineCustomElement 類(lèi)似的效果,如:

      
      Vue.customElement('widget-vue', MyVueComponent, {
  shadow: true,
  beforeCreateVueInstance(root) {
    const rootNode = root.el.getRootNode();

    if (rootNode instanceof ShadowRoot) {
      root.shadowRoot = rootNode;
    } else {
      root.shadowRoot = document.head;
    }
    return root;
  },
});

Vue CLI

而在由 Vue CLI 構(gòu)建的 Vue 項(xiàng)目中,可以通過(guò)為構(gòu)建命令指定 --target wc 參數(shù),從而將一個(gè)單獨(dú)的入口構(gòu)建為一個(gè) Web Components 組件:

      
      vue-cli-service build --target wc --name my-element [entry]
  • entry 應(yīng)該是一個(gè) *.vue 文件。Vue CLI 將會(huì)把這個(gè)組件自動(dòng)包裹并注冊(cè)為 Web Components 組件,無(wú)需在 main.js 里自行注冊(cè)
  • 在 Web Components 模式中,Vue 是外置的。這意味著包中不會(huì)有 Vue,即便你在代碼中導(dǎo)入了 Vue。這里的包會(huì)假設(shè)在頁(yè)面中已經(jīng)有一個(gè)可用的全局變量 Vue
  • 該構(gòu)建將會(huì)產(chǎn)生一個(gè)單獨(dú)的 JavaScript 文件 (及其壓縮后的版本) 將所有的東西都內(nèi)聯(lián)起來(lái)
  • 當(dāng)這個(gè)腳本被引入網(wǎng)頁(yè)時(shí),會(huì)注冊(cè)自定義組件,其使用 @vue/web-component-wrapper 包裹目標(biāo) Vue 組件,并自動(dòng)代理屬性、特性、事件和插槽
  • 也可以設(shè)置構(gòu)建命令打包多個(gè)組件或異步組件
      
      <script src="https://unpkg.com/vue"></script>
<script src="path/to/my-element.js"></script>

<!-- 可在普通 HTML 中或者其它任何框架中使用 -->
<my-element></my-element>

??實(shí)例:用異構(gòu)系統(tǒng)共建 web components

8c8600ab9dd22d78382a32fc22079ad6.webp https://gitee.com/tonylua/web-component-test1/tree/master

總結(jié)

正如以 Flash 為代表的 RIA 技術(shù)浪潮極大地刺激了瀏覽器廠商,從而加速了瀏覽器的進(jìn)步并催生了 ES5/ES6 的落地;同樣,Angular/React/Vue 等前端組件化開(kāi)發(fā)框架的普及,也讓原生的 Web Components 標(biāo)準(zhǔn)不斷發(fā)展。

Web Components 搭配的 shadow DOM 封裝等實(shí)用特性,讓一直困擾開(kāi)發(fā)者們的樣式局部化和事件隔離等問(wèn)題迎刃而解。

隨著現(xiàn)代瀏覽器兼容性的不斷改善和各種開(kāi)發(fā)框架對(duì) Web Components  的主動(dòng)擁抱,也勢(shì)必會(huì)在不久的將來(lái)打破前端開(kāi)發(fā)技術(shù)棧之間的壁壘,讓整個(gè)社區(qū)沉淀的服務(wù)和能力迎來(lái)一次大的整合。

參考資料

  • http://w3c-html-ig-zh.github.io/webcomponents/spec-zh/shadow/
  • https://javascript.info/webcomponents-intro
  • https://www.webcomponents.org/introduction
  • https://juejin.cn/post/7072715334519619598
  • https://juejin.cn/post/7148974524795453476
  • https://juejin.cn/post/7107856163361783816
  • https://www.zhihu.com/question/321832109
  • https://juejin.cn/post/7181088227531915322
  • https://www.jitendrazaa.com/blog/salesforce/introduction-to-html-web-components/
  • https://juejin.cn/post/7168630364246638606
  • https://juejin.cn/post/6976557762377416718
  • https://cn.vuejs.org/guide/extras/web-components.html
  • https://web.dev/custom-elements-best-practices/
  • https://github.com/stcruy/building-a-reusable-vue-web-component
  • https://www.oreilly.com/library/view/modern-javascript/9781491971420/ch05.html
  • https://developer.mozilla.org/zh-CN/docs/Web/API/Web_components
  • https://deepinout.com/css/css-questions/417_css_what_is_the_different_between_host_host_hostcontext_selectors.html
  • https://www.zhangxinxu.com/wordpress/2021/02/css-part-shadow-dom/
  • https://juejin.cn/post/6923957212075261966
  • https://web.dev/custom-elements-best-practices/
  • https://www.abeautifulsite.net/tags/web%20components/
  • https://juejin.cn/post/7010595352550047752
  • https://dev.to/nurlan_tl/tips-to-create-web-components-using-vue-3-ts-vite-3a7a
  • https://itnext.io/react-and-web-components-3e0fca98a593
  • https://www.bitovi.com/blog/how-to-create-a-web-component-with-create-react-app


瀏覽 121
點(diǎn)贊
評(píng)論
收藏
分享

手機(jī)掃一掃分享

分享
舉報(bào)
評(píng)論
圖片
表情
推薦
點(diǎn)贊
評(píng)論
收藏
分享

手機(jī)掃一掃分享

分享
舉報(bào)

感谢您访问我们的网站,您可能还对以下资源感兴趣:

国产秋霞理论久久久电影-婷婷色九月综合激情丁香-欧美在线观看乱妇视频-精品国avA久久久久久久-国产乱码精品一区二区三区亚洲人-欧美熟妇一区二区三区蜜桃视频 婷婷色导航| 国产精品美女久久久久久久久| 伊人三级片| 国产精品久久久久无码AV| 91在线无码精品在线看| 亚洲日韩欧美一区二区天天天| 国模一区二区| www欧美| 久久精品一区二区三区四区五区| 国产激情视频在线免费观看| 天天操天天射天天爽| 懂色av懂色av粉嫩av| 影音先锋国产av| 中文字幕Av在线| 国产在线视频一区二区三区| 久99视频| 51成人网站| 精品一区二区三区蜜桃臀www| 无码网站内射| 国产精品XXX视频| 欧美在线成人视频| 尤物无码| 美女做爱在线观看| 成人无码影院日韩,成人年…| 欧美婬乱片A片AAA毛片地址| 91精品国产麻豆国产自产在线| 蜜臀久久99精品久久久电影| 五月天婷婷在线无码| 天天拍天天日| 乱子伦日B视频| 亚洲五月婷| 亚欧无码| 开心五月色婷婷综合开心网| 伊人在线视频观看| 69成人无码| 97在线观看免费| 日本黄色电影网站| 亚洲免费观看高清完整| 草草视频在线观看| 国产2区| 午夜亚洲国产一区视频网站| 欧美一区在线视频| 中文字幕你懂的| 強姦婬片A片AAA毛片Mⅴ| 操逼AV无码| 国产精品在线看| 安徽妇搡BBBB搡BBBB袄爱直播 | 成人自拍偷拍视频| www.黄色在线| 影音先锋国产av| 大香蕉性爱| 加勒比在线视频| 精品亚洲成人| AV在线一区二区| 欧美成人A| 国产小视频在线观看| 日本免费在线观看视频| 91久热| 国产精品一二三区| 青青草视频免费在线观看| 只有精品| 亚洲综合中文| 黄色搞逼视频| 亚人精品中文字幕在线观看| 欧美一区二区三区在线| 狠狠做深爱婷婷久久综合一区| 男女视频91| 北条麻妃视频在线观看| 国产精品一区一区三区| 精品久久电影| 亚洲高清在线| 中文字幕精品视频在线| 亚洲高清AV| 欧美69| 伊人国产视频| 色妹子综合| 91亚洲精品久久久久蜜桃| 狠狠色噜噜狠狠狠888| 精品国产黄色| 黃色A片一級二級三級免費久久久 亚洲AV无码第一区二区三区蜜桃 亚洲综合免费观看高清完整版在线 | 亚洲国产色情| 精品人妻中文字幕视频| 嫩BBB| 亚洲资源在线| 尤物网站在线观看| 黄色性爱小说| 国产黄色片在线免费观看| 色六月婷婷| brazzers疯狂作爱| 久草毛片| 亚洲中文无码视频| 在线免费观看成人视频| 婷婷丁香五月激情| 亚洲日韩AV电影| 国内精品卡一卡二卡三| 日韩在线一区二区三区| 麻豆国产91| 中文字幕人妻一区| 成人性生交大片免费看小芳| 国产一级婬片A片免费无成人黑豆 国产真实露脸乱子伦对白高清视频 | 91资源在线| 色国产视频| 福利视频亚洲| 青青草社区视频| 日韩性爱视频在线观看| 六月婷婷五月天| 热久久免费| 亚洲无码电影视频| 欧美日一区二区三区| 日韩在线欧美在线| 欧美日韩大香蕉| 欧美一区二区丁香五月天激情 | www.91久久| 无码AV电影在线观看| 一区二区黄色| 国产第七页| 欧美三级大片| 国产一区亚洲| 国内老熟妇对白HDXXXX| 亚洲日韩AV电影| 国产色无码网站www色视频| 欧美日韩V| 国产Av一区二区三区| 成人AV一AV二| 一级女婬片A片AAAA片| 国产欧美日韩综合在线视频| 在线免费看av| 操操操av| 国产在线观看免费成人视频| 国产黄色免费电影| 大鸡巴视频在线| 国产欧美精品一区二区色综合| 99性爱网| 青青草成人在线| 日韩免费成人视频| 四虎永久在线精品无码| 国产做受91一片二片老头| 国产人与禽zoz0性伦| 一本色道综合久久欧美日韩精品 | 99热精品国产| 黄网免费看| 一本色道久久综合无码人妻| av天堂资源| 国产精品电影| 久激情内射婷内射蜜桃欧美一级| 一级二级无码| 久久国产成人| 毛片aaa| 精品久久国产| 先锋影音中文字幕| 亚洲视频在线免费| 精品国产久久久| 神马午夜视频| 国产传媒AV| 亚洲免费播放| 亚洲AV成人无码精品区| 成人小视频十八禁免费观看| 大香蕉久热| 欧美日韩在线观看中文字幕| 伊人黄片| 国产高清无码一区二区| 成人精品永久免费视频99久久精品| 怡红院一区| 亚洲黄色视频在线观看网站| 99久久国内精品成人免费| 欧美中文日韩| 这里精品| 午夜国产精品AV| 26∪u∪成人网站| 黄色国产网站| 亚洲AV无码日韩AV无码导航| 国产黄色影院| 在线观看一区二区三区四区| 高清无码1区| 无码AV大香线蕉伊人| 熟女少妇一区二区| yy午夜福利| 亚洲v视频| 日韩区一中文字幕a∨| 超碰在线免费播放| 五月丁香六月| 亚洲无码精品一区二区| 多啪啪免费视频| 在线观看视频日韩| 日韩欧美色| 亚洲国产成人在线视频| 日本不卡一区二区三区四区| 欧美在线v| 国产91嫩草乱婬A片2蜜臀| 91麻豆精品| 俺来也俺去了| 蝌蚪久久| 操逼三级片| 青青草五月天色婷婷丁香| 又紧又嫩又爽无遮挡免费| 欧美国产一区二区| 亚洲日韩在线a成| 国产亚洲久一区二区| 亚洲va国产天堂va久久en | 亚洲日韩国产AV| 婷婷五月天久久| 日本中文字幕在线播放| 九九韩剧网最新电视剧免费观看| 91麻豆国产在线| 一级免费黄色电影| 黄片网页| 亚洲高清无码网站| 九九re精品视频在线观看| 人妻japanesewoman| 特级西西WWW888| 91在线免费视频观看| 91西安站街老熟女露脸| 久久久久久久久久久久国产精品| 六月丁香五月| 99久久精品国产色欲| 国产理论视频| 欧美日韩亚洲视频| av手机在线| 久久久久久久久成人| 香蕉黄色三级片| 精久久久| 午夜无码AV| 淫荡人妻视频| 永久精品| 青草青在线| 台湾色综合| 国产国产国产在线无码视频| 无码一级| 黄色视频在线免费播放| 熟女在线视频| 亚洲中文第一页| 国产91嫩草乱婬A片2蜜臀| 国外亚洲成AV人片在线观看 | 人人草超碰| 九九r在线精品观看视频| 国产亚洲色婷婷久久99精品91| 一级黄色电影免费看| 精品久久成人| 天天操天天操天天操天天操| 口工视频| 2024国产精品| 中文字幕日韩有码| 日日夜夜天天综合| 嫩草99| 午夜视频99| aaa少妇| 日韩无码成人| 天天日夜夜| 国产av激情| 91亚洲精品视频| 不卡免费视频| 狠狠狠狠干| 国产在线A片| 人人操人人妻人人爽| 日本天堂网站| 黄片网址在线观看| 国产成人毛片18女人18精品 | 国产肏屄视频| 亚洲性爱中文字幕| 欧美午夜福利在线观看| 69国产| 最新AV在线| 欧美成人精品欧美一级私黄| 一级a一级a爱片免费免免高潮| 日韩免费看| 青青青草视频在线观看| 2025精品视频| 天堂а√在线中文在线新版| 无码国产一区二区三区四区五区| 日本免费不卡| 精品国产欧美一区二区三区成人| 亚洲成人第一页| 97精品视频在线观看| 欧美日韩毛片| 欧美成人精品激情在线视频 | 青青草原成人在线视频| 中文字幕日本人妻| 国产精品资源在线观看| 欧美激情视频一区| 国产精品人妻无码久久久郑州天气网| 人妻爽爽| 日韩成人大片| 国产97在线视频| 农村三级片| 国产v在线观看| 亚洲无码www| 欧美Aⅴ| 国产成人无码在线| 性欧美| 免费A片国产毛无码A片| 狠狠操av| 欧美无人区码suv| 日韩精品一区二区三区免费观看高清| 精品无码一区二区人妻久久蜜桃| 亚洲美女网站在线观看| 午夜视频免费在线观看| 在线a视频免费观看| 五月六月丁香| 91视频在| 美女91视频| 18一20女一片毛片| 高清无码在线观看视频| 久久无码电影| 免费看特别黄色视频| 香蕉网站操逼片| 91香蕉国产成人App| 免费欧美成人网站| 国产又爽又黄A片| 国产精品不卡一区二区三区| 波多野结衣高清无码视频| 亚洲欧美视频在线| 成人免费无遮挡无码黄漫视频| 高清免费无码| 丁香五月天婷婷久久| 精品国产999久久久免费| 丁香婷婷综合网| yw在线播放| 中文无码电影| yw尤物| 综合天堂网| 国产成人免费观看视频| 日韩AV无码一区二区| 国产一区二区免费| 九色一区| 手机AV在线| 日韩美女做爱| 人人操狠狠操| 内射视频在线免费观看| 一本道在线无码| 蜜臀久久99精品久久久久久酒店| 午夜成人爽| 影音先锋无码一区| 91porn国产| 北条麻妃AV在线播放| 五月丁香性爱| 玉米地一级婬片A片| 伊人综合电影| chinese高潮老女人| 欧美色噜噜| 岛国无码在线| 黄色av影院| 精品免费一区二区三区四区| 人妻精品久久久久中文字幕69 | 欧美三级片网| AV在线无码| 无码一区二区北条| 日韩无码一| 91丝袜一区二区三区| 午夜丁香| 91热99| 激情五月天综合网| 亚欧成人网站| 美女被操免费网站| 超碰二区| 黄色三级电影| 99热免费在线| 国外亚洲成AV人片在线观看 | 老太婆擦BBBB撩BBBB| 肏逼视频网站| 波多野结衣视频网站| 99精品一区二区三区| 国产午夜激情视频| 偷拍第一页| 中文无码人妻少妇| 久久久久a| 中文字幕黄色电影| 国产中文字幕亚洲综合欧美| 草久在线| 一区二区成人电影| 天天色天天日| 91成人区| www| 妓女不卡| 日本黄色视频在线免费观看| 四虎一区二区| 成人h网站在线观看| 九色蝌蚪视频| 日日摸日日碰| 午夜成人黄色| 精品一区电影| 色噜噜狠狠色综无码久久合欧美| 亚洲五月激情| 波多野结衣视频一区| 免费无码毛片一区二区A片小说| 大香蕉久在线| 夜色福利在线| 99热国产| 欧美三级欧美三级三级| av婷婷五月天| 亚洲91在线| 苍井空精毛片精品久久久| 中日韩特黄A片免费视频| 18禁91| 熟女人妻人妻の视频| 婷婷五月六月丁香| 在线观看无码| 风流老熟女一区二区三区| www.五月婷婷| 极品在线视频| 欧美亚洲日韩成人| 少妇被躁到高潮无码| 亚洲有码在线视频| 婷婷五月天在线播放| 日逼导航| 国产乱子伦精品免费,| 日韩无码成人电影| 中文字幕在线成人| 亚洲综合免费观看| 黄片网站入口| 亚洲AV动漫| 免费av毛片| 手机在线观看av| 亚洲AV五月天在线| 91无码人妻精品一区二区蜜桃| 五月天操逼| 亚洲高清无码视频大全| 黄色网页在线免费观看| 白白操白白干| 日韩一级片免费观看| 99偷拍| 天天爽日日澡| 波多野结衣黄色| 伊人免费成人视频| 国产偷拍| 一级黄色片网站| 欧美日韩A片| 亚洲国产精品VA在线看黑人 | 十八禁黄网站| 国产三级国产三级国产普通话| 色婷婷五月天在线观看| 蜜臀AV一区二区三区免费看| 久久国产精品一区二区三区| 九九r在线精品观看视频| www免费视频在线观看播放| 爱爱网址| 91福利网站| www.青青草视频| 9999国产精品| 色丁香六月| 99这里有精品视频| 丰满的人妻一区二区10| 国产精品免费看| 先锋影音男人资源站| 亚洲一二三四| 久久久高清无码视频| 无码专区一区二区三区| 99热国品| 日韩欧美中文在线| 伊人五月丁香| 免费无码视频在线观看| 青青青亚州视频在线| 东京热无码视频| 精品无码久久久| 夜夜爽夜夜爽| 影音AV| 大香蕉青娱乐| 9I看片成人免费视频| 国产成人无码AⅤ片免费播放 | 无码久久| ThePorn-成人网站入口| 国产男女av| 91人人草| 天天天日天天天天天天天日歌词 | 无码国产一区二区三区四区五区| 激情青青草| 免费性爱视频网站| 大炕上公让我高潮了六次| 成人aV免费观看| 欧美特黄AAAAAAAAA片 | 啪啪啪av| 波多野成人无码精品69| 中文字幕免费AV| 成人激情片| 黄色成人网站免费在线观看| 人妻黑人一区二区三区| 九九热毛片在线观看| 国产原创精品| 豆花视频一区| 亚洲日韩久久| 午夜成人在线视频| 北条麻妃无码视频在线| 久久h| 久久久久成人精品无码| 婷婷视频导航| 中文字幕毛片| 中国无码视频| 国产美女自拍| 抽插视频欧美| 日韩成人在线免费观看| 色婷婷久久综合| 做aAAAAA免费视频| 四虎精品影院| 亚洲男人天堂AV| 日韩一级免费观看| 国产av高清| 日本十八禁网站| 中文字幕免费观看视频| 白丝自慰网站| 中文字字幕在线中文乱码电影 | 夜夜爽久久精品91| 欧美日本中文字幕| 韩国无码中文| 成人免费视频性爱| 最近最好的2019中文| 中国老女人性爱视频| 高清无码视频免费版本在线观看| 精品无码一区二区| 一区二区三区高清| 蜜臀久久99精品久久久久酒店更新时间 | 午夜无码福利| 偷拍欧美日韩| 九一国产在线| 五月天AV网站| 狠狠久久| 久久婷五月| 自拍偷拍av| 玖玖爱这里只有精品| 国产女主播在线播放| 视频一区中文字幕| 亚洲日韩激情| 色欲网| 日本一区二区在线视频| 亚欧洲精品视频| 奇米久久| 成人国产精品| 五月天激情性爱| 丁香激情五月少妇| 日韩不卡视频在线观看| 永久免费不卡在线观看黄网站| 欧美三级片在线| 日韩综合久久| 亚洲美女在线观看| www黄色com| AV无码人妻| 欧美日韩在线观看一区| 精品在线播放| 五月综合久久| 欧美日逼网站| 欧美性爱怡红院| 色哟哟一中文字慕| 中文字幕在线视频无码| 亚洲综合伊人| 91在线无码精品秘入口动作| 内射在线| 黄色国产AV| 久久婷婷在线| 豆花视频在线免费观看| 91麻豆成人精品国产| 亚洲色色视频| 五丁香在线观看AV| 草逼com| 麻豆av在线| 五月丁香大香蕉| 国产91精品在线观看| AV自拍偷拍| 国产精品不卡在线| 婷婷五月天激情电影| 91免费观看网站| 大香蕉尹人在线视频| 国产淫乱视频| 亚州精品国产精品乱码不99勇敢 | 色婷婷在线观看视频| 久久嫩草国产成人一区| 日韩久久中文字幕| 韩国三级片在线| 日韩三级片在线播放| 极品久久久久| 国产精品啪啪啪啪| 免费无码国产在线怀| 欧美后门菊门交4| 午夜AV免费| 特级特黄A级高潮播放| 加勒比日韩在线| AV天堂国产| 亚洲Japanese办公室制服 | 亚洲av资源在线观看| 俺来也影院| AV天堂国产| 欧美日韩A片欧美日| 99热精品免费在线观看| 怡红影院美乳| 久久久久久久久久国产精品免费观看-百度 | 人妻丝袜无码视频专区| 十八禁无码| 蜜臀99久久精品久久久懂爱| 男人的天堂色婷婷| 人人干人人干人人干| 欧美一级特黄AAAAAA片| 狠狠干在线观看| 影音先锋色先锋| 热久久9| 国产日韩在线观看视频| AV免费在线播放| 久久综合中文| 无码视频在线观看免费| 99久久婷婷国产综合精品hsex,亚 91小宝寻花一区二区三区三级 | 欧美51精品| 无套内射在线播放| 欧美被操| 蜜桃视频com.www| 日韩欧美在线中文字幕| 日本乱伦网站| 91色秘乱码一区二区| 四虎综合| 抽插视频免费| 91精品久久香蕉国产线看观看| 成人黄片网站| 欧美日韩国产在线| 国产精品久久AV电影| 操B五月天| 天天日天天干天天爽| 夜夜嗨AV一区二区三区啊| 欧美高清久久| 亚洲日韩色色| 18成人网站在线观看| 精品国产久久久久久| 日韩在线观看视频网站| 欧美黄色大片| 91精品久久香蕉国产线看观看| www.色老板| 伊人成人视频在线观看| 你懂的在线视频观看| 免费的黄色视频网站| 亚洲AV成人一区二区三区不卡| 色色色欧美| 自拍乱伦| 国产肏屄视频| 先锋影音资源站| 大鸡巴影院| 久久久在线视频| 丁香啪啪| 国产一级二级在线观看| 亚洲一级在线观看| 亚洲精品乱码久久久久| JlZZJLZZJlZZ亚洲女人17| 91在线无码精品入口电车| 色777色| 高清成人无码| av三级片在线观看| 蜜桃av| 吴梦梦| av不卡在线观看| 欧美伦妇AAAAAA片| 人人操人人干人人摸| www黄片| 精品乱子伦一区二区三区下载| 波多野结衣与黑人| 欧美综合第一页| 亚洲少妇一区| 外国成人视频| 一大高清日韩| 中文字幕第11页| 激情小视频| 欧美一区二区在线观看| 精品夜夜澡人妻无码AV| 国产一级免费观看| 九九热视频99| 色婷婷AV一区二区三区之e本道| 亚洲免费视频在线| 一品国精和二品国精的文化意义| 三级片AV在线| 黄色99| 天天插天天插| 青青草黄色片| 一区二区三区不卡在线| 一级全黄120分钟免费| 99视频免费观看| 99热在线观看精品| a片一级片| 国产区精品| 3级片网站| 特一级A片| 欧美日韩视频在线播放| 亚洲无码免费在线观看| 色婷婷Av一区| 人妻制服丝袜| 这里都是精品| 这里视频很精彩免费观看电视剧最新 | 一区二区三区小视频| 欧美成人精品网站| 香蕉伊人在线| 亚洲中文字幕在线观看免费| 亚洲社区在线观看| 丁香五月激情网| 国产最新在线视频| 日韩一级片网站| 亚洲69视频| 天天精品无码| 日B视频在线观看| 国产亚洲视频在线观看| 亚洲a级毛片| 亚洲在线第一页| www.sese| 蝌蚪九色啦403| 欧美视频h| 日韩性爱无码| 欧美精品在线免费| 亚洲国产无码在线观看| 爱爱无码| 7777精品伊人久久7777| 少妇搡BBBB搡BBB搡造水多| 99热1| 18成人网站在线观看| 大香蕉伊人影院| 黄色一级在线| 日韩欧美第一页| 蜜桃av| 国内精品久久久久久久久久变脸| www.五月婷婷| 97超碰在线视| 色爽AV| 成人国产精品在线观看| 无码动漫av| 黃色A片一级一级一级久别的草原| 欧美日韩中文视频| 强伦轩一区二区三区四区| 短发半推半就AV| 一本加勒比HEZYO东京热无码| 久草手机在线视频| 影音先锋色资源站| www| a√在线视频| 97在线视频免费观看| 色图在线观看| 黄色激情五月| 亚洲欧美综合| 日韩色婷婷| 久草视频免费在线观看| 欧美成人三级片| 五月天婷婷视频| 中文亚洲视频| 国产精品v| 国产69AV| 骚逼无码| 久久午夜无码鲁片午夜精品男男| 超碰人人操人人摸| 91精品人妻| 欧美性爱视频免费观看| 韩国久久| 中文字幕35页| av大片免费看| 中文字幕亚洲高清| 黄色带亚州| 熟妇槡BBBB槡BBBB图| 黄色福利| 狼人一区二区| 午夜黄片| 久久精品苍井空免费一区二| 北条麻妃一区二区三区在线观看| 国产欧美日韩一区二区三区| 加勒比日韩在线| 国产情侣在线视频| 人人操人人爱人人摸| 中文字幕人妻一区| 亚洲Av无码午夜国产精品色软件 | 国产AV小电影| 国产黄色视频观看| 亚洲无码专区在线观看| 日韩AV手机在线观看| 国产爱搞| 久久久久久伊人| 懂色中文字幕| 男女啊啊啊| 欧美日韩h| 蜜臀99久久精品久久久久久软件| 一区二区三区福利| 91在线精品无码秘入口苹果| 无码人妻精品一区二区蜜桃漫画 | 国产传媒视频| 嫩BBB槡BBBB槡BBBB百度| 在线人妻| 超碰在线网站| 肉色超薄丝袜脚交一区二区| 黄色小视频在线观看| 国产网站精品| 色色色色色欧美| 国产操逼视频网站| 亚洲福利在线免费观看| 日韩一页| 亚洲视频黄色| 18成人在线观看| 婷婷五月天社区| 河南乱子伦视频国产| 日韩无码黄色片| 日韩高清一区二区| 91九色TS另类国产人妖| 大香蕉精品欧美色综合2025| 黄a在线观看| 三级av在线观看| 狼人社區91國產精品| 成人无码区免费| 日本在线网站| 国精久久久久| 一级黄色电影在线观看| 久久大香蕉网| 亚洲欧美日韩性爱| 躁BBB躁BBB添BBBBBB| 香蕉中文在线| 99视频精品| AV免费在线播放| AV在线导航| 日韩成人黄色视频| 91国产爽黄| 无码在线观看免费视频| 色婷婷AV在线观看| 日韩一区二区在线观看| 中文字幕第69页| 亲子伦一区二区三区观看方式| 婷婷免费| 五月丁香中文字幕| 欧美日韩色图| 水多多成人视频| 欧洲天堂在线视频网站| 操操日| 五月天婷婷丁香网| 国产成人久久精品麻豆二区| 91丨人妻丨国产丨丝袜| 一道本无码在线| 3D动漫精品一区二区在线播放免费| 亚洲成人福利电影| 一区二区三区在线观看| 91网站免费在线观看| 国产字幕| 国产一级A片在线观看| 日韩在线观看| 国内精品久久久| 中文成人无字幕乱码精品区| 男人手机天堂| 一区二区精品视频| 人妻一区二区三区| 亚洲综合视频在线观看| 小视频你懂的| www.色日本| 岛国av无码免费| a在线观看| 日韩免费视频一区二区| 西西444WWW无码大胆知乎| 加勒比国产在线| 麻豆黄网| 欧美在线观看视频| 日本不卡一区二区三区| 桃色五月天| 日韩成人免费在线观看| 国产欧美第一页| 亚洲va综合va国产va中文| 日韩国产一区| 天天插夜夜操| 国产jk在线| 自拍视频国产| 一区二区三区Av| 无码人妻精品一区二区三区温州| 狠狠爱一区| 青青草国产| 国产欧美精品一区二区| 婷婷五月丁香激情| 人妻斩り43歳| 亚洲AV毛片成人精品网站| 国产AV一卡| 最新久欠一区二区免费看| 欧美日韩视频在线| 悠悠色影院| 精品无码在线| 超碰在线观看99| 久久久免费观看视频| 人妻国产| 大学生18一19GAY169| 国产美女操逼网站| 淫荡少妇美红久久久久久久久久| 综合久久网| 波多野结衣无码AV在线| 视频三区| 亚洲精品熟女| 91久久亚洲| 十八禁无码| 久久婷婷国产综合| 青青草黄色视频| 欧美激情国产精品| 人妻中文字幕av| 99精品9| 91av在线免费播放| AV在线小说| 少妇69p| 亚洲天堂在线观看网站| 插插插插网| 羞羞色院91蜜桃| 黄色视频在线观看18| 亚洲av在线观看| 超碰9| 国产乱子伦-区二区三区| 91乱子伦国产乱子伦| 中文字幕人妻无码| 五月丁香激情视频| 日本激情网站| 俄女兵一级婬片A片| 日韩综合久久| 91九色在线观看| 日本91视频| 亚洲精品一区二区三区在线观看 | 大香蕉大香蕉网| 亚洲社区在线观看|