Vue and Web Components,其實不太懂什麼web組件
快來看看是什麼東西吧 www ~~~
Web Components 是一組網頁原生 API 的統稱,它允許開發者創建可重複使用的自訂元素。
我們認為 Vue 和 Web Components 是主要互補的技術。Vue 對於使用和創建自訂元素都有出色的支援。無論你是將自訂元素整合到現有的 Vue 應用程式中,還是使用 Vue 來構建和分發自訂元素,你都會發現這是一個良好的選擇。
Vue 在 Custom Elements Everywhere 測試中獲得了滿分 100%。在 Vue 應用中使用自訂元素基本上與使用原生 HTML 元素相同,但需要注意以下幾點:
預設情況下,Vue 會嘗試將非原生 HTML 標籤解析為已註冊的 Vue 元件,如果解析失敗,才會將其渲染為自訂元素。這會導致 Vue 在開發過程中發出「解析元件失敗」的警告。為了讓 Vue 知道某些元素應被視為自訂元素並跳過元件解析,我們可以指定 compilerOptions.isCustomElement
選項。
如果你使用 Vue 與建構設定,應通過建構配置來傳遞此選項,因為這是一個編譯時選項。
// 僅適用於在瀏覽器內部編譯。
// 如果使用建構工具,請參閱下面的配置示例。
app.config.compilerOptions.isCustomElement = (tag) => tag.includes('-')
// vite.config.js
import vue from '@vitejs/plugin-vue'
export default {
plugins: [
vue({
template: {
compilerOptions: {
// 將所有帶有連字符的標籤視為自訂元素
isCustomElement: (tag) => tag.includes('-')
}
}
})
]
}
// vue.config.js
module.exports = {
chainWebpack: (config) => {
config.module
.rule('vue')
.use('vue-loader')
.tap((options) => ({
...options,
compilerOptions: {
// 將所有以 ion- 開頭的標籤視為自訂元素
isCustomElement: (tag) => tag.startsWith('ion-')
}
}))
}
}
由於 DOM 屬性只能是字串,我們需要將複雜數據作為 DOM 屬性傳遞給自訂元素。在設置自訂元素的 props 時,Vue 3 自動使用 in
運算符檢查 DOM 屬性的存在,如果鍵存在,將優先將其設置為 DOM 屬性。這意味著在大多數情況下,如果自訂元素遵循推薦的最佳實踐,你不需要考慮這一點。
然而,在某些罕見情況下,數據必須作為 DOM 屬性傳遞,但自訂元素未正確定義/反映該屬性(導致 in
檢查失敗)。在這種情況下,你可以使用 .prop
修飾符強制將 v-bind
綁定設置為 DOM 屬性:
<template>
<my-element :user.prop="{ name: 'jack' }"></my-element>
<!-- 簡寫等價 -->
<my-element .user="{ name: 'jack' }"></my-element>
</template>
自訂元素的主要優勢在於可以與任何框架(甚至不使用框架)搭配使用。這使得它們非常適合於分發給使用不同前端技術棧的終端使用者,或者在希望隔離終端應用程式與元件實現細節時使用。
defineCustomElement
Vue 支援通過 defineCustomElement
方法使用完全相同的 Vue 元件 API 來建立自訂元素。此方法接受與 defineComponent
相同的參數,但會返回一個擴展自 HTMLElement
的自訂元素構造函數:
<my-vue-element></my-vue-element>
import { defineCustomElement } from 'vue'
const MyVueElement = defineCustomElement({
// 這裡是普通的 Vue 元件選項
props: {},
emits: {},
template: `...`,
// 只在 defineCustomElement 中使用: 將 CSS 注入 shadow root
styles: [`/* inlined css */`]
})
// 註冊自訂元素
// 註冊後,頁面上的所有 <my-vue-element> 標籤將被升級
customElements.define('my-vue-element', MyVueElement)
// 你也可以程序化地實例化元素:
// (只能在註冊後進行)
document.body.appendChild(
new MyVueElement({
// 初始化屬性(可選)
})
)
connectedCallback
首次被調用時,Vue 自訂元素會在其 shadow root 內掛載一個內部的 Vue 元件實例。disconnectedCallback
被調用時,Vue 將在一個微任務滴答後檢查該元素是否從文檔中分離。所有通過 props
選項宣告的屬性都會作為屬性定義在自訂元素上。Vue 會自動處理屬性與屬性之間的反射。
Vue 也會自動將用 Boolean 或 Number 類型宣告的屬性在設置為屬性(總是字串)時轉換為所需的類型。例如,給定以下屬性宣告:
props: {
selected: Boolean,
index: Number
}
以及自訂元素的使用:
<my-element selected index="1"></my-element>
在元件中,selected
將被轉換為 true
(布林值),index
將被轉換為 1
(數字)。
通過 this.$emit
或 setup emit 發出的事件將作為原生的 CustomEvent
在自訂元素上分派。附加的事件參數(payload)將作為 CustomEvent
對象的 detail
屬性的一個數組暴露出來。
在元件內部,可以像平常一樣使用 <slot/>
元素來渲染插槽。然而,當使用生成的元素時,它只接受原生的插槽語法:
傳遞命名插槽時,請使用 slot
屬性而不是 v-slot
指令:
<my-element>
<div slot="named">hello</div>
</my-element>
Provide / Inject
API 及其組合式 API 等效方式也適用於 Vue 定義的自訂元素之間。但是,請注意,這僅適用於自訂元素之間,即 Vue 定義的自訂元素無法注入由非自訂元素 Vue 元件提供的屬性。
你可以使用 configureApp
選項來配置 Vue 自訂元素的應用實例:
defineCustomElement(MyComponent, {
configureApp(app) {
app.config.errorHandler = (err) => {
/* ... */
}
}
})
defineCustomElement
也適用於 Vue 單文件元件(SFC)。但是,使用預設的工具設定時,SFC 內的 <style>
會在生產環境構建時提取並合併到單個 CSS 文件中。使用 SFC 作為自訂元素時,通常希望將 <style>
標籤注入自訂元素的 shadow root 中。
官方的 SFC 工具支援以「自訂元素模式」導入 SFC(需要 @vitejs/plugin-vue@^1.4.0
或 vue-loader@^16.5.0
)。在自訂元素模式下加載的 SFC 會將其 <style>
標籤內聯為 CSS 字串,並在元件的 styles
選項下暴露。這將被 defineCustomElement
捕獲並在實例化時注入元素的 shadow root 中。
要選擇加入此模式,只需將元件文件名以 .ce.vue
結尾:
import { defineCustomElement } from 'vue'
import Example from './Example.ce.vue'
console.log(Example.styles) // ["/* inlined css */"]
// 轉換為自訂元素構造函數
const ExampleElement = defineCustomElement(Example)
// 註冊
customElements.define('my-example', ExampleElement)
如果希望自訂應以自訂元素模式導入的文件(例如,將所有 SFC 視為自訂元素),可以將 customElement
選項傳遞給相應的建構插件:
使用 Vue 建立自訂元素時,這些元素將依賴於 Vue 的運行時。根據使用的功能數量,這大約會有 16kb 的基線大小成本。這意味著如果你只發布單個自訂元素,使用 Vue 並不理想——你可能希望使用原生 JavaScript、petite-vue 或專門針對小運行時大小的框架。然而,如果你發布的是具有複雜邏輯的自訂元素集合,這些基礎大小是完全可以接受的,因為 Vue 將使每個元件的編寫代碼大大減少。發布的元素越多,這種權衡越好。
如果自訂元素將在也使用 Vue 的應用中使用,你可以選擇從構建包中外部化 Vue,這樣元素將使用主機應用中的相同 Vue 副本。
建議導出個別的元素構造函數,以便用戶靈活地按需導入它們並使用所需的標籤名稱進行註冊。你也可以導出一個方便的函數來自動註冊所有元素。這是一個 Vue 自訂元素庫的示例入口點:
import { defineCustomElement } from 'vue'
import Foo from './MyFoo.ce.vue'
import Bar from './MyBar.ce.vue'
const MyFoo = defineCustomElement(Foo)
const MyBar = defineCustomElement(Bar)
// 導出個別元素
export { MyFoo, MyBar }
export function register() {
customElements.define('my-foo', MyFoo)
customElements.define('my-bar', MyBar)
}
如果你有很多元件,你還可以利用建構工具功能,例如 Vite 的全局導入或 webpack 的 require.context
來從目錄中加載所有元件。
如果你正在開發應用或庫,可能希望對你的 Vue 元件進行類型檢查,包括那些定義為自訂元素的元件。
自定義元素是使用原生 API 全局註冊的,因此在 Vue 模板中使用時,默認情況下它們不會具有類型推斷。為了為註冊為自定義元素的 Vue 元件提供類型支持,我們可以在 Vue 模板和/或 JSX 中使用 GlobalComponents 介面註冊全局元件類型:
import { defineCustomElement } from 'vue'
// vue SFC
import CounterSFC from './src/components/counter.ce.vue'
// 將元件轉換為 Web 元素
export const Counter = defineCustomElement(CounterSFC)
// 註冊全局類型
declare module 'vue' {
export interface GlobalComponents {
Counter: typeof Counter
}
}
有些開發者認為應該避免使用特定框架的元件模型,並認為只使用自定義元素(Custom Elements)可以使應用程式「未來安全」。在這裡,我們將嘗試解釋為什麼我們認為這是一個過於簡化的看法。
確實,自定義元素與 Vue 元件之間有一定程度的功能重疊:它們都允許我們定義可重用的元件,進行數據傳遞、事件發送和生命周期管理。然而,Web 元件的 API 相對較低級且基本。要構建實際的應用程式,我們需要許多額外的功能,而這些功能是平台本身不涵蓋的:
Vue 的元件模型是針對這些需求設計的,作為一個連貫的系統。
擁有一個能幹的工程團隊,你可能可以在原生自定義元素之上構建類似的系統——但這也意味著你將承擔一個內部框架的長期維護負擔,同時失去了成熟框架如 Vue 的生態系統和社群所帶來的好處。
也有一些框架是基於自定義元素作為其元件模型的基礎,但它們都不可避免地需要引入其專有的解決方案來處理上述問題。使用這些框架意味著接受它們在如何解決這些問題上的技術決策,這並不會如廣告所宣稱的那樣自動使你免受未來可能的變動影響。
此外,我們還發現自定義元素在某些方面具有局限性:
Vue 將始終跟隨網頁平台的最新標準,我們也會樂於利用平台提供的任何功能來讓我們的工作變得更輕鬆。然而,我們的目標是提供工作良好且適用於今天的解決方案。這意味著我們必須以批判的態度來整合新平台功能,這涉及到在標準仍有不足時填補這些空缺。
這篇還是有點模糊~可能對於開發library蠻有幫助吧?ha~~