使用 Vue 構(gòu)建自定義元素
Web Components 是一組 web 原生 API 的統(tǒng)稱,允許開發(fā)者創(chuàng)建可復(fù)用的自定義元素 (custom elements)。
使用自定義元素的主要優(yōu)勢在于,它們可以在任何框架甚至非框架環(huán)境中使用。當(dāng)你面向的最終用戶可能使用了不同的前端技術(shù)棧,或是當(dāng)你希望將最終的應(yīng)用與它使用的組件實現(xiàn)細(xì)節(jié)解耦時,它們會是理想的選擇。
Vue 和 Web Components 是互補的技術(shù),Vue 為使用和創(chuàng)建自定義元素提供了出色的支持。在 Vue 應(yīng)用中,你可以集成自定義元素,也可以使用 Vue 來構(gòu)建和發(fā)布自定義元素。
Vue?在 Custom Elements Everywhere 測試中取得了 100% 的分?jǐn)?shù)。在 Vue 應(yīng)用中使用自定義元素基本上與使用原生 HTML 元素的效果相同,但需要進(jìn)行一些額外的配置才能工作:
跳過組件解析
默認(rèn)情況下,Vue 會將任何非原生的 HTML 標(biāo)簽優(yōu)先當(dāng)作 Vue 組件處理,而將“渲染一個自定義元素”作為后備選項。這會在開發(fā)時導(dǎo)致 Vue 拋出一個“解析組件失敗”的警告。
要讓 Vue 知曉特定元素應(yīng)該被視為自定義元素并跳過組件解析,我們可以指定 compilerOptions.isCustomElement 這個選項,設(shè)置在此選項對象上的值將會在瀏覽器內(nèi)進(jìn)行模板編譯時使用,并會影響到所配置應(yīng)用的所有組件。
另外也可以通過 compilerOptions 選項在每個組件的基礎(chǔ)上覆蓋這些選項(針對當(dāng)前組件有更高的優(yōu)先級)。
因為它是一個編譯時選項,構(gòu)建工具需要將配置傳遞給 @vue/compiler-dom:
vue-loader:通過 compilerOptions loader 的選項傳遞。
// vue.config.js module.exports = { chainWebpack: config => { config.module .rule('vue') .use('vue-loader') .tap(options => ({ ...options, compilerOptions: { // 將所有帶 ion- 的標(biāo)簽名都視為自定義元素 isCustomElement: tag => tag.startsWith('ion-') } })) } }
vite:通過 @vitejs/plugin-vue 的選項傳遞。
// vite.config.js import vue from '@vitejs/plugin-vue' export default { plugins: [ vue({ template: { compilerOptions: { // 將所有帶短橫線的標(biāo)簽名都視為自定義元素 isCustomElement: (tag) => tag.includes('-') } } }) ] }
瀏覽器內(nèi)編譯時的配置。
// src/main.js // 僅在瀏覽器內(nèi)編譯時才會工作 const app = createApp(App) app.config.compilerOptions.isCustomElement = (tag) => tag.includes('-')
傳遞 DOM 屬性
由于 DOM attribute 只能為字符串值,因此我們只能使用 DOM 對象的屬性來傳遞復(fù)雜數(shù)據(jù)。當(dāng)為自定義元素設(shè)置 props 時,Vue 3 將通過 in
操作符自動檢查該屬性是否已經(jīng)存在于 DOM 對象上,并且在這個 key 存在時,更傾向于將值設(shè)置為一個 DOM 對象的屬性。這意味著,在大多數(shù)情況下,如果自定義元素遵循推薦的最佳實踐,你就不需要考慮這個問題。
然而,也會有一些特別的情況:必須將數(shù)據(jù)以一個 DOM 對象屬性的方式傳遞,但該自定義元素?zé)o法正確地定義/反射這個屬性 (因為 in
檢查失敗)。在這種情況下,你可以強制使用一個 v-bind
綁定、通過 .prop
修飾符來設(shè)置該 DOM 對象的屬性:
<my-element :user.prop="{ name: 'jack' }"></my-element> <!-- 等價簡寫 --> <my-element .user="{ name: 'jack' }"></my-element>
defineCustomElement()
Vue 提供了一個和定義一般 Vue 組件幾乎完全一致的 defineCustomElement 方法來支持創(chuàng)建自定義元素。defineComponent 和這個方法所接收的參數(shù)完全一致。但它會返回一個繼承自 HTMLElement 的原生自定義元素類的構(gòu)造器(可以通過 customElements.define() 注冊)。
function defineCustomElement( component: | (ComponentOptions & { styles?: string[] }) | ComponentOptions['setup'] ): { new (props?: object): HTMLElement }
除了常規(guī)的組件選項,defineCustomElement() 還支持一個特別的選項 styles,它是一個內(nèi)聯(lián) CSS 字符串的數(shù)組,所提供的 CSS 會被注入到該元素的 ShadowRoot 上。
<my-vue-element></my-vue-element>
import { defineCustomElement } from 'vue' const MyVueElement = defineCustomElement({ // 這里是同平常一樣的 Vue 組件選項 props: {}, emits: {}, template: `...`, // defineCustomElement 特有的:注入進(jìn) ShadowRoot 的 CSS styles: [`/* css */`] }) // 注冊自定義元素之后,所有此頁面中的 `<my-vue-element>` 標(biāo)簽都會被升級 customElements.define('my-vue-element', MyVueElement) // 也可以在注冊之后實例化元素: document.body.appendChild( new MyVueElement({ // 初始化 props(可選) }) )
如果這時控制臺報錯:\color{red}{如果這時控制臺報錯:}如果這時控制臺報錯:Component provided template option but runtime compilation is not supported,在 vite.config.js 中添加以下配置:
resolve: { alias: { 'vue': 'vue/dist/vue.esm-bundler.js' } },
生命周期
當(dāng)該元素的 connectedCallback 初次調(diào)用時,一個 Vue 自定義元素會在內(nèi)部掛載一個 Vue 組件實例到它的 ShadowRoot 上。
當(dāng)此元素的 disconnectedCallback 被調(diào)用時,Vue 會在一個微任務(wù)后檢查元素是否還留在文檔中。
如果元素仍然在文檔中,那么說明它是一次移動操作,組件實例將被保留;
如果該元素不再存在于文檔中,那么說明這是一次移除操作,組件實例將被銷毀。
Props
所有在 props 選項中聲明的 props 都會被定義為自定義元素的屬性。Vue 能夠自動地適當(dāng)處理它是否反射為屬性或?qū)傩浴?/p>
attribute 總是根據(jù)需要反射為相應(yīng)的屬性類型。原話已經(jīng)非常清晰明了,可以直接重述。重新表述如下: 基本數(shù)據(jù)類型的屬性值(例如字符串、布爾值或數(shù)字)可以通過反射反映為屬性。
當(dāng)它們被設(shè)為 attribute 時 (永遠(yuǎn)是字符串),Vue 也會自動將以 Boolean 或 Number 類型聲明的 prop 轉(zhuǎn)換為所期望的類型。比如下面這樣的 props 聲明:
props: { selected: Boolean, index: Number }
并以下面這樣的方式使用自定義元素:
<my-element selected index="1"></my-element>
在組件中,selected 會被轉(zhuǎn)換為 true (boolean 類型值) 而 index 會被轉(zhuǎn)換為 1 (number 類型值)。
事件
emit 觸發(fā)的事件都會通過以 CustomEvents 的形式從自定義元素上派發(fā)。
額外的事件參數(shù) (payload) 將會被暴露為 CustomEvent 對象上的一個 detail 數(shù)組。
插槽
在一個組件中,插槽將會照常使用 渲染。但是,使用最終元素時,只能使用原生插槽的語法,無法使用作用域插槽。
當(dāng)傳遞具名插槽時,應(yīng)使用 slot attribute 而不是 v-slot 指令:
<my-element> <div slot="named">hello</div> </my-element>
依賴注入
Provide / Inject API 和相應(yīng)的組合式 API 在 Vue 定義的自定義元素中都可以正常工作。
但是,依賴關(guān)系只在自定義元素之間起作用。一個由常規(guī) Vue 組件提供的屬性無法被注入到由 Vue 定義的自定義元素中。
將 SFC 編譯為自定義元素
defineCustomElement 也可以搭配 Vue 單文件組件 (SFC) 使用。但是,根據(jù)默認(rèn)的工具鏈配置,SFC 中的 <style>
在生產(chǎn)環(huán)境構(gòu)建時仍然會被抽取和合并到一個單獨的 CSS 文件中。當(dāng)正在使用 SFC 編寫自定義元素時,通常需要改為注入 <style>
標(biāo)簽到自定義元素的 ShadowRoot 上。
官方的 SFC 工具鏈支持以“自定義元素模式”導(dǎo)入 SFC (需要 @vitejs/plugin-vue@^1.4.0 或 vue-loader@^16.5.0)。一個以自定義元素模式加載的 SFC 將會內(nèi)聯(lián)其 <style>
標(biāo)簽為 CSS 字符串,并將其暴露為組件的 styles 選項。這會被 defineCustomElement 提取使用,并在初始化時注入到元素的 ShadowRoot 上。
要開啟這個模式,將組件文件以 .ce.vue 結(jié)尾即可:
// Example.ce.vue <template> <h2>Example.ce</h2> </template> <script> </script> <style> h2 { color: red; } </style>
import { defineCustomElement } from 'vue' import Example from './Example.ce.vue' console.log(Example.styles) // 轉(zhuǎn)換為自定義元素構(gòu)造器 const ExampleElement = defineCustomElement(Example) // 注冊 customElements.define('my-example', ExampleElement)
基于 Vue 構(gòu)建自定義元素庫
按元素分別導(dǎo)出構(gòu)造函數(shù),以便用戶可以靈活地按需導(dǎo)入它們,還可以通過導(dǎo)出一個函數(shù)來方便用戶自動注冊所有元素。
// 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) // 分別導(dǎo)出元素 export { MyFoo, MyBar } export function register() { customElements.define('my-foo', MyFoo) customElements.define('my-bar', MyBar) }
defineComponent()
用來在定義 Vue 組件時為 TypeScript 提供類型推導(dǎo)的輔助函數(shù)。
對于一個 ts 文件,如果我們直接寫 export default {},無法有針對性的提示 vue 組件里應(yīng)該有哪些屬性。
但是,增加一層 defineComponet 的話,export default defineComponent({}),就可以對參數(shù)進(jìn)行一些類型推導(dǎo)和屬性的提示等操作。
function defineComponent( component: ComponentOptions | ComponentOptions['setup'] ): ComponentConstructor
參數(shù)是一個組件選項對象。返回值將是該選項對象本身,因為該函數(shù)實際上在運行時沒有任何操作,僅用于提供類型推導(dǎo),注意返回值的類型有一點特別:它是一個構(gòu)造函數(shù)類型,它是根據(jù)選項推斷出的組件實例類型。這樣做可以在 TSX 中將返回值用作標(biāo)簽時提供類型推斷支持。
你可以像這樣從 defineComponent()
的返回類型中提取出一個組件的實例類型 (與其選項中的 this
的類型等價):
const Foo = defineComponent(/* ... */) type FooInstance = InstanceType<typeof Foo>
defineAsyncComponent()
用來定義一個異步組件。在大型項目中,我們可能會將應(yīng)用拆分成更小的模塊,并在需要時從服務(wù)器加載相關(guān)組件。defineAsyncComponent 在運行時是懶加載的,參數(shù)可以是一個返回 Promise 的異步加載函數(shù)(resolve 回調(diào)方法應(yīng)該在從服務(wù)器獲得組件定義時調(diào)用),或是對加載行為進(jìn)行更具體定制的一個選項對象。
import { defineAsyncComponent } from 'vue' const AsyncComp = defineAsyncComponent(() => { return new Promise((resolve, reject) => { // ...從服務(wù)器獲取組件 resolve(/* 獲取到的組件 */) }) }) // ... 像使用其他一般組件一樣使用 `AsyncComp` // 也可以使用 ES 模塊動態(tài)導(dǎo)入 const AsyncComp = defineAsyncComponent(() => import('./components/MyComponent.vue') )
得到的 AsyncComp 是一個外層包裝過的組件,僅在頁面需要它渲染時才會調(diào)用加載內(nèi)部實際組件的函數(shù)。它會將接收到的 props 和插槽傳給內(nèi)部組件,所以你可以使用這個異步的包裝組件無縫地替換原始組件,同時實現(xiàn)延遲加載。
與普通組件一樣,異步組件可以使用 app.component() 全局注冊:
app.component('MyComponent', defineAsyncComponent(() => import('./components/MyComponent.vue') ))
也可以直接在父組件中直接定義它們:
<script setup> import { defineAsyncComponent } from 'vue' const AdminPage = defineAsyncComponent(() => import('./components/AdminPageComponent.vue') ) </script> <template> <AdminPage /> </template>
異步操作不可避免地會涉及到加載和錯誤狀態(tài),因此 defineAsyncComponent() 也支持在高級選項中處理這些狀態(tài):
const AsyncComp = defineAsyncComponent({ // 加載函數(shù) loader: () => import('./Foo.vue'), // 加載異步組件時使用的組件 loadingComponent: LoadingComponent, // 展示加載組件前的延遲時間,默認(rèn)為 200ms delay: 200, // 加載失敗后展示的組件 errorComponent: ErrorComponent, // 如果提供了一個時間限制,并超時了,也會顯示這里配置的報錯組件,默認(rèn)值是:Infinity timeout: 3000 })
以上是Vue3中如何使用defineCustomElement定義組件的詳細(xì)內(nèi)容。更多信息請關(guān)注PHP中文網(wǎng)其他相關(guān)文章!

熱AI工具

Undress AI Tool
免費脫衣服圖片

Undresser.AI Undress
人工智能驅(qū)動的應(yīng)用程序,用于創(chuàng)建逼真的裸體照片

AI Clothes Remover
用于從照片中去除衣服的在線人工智能工具。

Clothoff.io
AI脫衣機

Video Face Swap
使用我們完全免費的人工智能換臉工具輕松在任何視頻中換臉!

熱門文章

熱工具

記事本++7.3.1
好用且免費的代碼編輯器

SublimeText3漢化版
中文版,非常好用

禪工作室 13.0.1
功能強大的PHP集成開發(fā)環(huán)境

Dreamweaver CS6
視覺化網(wǎng)頁開發(fā)工具

SublimeText3 Mac版
神級代碼編輯軟件(SublimeText3)

想要實現(xiàn)頁面的局部刷新,我們只需要實現(xiàn)局部組件(dom)的重新渲染。在Vue中,想要實現(xiàn)這一效果最簡便的方式方法就是使用v-if指令。在Vue2中我們除了使用v-if指令讓局部dom的重新渲染,也可以新建一個空白組件,需要刷新局部頁面時跳轉(zhuǎn)至這個空白組件頁面,然后在空白組件內(nèi)的beforeRouteEnter守衛(wèi)中又跳轉(zhuǎn)回原來的頁面。如下圖所示,如何在Vue3.X中實現(xiàn)點擊刷新按鈕實現(xiàn)紅框范圍內(nèi)的dom重新加載,并展示對應(yīng)的加載狀態(tài)。由于Vue3.X中scriptsetup語法中組件內(nèi)守衛(wèi)只有o

Vue實現(xiàn)博客前端,需要實現(xiàn)markdown的解析,如果有代碼則需要實現(xiàn)代碼的高亮。Vue的markdown解析庫有很多,如markdown-it、vue-markdown-loader、marked、vue-markdown等。這些庫都大同小異。這里選用的是marked,代碼高亮的庫選用的是highlight.js。具體實現(xiàn)步驟如下:一、安裝依賴庫在vue項目下打開命令窗口,并輸入以下命令npminstallmarked-save//marked用于將markdown轉(zhuǎn)換成htmlnpmins

vue3+vite:src使用require動態(tài)導(dǎo)入圖片報錯和解決方法vue3+vite動態(tài)的導(dǎo)入多張圖片vue3如果使用的是typescript開發(fā),就會出現(xiàn)require引入圖片報錯,requireisnotdefined不能像使用vue2這樣imgUrl:require(’…/assets/test.png’)導(dǎo)入,是因為typescript不支持require所以用import導(dǎo)入,下面介紹如何解決:使用awaitimport

tinymce是一個功能齊全的富文本編輯器插件,但在vue中引入tinymce并不像別的Vue富文本插件一樣那么順利,tinymce本身并不適配Vue,還需要引入@tinymce/tinymce-vue,并且它是國外的富文本插件,沒有通過中文版本,需要在其官網(wǎng)下載翻譯包(可能需要翻墻)。1、安裝相關(guān)依賴npminstalltinymce-Snpminstall@tinymce/tinymce-vue-S2、下載中文包3.引入皮膚和漢化包在項目public文件夾下新建tinymce文件夾,將下載的

vue3+ts+axios+pinia實現(xiàn)無感刷新1.先在項目中下載aiXos和pinianpmipinia--savenpminstallaxios--save2.封裝axios請求-----下載js-cookienpmiJS-cookie-s//引入aixosimporttype{AxiosRequestConfig,AxiosResponse}from"axios";importaxiosfrom'axios';import{ElMess

最終效果安裝VueCropper組件yarnaddvue-cropper@next上面的安裝值針對Vue3的,如果時Vue2或者想使用其他的方式引用,請訪問它的npm官方地址:官方教程。在組件中引用使用時也很簡單,只需要引入對應(yīng)的組件和它的樣式文件,我這里沒有在全局引用,只在我的組件文件中引入import{userInfoByRequest}from'../js/api'import{VueCropper}from'vue-cropper&

vue3項目打包發(fā)布到服務(wù)器后訪問頁面顯示空白1、處理vue.config.js文件中的publicPath處理如下:const{defineConfig}=require('@vue/cli-service')module.exports=defineConfig({publicPath:process.env.NODE_ENV==='production'?'./':'/&

前言無論是vue還是react,當(dāng)遇到多處重復(fù)代碼的時候,我們都會想著如何復(fù)用這些代碼,而不是一個文件里充斥著一堆冗余代碼。實際上,vue和react都可以通過抽組件的方式來達(dá)到復(fù)用,但如果遇到一些很小的代碼片段,你又不想抽到另外一個文件的情況下,相比而言,react可以在相同文件里面聲明對應(yīng)的小組件,或者通過renderfunction來實現(xiàn),如:constDemo:FC=({msg})=>{returndemomsgis{msg}}constApp:FC=()=>{return(
