web components


什么是 Web Components

2011年 谷歌就提出 web components
2015 年 web components 才開始能用,只有Chrome瀏覽器支持
web components 瀏覽器原生組件化,不依賴框架
safari 2017 年實現一部分
Firfox 2018 年實現

Vue cli 已經實現編譯成 web components 的功能,實際上 vue 在很大程度上參照了 web components 的實現,vue 可以說是進階版的 web components

web components 包含技術

can i use web components

Web Components包含三種主要技術:

HTML Templates

熟悉 vue 的開發者對 template 模板這個概念會很熟悉了,模板、組件方便重用
<template><slot> 元素使您可以編寫不在呈現頁面中顯示的標記模板。然后它們可以作為自定義元素結構的基礎被多次重用,和vue很像

自定義組件

<template>
   <span>
     <!-- slot 用法也是和vue的slot用法一樣 -->
     <slot></slot>
   </span>
</template>

<script>
const dom = document.currentScript.ownerDocument.querySelector('template')
 customElements.define('my-span', class extends HTMLElement {
      constructor() { // 初始化階段
        super()
        // 相當於vue的setup
        this.appendChild(dom)
      }
    })
</script>

<style>
span {
  color: green;
}
</style>

在html頁面中使用

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
  <!-- link引用組件,可能會不生效,因為 HTML Imports 🈲 被廢棄 -->
  <link rel="import" href="./span.html" /> 
</head>

<body>
  <!-- 在頁面中使用自定義的標簽 -->
  <my-span>123</my-span>
  <!-- 還可以用is屬性,和vue里的類似 -->
  <span is="my-span">456</span>
</body>

</html>

使用 HTML Modules

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>

<body>
  <script type="module">
    import MySpan from './span.html'
  </script>
  <!-- 在頁面中使用自定義的標簽 -->
  <my-span>123</my-span>
  <!-- 還可以用is屬性,和vue里的類似 -->
  <span is="my-span">456</span>
</body>

</html>

出現報錯,目前還未支持html的 module scripts

Failed to load module script: Expected a JavaScript module script but the server responded with a MIME type of "text/html". Strict MIME type checking is enforced for module scripts per HTML spec.

Custom Elements

自定義元素,開發者可以創建自己的DOM元素,擴展 HTMLElement 接口,繼承自 HTMLElement, 並自定義一些 JS 邏輯。
一般繼承自 HTMLElement 但是如果想要擴展某個元素,則應該繼承該元素,例如想要自定義 input,則需要繼承 input

<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>web-components</title>
</head>
<body>
  <script>
    // 創建一個 <a-b-c> 的元素,名為 el
    const el = document.createElement('a-b-c')

    // 定義一個名為 <a-b-c> 的組件
    customElements.define('a-b-c', class extends HTMLElement {})

    // 獲取 <a-b-c> 組件的構造函數
    customElements.get('a-b-c')

    // 升級我們之前創建的 el 元素
    customElements.upgrade(el)

    // 當 <a-b-c> 組件定義后
    customElements.whenDefined('a-b-c').then(() => { /* 當 <a-b-c> 組件定義后的回掉函數 */ })
  </script>
</body>
</html>

Shadow DOM

一組JavaScript API,用於將封裝的“影子”DOM樹附加到元素(與主文檔DOM分開呈現)並控制其關聯的功能。通過這種方式,您可以保持元素的功能私有,這樣它們就可以被腳本化和樣式化,而不用擔心與文檔的其他部分發生沖突

HTML Imports (廢棄)

之前 HTML imports 也是其中的一部分,但對於 HTML Modules (ES6),HTML Imports 已經被廢棄

生命周期


<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>

<body>
  <iframe src="./iframe.html"></iframe>
  <life-cycle color="purple"></life-cycle>

  <script>
    const iframe = document.querySelector('iframe')
    iframe.onload = () => {
      const h1 = iframe.contentDocument.querySelector('h1')
      document.body.append(document.adoptNode(h1))
    }
    customElements.define('life-cycle', class extends HTMLElement {
      getColor() {
        return this.getAttribute('color')
      }
      setColor(value) {
        this.setAttribute('color', value)
      }
      static observedAttributes = ['color']

      constructor() { // 初始化階段
        super()
        // 相當於vue的setup
        this.innerHTML = '<h1>組件</h1>'
      }
      connectedCallback() { // 掛載階段
        // 相當於vue的mouted
      }
      disconnectCallback() { // 卸載階段
        // 相當於vue的unmounted
      }
      adoptedCallback() { // 收養階段
        // this.innerHTML = '<h1>來自iframe的組件</h1>'
      }
      attributeChangedCallback(name, oldValue, newValue) { // 屬性改變后的階段
        // attribute 特性
        // proporty 屬性
        if (name === 'color') {
          this.style.color = newValue
        }
      }
    })
  </script>


</body>

</html>

使用

web components 是瀏覽器原生組件,所以是不依賴框架,可以在原生、vue、react等都可以使用,在react中可直接使用,vue、vite中需要聲明是自定義元素
react組件都是大寫開頭駝峰命名的組件,原生組件是必須小寫且以-中划線連接,所以不需要做識別處理,原生組件不會被識別為react組件,vue 則需要指定哪些是自定義的原生組件,否則會根據vue的組件編譯規則來查找,出現找不到的情況

fancy-components 是用 web components 實現的組件,使用以此為例

原生

<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>web-components</title>
</head>
<body>
<fc-3d-btn></fc-3d-btn>

<script type="module">

import { Fc3DBtn } from 'https://unpkg.zhimg.com/fancy-components'

// new 就相當於全局注冊了這個組件,相當於 Vue 的 Vue.component('Fc3DBtn', 組件)
new Fc3DBtn()
</script>
</body>
</html>

react

src/index.js 入口文件里注冊,使用就直接用對應的標簽

import {
  FcTypingInput
} from 'fancy-components'

new FcTypingInput()

vue

main.js 入口文件里注冊,使用就直接用對應的標簽,注冊使用同上
vue.config.js 添加額外配置

module.exports = {
  chainWebpack: config => {
    config.module
      .rule('vue')
      .use('vue-loader')
      .tap(options => {
        options.compilerOptions = {
          ...(options.compilerOptions || {}),
          isCustomElement: tag => tag.startsWith('fc-') // 指定以fc開頭的是自定義元素(原生組件),不是vue的組件
        }
        return options
      })
  }
}

vite

main.js 入口文件里注冊,使用就直接用對應的標簽,注冊使用同上
vite.config.js

import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'

// https://vitejs.dev/config/
export default defineConfig({
  plugins: [vue({
    template: {
      compilerOptions: {
        isCustomElement: tag => tag.startsWith('fc-')
      }
    }
  })]
})

兼容

由於 web components 各個瀏覽器的支持還不是特別好,如果想要使用,還是需要polyfills來做兼容
@webcomponents/webcomponentsjs 這個庫可以用來做兼容


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM