基于 Vue 3 构建企业级 Web Components 组件库

前言

在前端技术栈百花齐放的今天,我们经常面临一个痛点:组件复用难。React 组件无法直接在 Vue 项目中使用,Vue 2 的组件难以平滑迁移到 Vue 3。

Web Components 的出现正是为了解决这个问题。它是一套 W3C 标准,允许开发者创建可重用、封装良好且独立于框架的 UI 组件。无论你的主应用是 Vue、React 还是纯原生 JS,Web Components 都能完美运行。

一、 技术全景:什么是 Web Components?

Web Components 并非单一技术,而是由四项核心技术组成的规范集合,旨在实现组件的高内聚与低耦合。

1.1 核心组成体系

我们可以通过下图理解其运作机制:

graph TD WC[Web Components] --> CE[Custom Elements] WC --> SD[Shadow DOM] WC --> HT[HTML Templates] WC --> ES[ES Modules] subgraph "逻辑层: Custom Elements" CE --> CER[CustomElementRegistry] CE --> LC[生命周期回调] LC --> C1[connectedCallback <br/>(挂载)] LC --> C2[disconnectedCallback <br/>(卸载)] LC --> C3[attributeChangedCallback <br/>(属性变更)] end subgraph "视图层: Shadow DOM" SD --> SR[ShadowRoot] SD --> DOMI[DOM 隔离] SD --> CSSI[样式 隔离] end
  • Custom Elements:通过 CustomElementRegistry 定义浏览器直接识别的新标签(如 <tera-chat-root>)。
  • Shadow DOM:这是组件化的灵魂。它将组件内的 HTML 和 CSS 隐藏在 #shadow-root 中,完全隔离于外部文档。外部的 CSS 无法影响组件,组件的样式也不会污染外部。
  • HTML Templates:使用 <template> 标签定义结构。
  • ES Modules:标准的模块化加载方案。

二、 方案选型:为什么选择 Vue 3?

虽然原生 API 可以编写 Web Components,但通过 HTMLElement 手写繁琐的 DOM 操作和状态管理效率极低。

Vue 3 提供了 defineCustomElement API,让我们能用熟悉的 SFC (单文件组件) 语法开发,最后编译成标准的 Custom Element。

2.1 转换原理

Vue 编译器将组件转换为 Web Component 的流程如下:

graph TD VueSFC[Vue 单文件组件 (.vue)] -->|编译| VueCE[defineCustomElement] VueCE -->|封装| CE[HTMLElement 类] subgraph "运行时行为" CE -->|Props 映射| Atts[HTML Attributes] CE -->|Emits 映射| Events[Custom Events] CE -->|挂载| SR[Shadow Root] end SR -->|注入| Styles[CSS (Inline)] SR -->|渲染| Template[DOM 结构]

三、 工程化架构

为了满足企业级开发需求(TypeScript、Pinia 状态管理、多环境构建),我们需要设计合理的目录结构。

3.1 项目结构 (vite-shadow-dom)

vite-shadow-dom/ ├── demo/ # 调试/演示应用(模拟真实使用场景) │ ├── main.ts │ └── index.html ├── src/ # 组件库源码 │ ├── components/ │ │ └── ChatRoot.vue # 核心业务组件 │ ├── styles/ # 全局样式 │ ├── entry.ts # 【核心】自定义元素注册入口 │ └── vite-env.d.ts ├── scripts/ # 构建脚本 (npm publish, build) ├── vite.config.ts # 标准构建配置 (Vue 3) ├── vite.compat.config.ts # 兼容构建配置 (Vue 2/无框架) └── package.json

3.2 产出物设计

为了兼顾不同使用场景,我们设计了两套构建产物:

  1. Standard (标准版):依赖外部 Vue 运行时,体积小。适用于宿主环境已经是 Vue 3 的项目。
  2. Compat (兼容版)内联 Vue 运行时。适用于 Vue 2、React 或 jQuery 等非 Vue 3 环境,避免版本冲突。

四、 核心代码实现

4.1 解决痛点:Shadow DOM 中的样式与状态管理

在 Shadow DOM 中使用 Vue 生态库(如 Pinia)和全局样式会遇到两个挑战:

  1. Pinia 挂载问题:Web Component 内部没有常规的 Vue App 实例。
  2. 样式隔离问题:全局 CSS 无法穿透 Shadow DOM。

我们需要在 entry.ts 入口文件中进行特殊处理:

// src/entry.ts import { defineCustomElement, provide, h } from 'vue'; import { createPinia, setActivePinia } from 'pinia'; import ChatRoot from './components/ChatRoot.vue'; // 利用 ?inline 导入样式字符串,而非通过 style 标签插入 head import commonStyles from '@/styles/index.scss?inline'; // 定义组件标签名常量 export enum SHADOW_DOM { CHAT_ROOT = 'tera-chat-root', CHAT_ROOT_UMD = 'TeraChatRoot', }; // 封装 defineCustomElement const ChatRootElement = defineCustomElement({ // 继承原始组件逻辑 ...ChatRoot, setup(props, ctx) { // 1. 手动初始化 Pinia const pinia = createPinia(); setActivePinia(pinia); // 注入到组件树中 provide('pinia', pinia); // 2. 调用原始组件的 setup return ChatRoot.setup?.(props, ctx); }, // 3. 注入样式:Vue 会自动将这些 CSS 字符串注入到 ShadowRoot 的 <style> 中 styles: [commonStyles, ...(ChatRoot.styles || [])], }); // 注册自定义元素(防止重复注册) if (!customElements.get(SHADOW_DOM.CHAT_ROOT)) { customElements.define(SHADOW_DOM.CHAT_ROOT, ChatRootElement); } // 导出以便 UMD 环境挂载到 window export { ChatRootElement as TeraShadowDom }; if (typeof window !== 'undefined') { (window as any)[SHADOW_DOM.CHAT_ROOT_UMD] = ChatRootElement; }
CSS 中若使用了 :root 定义变量,在 Shadow DOM 中需替换为 :host,否则无法生效。

4.2 构建配置:多版本共存

我们需要两个 Vite 配置文件来应对不同场景。

Vue 2 兼容版配置 (vite.compat.config.ts):

import { defineConfig } from 'vite'; import vue from '@vitejs/plugin-vue'; export default defineConfig({ plugins: [ vue({ customElement: true }), // 开启 Custom Element 模式 ], build: { lib: { entry: 'src/entry.ts', name: 'TeraShadowDomCompat', fileName: (format) => `tera-shadow-dom.vue2-compat.${format}.js`, }, // 关键点:将 rollupOptions.external 设为空 // 这样 Vue 运行时会被打包进组件库中,确保在 Vue 2 环境下也能运行 Vue 3 逻辑 rollupOptions: { external: [], }, }, });

五、 组件使用指南

构建完成后,我们的组件就可以在任何地方使用了。

场景 1:原生 HTML (CDN 方式)

直接引入 UMD 文件,像使用 HTML 原生标签一样使用它。

<body> <!-- 引入打包后的 JS --> <script src="./dist/tera-shadow-dom.vue2-compat.umd.js"></script> <!-- 直接使用标签 --> <tera-chat-root token="sk-123456"></tera-chat-root> <script> const el = document.getElementById('my-chat'); // 监听自定义事件 el.addEventListener('btn-click', (e) => { console.log('Clicked:', e.detail); }); // 动态修改属性 el.setAttribute('token', 'new-token'); </script> </body>

场景 2:在 Vue 2 项目中集成

由于 Vue 2 不认识 defineCustomElement,必须引入我们的 Compat (兼容) 版本。

// main.js // 引入包含 Vue 3 运行时的兼容包 import '@baidu/vite-shadow-dom/dist/tera-shadow-dom.vue2-compat.es.js';

<!-- 组件内使用 --> <template> <div> <!-- Vue 2 会将其视为原生标签,跳过组件解析 --> <tera-chat-root :token="token" @btn-click="handleClick" ></tera-chat-root> </div> </template>

场景 3:在 Vue 3 项目中集成

Vue 3 环境天然支持,可以使用轻量版(不含 Vue 运行时)。

// main.ts import '@baidu/vite-shadow-dom'; // 引入注册逻辑

如果使用 TS,记得在 vue 模块中补充类型声明,否则 <tera-chat-root> 可能会报类型错误。


六、 总结

基于Web Components + Vue 3能够实现 :

  1. 样式隔离:Shadow DOM 彻底解决了 CSS 污染问题。
  2. 框架解耦:一次编写,到处运行(Vue2/3/React/jQuery)。
  3. 开发效率:利用 Vue 3 的响应式系统简化开发,利用 Vite 实现高效构建。

这种模式非常适合开发通用的业务组件库(如 AI 助手、支付弹窗、反馈组件),让基础设施团队能够跨越业务线技术栈的差异,提供统一的服务。

Read more

宜搭-低代码开发师(高级)认证实操题1-待办列表

宜搭-低代码开发师(高级)认证实操题1-待办列表

终于通过了认证!!!耗时整理了一份自己实操的实现步骤,主要是复习使用自定义页面表格实现数据管理页功能✌✌✌希望大家都能顺利通过!!! 1. 考前须知 如下图:需要扫描二维码加入组织,我当时扫描失效,以下是另一种加入组织的方法 步骤1:打开手机钉钉右下角点击我的找到【客服与帮助】 步骤2:在【客服与帮助】页面下滑找到【快捷工具】选择【加入团队】即可根据名称搜索加入组织   2. 项目实操 2.1新增普通表单 2.1.1进行中待办 (1) 创建如下字段: * 待办事项:单行文本组件,必填 * 分类:单选组件,必填,按照个人、工作、其他分类 * 重要度:评分组件,默认值为1,必填 * 设置提醒日期:日期组件,格式为年月日 * 待办详情:多行文本组件 (2)设置重要度的默认值为1

【STM32项目开源】基于STM32的智能家居环境监测系统

【STM32项目开源】基于STM32的智能家居环境监测系统

目录 一、设计背景和意义 1.1设计背景 1.2设计意义 二、实物效果展示 2.1实物图片 2.2实物演示视频 三、硬件功能简介 3.1项目功能详解 3.2元器件清单 四、主框图与软件流程图 五、硬件PCB展示 六、软件程序设计 七、项目资料包内容          资料获取:查看主页介绍“充哥单片机设计” 一、设计背景和意义 1.1设计背景         随着物联网(IoT)、嵌入式系统和云计算等技术的飞速发展,智能家居系统正在逐渐改变人们的生活方式。智能家居不仅仅是简单的远程开关控制,而是向着环境感知、自主判断、智能决策的方向不断演进。特别是在城市化进程加快、生活节奏加快的背景下,用户对生活便捷性、家庭安全性和环境舒适度的要求不断提高,这对智能家居系统的综合感知、智能响应能力提出了更高的要求。         当前市面上的智能家居产品多以分立模块存在,系统功能较为单一,

2026年RAG技术路线图:基于DeepSeek与Neo4j知识图谱构建企业智能体系

RAG的演进:为何图检索增强生成(GraphRAG)将主导2026年 检索增强生成(RAG)自问世以来经历了深刻变革,2026年标志着其向图检索增强生成(GraphRAG)范式的关键性转变。这一演进源于传统平面向量型RAG在满足企业级复杂推理和可靠决策支持需求方面日益凸显的局限性。 这一转型的核心驱动力是从平面向量相似性向复杂关系推理的跨越。传统RAG依赖向量嵌入来衡量查询与文档片段的语义相似性,但这种方法无法捕捉企业决策至关重要的实体、概念与事件间的复杂关联。相比之下,GraphRAG将信息构建为包含节点(实体)和边(关系)的知识图谱,使模型能够遍历并推理这些关联——解锁了平面向量RAG无法实现的多跳推理和上下文关系理解能力。 GraphRAG还解决了传统RAG的两大长期痛点:上下文窗口限制和“中间信息丢失”问题。随着企业查询日益复杂,需要更大的上下文窗口来整合相关信息,但即便是最先进的大语言模型(LLM)也存在有限的上下文容量。GraphRAG通过将结构化知识存储在外部图数据库中解决了这一问题,允许模型按需检索最相关的节点和关系,而非将大量文本塞入上下文窗口。此外,“中间信息

宇树机器人g1二次开发:建图,定位,导航手把手教程(四)导航仿真部分:建完图之后打开仿真导航

先补一下第三教程的四看路线这一块:终端2那里,可以先修改下面图片所示的文件为true,这样打开终端2的时候就可以打开设定好的rviz,就不用单独打开rviz。但是开始播放bag包之后还是要单独添加话题。 建成pcd之后可以查看pcd图:filename.pcd换成自己pcd的名字 pcl_viewer filename.pcd 额外说一句,有几种类型的图大家不要弄错了: 一、下载项目 # 克隆导航栈源码 cd ~/ws_loc/src git clone https://github.com/deepglint/ros_navigation_humanoid.git # 重命名并整理目录结构 mv ros_navigation_humanoid-master ros_navigation_humanoid 1.2、安装系统依赖 # 安装ROS导航相关包 sudo apt-get install libpcl-dev ros-noetic-navigation ros-noetic-tf