使用 Vue3 構建 Web Components

来源:https://www.cnblogs.com/nextl/archive/2022/09/08/16668952.html
-Advertisement-
Play Games

有時候想寫一個無關框架組件,又不想用原生或者 Jquery 那套去寫,而且還要避免樣式衝突,用 Web Components 去做剛覺就挺合適的。但是現在 Web Components 使用起來還是不夠靈活,很多地方還是不太方便的,如果能和 MVVM 搭配使用就好了。早在之前 Angular 就支持 ...


有時候想寫一個無關框架組件,又不想用原生或者 Jquery 那套去寫,而且還要避免樣式衝突,用 Web Components 去做剛覺就挺合適的。但是現在 Web Components 使用起來還是不夠靈活,很多地方還是不太方便的,如果能和 MVVM 搭配使用就好了。早在之前 Angular 就支持將組件構建成 Web Components,Vue3 3.2+ 開始終於支持將組建構建成 Web Components 了。正好最近想重構下評論插件,於是上手試了試。

構建 Web Components

vue 提供了一個 defineCustomElement 方法,用來將 vue 組件轉換成一個擴展至HTMLElement的自定義函數構造函數,使用方式和 defineComponent 參數api基本保持一致。

import { defineCustomElement } from 'vue' 

const MyVueElement = defineCustomElement({
  // 在此提供正常的 Vue 組件選項
  props: {},
  emits: {},
  template: `...`,

  // defineCustomElement 獨有特性: CSS 會被註入到隱式根 (shadow root) 中
  styles: [`/* inlined css */`]
})

// 註冊 Web Components
customElements.define('my-vue-element', MyVueElement)

如果需要使用單文件,需要 @vitejs/plugin-vue@^1.4.0vue-loader@^16.5.0 或更高版本工具。如果只是部分文件需要使用,可以將尾碼改為 .ce.vue 。若果需要將所有文件都構建 Web Components 可以將 @vitejs/plugin-vue@^1.4.0vue-loader@^16.5.0customElement 配置項開啟。這樣不需要再使用 .ce.vue 尾碼名了。

屬性

vue 會把所有的的 props 自定義元素的對象的 property 上,也會將自定義元素標簽上的 attribute 做一個映射。

<com-demo type="a"></com-demo>

props:{
  type:String
}

因為 HTML 的 attribute 的只能是字元串,除了基礎類型(Boolean、Number) Vue 在映射時會幫忙做類型轉換,其他複雜類型則需要設置到 DOM property 上。

事件

在自定義元素中,通過 this.$emit 或在 setup 中的 emit 發出的事件會被調度為原生 CustomEvents。附加的事件參數 (payload) 會作為數組暴露在 CustomEvent 對象的 details property 上。

插槽

編寫組件時,可以想 vue 一樣,但是使用時只能原生的插槽語法,所以也不在支持作用域插槽。

子組件樣式問題

使用子組件嵌套的時,有個坑的地方就是預設不會將子組件里的樣式抽離出來。

父組件

<template>
    <div class="title">{{ title }}</div>
    <Childer />
</template>
<script>
import Childer from "./childer.vue"
export default {
    components: { Childer },
    data() {
        return {
            title: "父組件"
        }
    },
}
</script>
<style lang="less" scoped>
.title {
    padding: 10px;
    background-color: #eee;
    font-weight: bold;
}
</style>

子組件

<template>
    <div class="childer">{{ title }}</div>
</template>
<script>
export default {
    data() {
        return {
            title: "子組件"
        }
    },
}
</script>
<style lang="less" scoped>
.childer {
    padding: 10px;
    background-color: #222;
    color: #fff;
    font-weight: bold;
}
</style>

可以看到子組件的樣式沒有插入進去,但是樣式隔離的標識是有生成的 data-v-5e87e937。不知道vue官方後續會不會修複這個bug

插入圖片
查看組件是可以看到,子組件的樣式是有被抽離出來的,這樣就只需要自己註入進去了。

插入圖片

將子組件樣式抽離插入到父組件里,參考這個的實現

import ComDemo from '~/demo/index.vue'

const deepStylesOf = ({ styles = [], components = {} }) => {
    const unique = array => [...new Set(array)];
    return unique([...styles, ...Object.values(components).flatMap(deepStylesOf)]);
}
// 將子組件樣式插入到父組件里
ComDemo.styles = deepStylesOf(ComDemo)

!customElements.get('com-demo') && customElements.define('com-demo', defineCustomElement(ComDemo))

完美解決子組件樣式問題

插入圖片

方法

defineCustomElement 構建的組件預設是不會將方法掛到 customElement 上的,看 Vue 源碼中,只有 _def(構造函數),_instance(組件實例))。如果想調用組件內的方法,dom._instance.proxy.fun(),感覺實在不太優雅。
vuecode
我們當然希望我們組件暴露的方法能像普通dom那樣直接 dom.fun() 去掉用,我們對 defineCustomElement 稍作擴展。

import { VueElement, defineComponent } from 'vue'

const defineCustomElement = (options, hydate) => {
    const Comp = defineComponent(options);
    class VueCustomElement extends VueElement {
        constructor(initialProps) {
            super(Comp, initialProps, hydate);
            if (Comp.methods) {
                Object.keys(Comp.methods).forEach(key => {
                    // 將所有非下劃線開頭方法 綁定到 元素上
                    if(!/^_/.test(key)){
                        this[key] = function (...res) {
                            if (this._instance) {
                                // 將方法thi改為 組件實例的proxy
                                return Comp.methods[key].call(this._instance.proxy, ...res)
                            } else {
                                throw new Error('未找到組件實例')
                            }
                        }
                    }
                })
            }
        }
    }
    VueCustomElement.def = Comp;
    return VueCustomElement;
}

總結

總體來說坑還是有不少的,如果僅僅需要構建一些比較簡單跨框架插件,使用這種方式來構建 Web Components 也是一種不錯的方案。


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 摘要:執行引擎一般負責查詢的執行,執行引擎在SQL執行棧中起到接收優化器生成的執行計劃Plan、並對通過存儲引擎提供的數據讀寫介面,實現對數據進行計算得到查詢的結果集。 本文分享自華為雲社區《openGauss內核分析(七):SQL by pass & 經典執行器》,作者:Gauss松鼠會 。 執行 ...
  • 資料庫 SQL SERVER 2008,連接時報:[IM002]Navicat ODBC驅動器管理器 未發現數據源名稱並且未指定預設驅動程式 到安裝目錄下找到 sqlncli_x64.mis 雙擊安裝 下一步,直到安裝完成 ...
  • 1. 參考https://ask.dcloud.net.cn/article/36937: 增加如下參考條款 我們的產品基於DCloud uni-app(5+ App/Wap2App)開發,應用運行期間需要收集您的設備唯一識別碼(IMEI/android ID/DEVICE_ID/IDFA、SIM ...
  • 華為開發者聯盟和艾瑞咨詢聯合發佈《2022年移動應用技術趨勢白皮書》,本白皮書通過盤點國內移動應用發展環境、熱門技術創新動態,分析影響移動應用發展的技術趨勢,以及細分應用領域的技術創新熱點,洞悉開發者所面臨的挑戰和機遇,幫助開發者釐清不斷發展的技術局面,提供可落地的行業洞察。 華為開發者聯盟一直致力 ...
  • Math對象 Math對象,不是一個構造函數,所以我們不需要new 來調用,而是直接使用裡面的屬性和方法即可,它具有數學常數和函數的方法,跟數學相關的運算(求絕對值,取整,最大值等)可以使用Math中的成員。 1.Math絕對值和三個取整的方法: 2.Math隨機數方法 Math對象隨機數方法:ra ...
  • 每日3題 28 以下代碼執行後,控制臺中的輸出內容為? function showCase(value){ switch(value){ case 'A': console.log('case A'); break; case 'B': console.log('case B'); break; c ...
  • 多個元素的過渡 點擊打開視頻講解更加詳細 我們之後討論多個組件的過渡,對於原生標簽可以使用 v-if/v-else。最常見的多標簽過渡是一個列表和描述這個列表為空消息的元素: <transition> <table v-if="items.length > 0"> <!-- ... --> </ta ...
  • process-env 在使用vue框架時,經常用到的倆種環境,一種是開發環境,一種是生產環境。 平時開發使用的是開發環境,如果發佈到線上時,需要切換為線上環境。可以通過不同配置不同的運行命令來自動切換環境。 配置環境實現原理 實現原理是採用nodeJS的頂層對象中的process.env(進程環境 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...