如何利用Vue 3的`Custom Elements`构建跨框架组件？

admin
Mon, 08 Sep 2025 08:08:50 +0000
Vue, custom, elements, vue

Vue 3 Custom Elements：构建跨框架组件的利器 大家好，今天我们来聊聊如何利用 Vue 3 的 Custom Elements 功能，构建可以在不同框架间复用的组件。这是一种非常强大的技术，能够帮助我们解决组件在不同技术栈之间共享的问题，提高代码复用率，降低维护成本。 1. 什么是 Custom Elements？ Custom Elements 是 Web Components 的一部分，它是一种浏览器原生提供的 API，允许我们定义自己的 HTML 元素。这些自定义元素可以像标准的 HTML 元素一样使用，并且拥有自己的行为和样式。Web Components 包括三个主要技术： Custom Elements: 定义新的 HTML 元素。 Shadow DOM: 为元素创建独立的 DOM 树，避免样式冲突。 HTML Templates: 定义可复用的 HTML 模板。 Custom Elements 的核心思想是，将组件封装成一个自定义的 HTML 标签，使其可以在任何支持 Web Components 的浏览器环境中使用，无需依赖特定的框架。 2. Vue …

继续阅读“如何利用Vue 3的`Custom Elements`构建跨框架组件？”

The post 如何利用Vue 3的`Custom Elements`构建跨框架组件？ first appeared on 智猿学院-前后端，数据库，人工智能，云计算等领域前沿技术讲座.

Vue 3 Custom Elements：构建跨框架组件的利器

大家好，今天我们来聊聊如何利用 Vue 3 的 Custom Elements 功能，构建可以在不同框架间复用的组件。这是一种非常强大的技术，能够帮助我们解决组件在不同技术栈之间共享的问题，提高代码复用率，降低维护成本。

1. 什么是 Custom Elements？

Custom Elements 是 Web Components 的一部分，它是一种浏览器原生提供的 API，允许我们定义自己的 HTML 元素。这些自定义元素可以像标准的 HTML 元素一样使用，并且拥有自己的行为和样式。Web Components 包括三个主要技术：

Custom Elements: 定义新的 HTML 元素。

Shadow DOM: 为元素创建独立的 DOM 树，避免样式冲突。

HTML Templates: 定义可复用的 HTML 模板。

Custom Elements 的核心思想是，将组件封装成一个自定义的 HTML 标签，使其可以在任何支持 Web Components 的浏览器环境中使用，无需依赖特定的框架。

2. Vue 3 与 Custom Elements 的关系

Vue 3 对 Custom Elements 提供了良好的支持。我们可以使用 Vue 组件的逻辑来定义 Custom Elements，并利用 Vue 的数据绑定、计算属性、生命周期钩子等特性。Vue 3 提供了 defineCustomElement 方法，专门用于将 Vue 组件转换为 Custom Elements。

3. 构建跨框架组件的步骤

下面我们一步步讲解如何使用 Vue 3 构建跨框架组件。

3.1 创建 Vue 组件

首先，我们需要创建一个标准的 Vue 组件。这个组件将包含我们想要复用的逻辑和 UI。

// MyButton.vue


    <slot></slot>




import { ref, computed } from 'vue';

export default {
  props: {
    type: {
      type: String,
      default: 'primary'
    },
    size: {
      type: String,
      default: 'medium'
    }
  },
  setup(props) {
    const count = ref(0);

    const handleClick = () => {
      count.value++;
      // 发送自定义事件
      const event = new CustomEvent('button-clicked', {
        detail: {
          count: count.value
        }
      });
      this.$el.dispatchEvent(event);
    };

    const buttonStyle = computed(() => {
      let style = {
        padding: '8px 16px',
        borderRadius: '4px',
        border: 'none',
        cursor: 'pointer',
        fontSize: '14px',
        color: '#fff'
      };

      if (props.type === 'primary') {
        style.backgroundColor = '#409EFF';
      } else if (props.type === 'secondary') {
        style.backgroundColor = '#67C23A';
      } else {
        style.backgroundColor = '#E6A23C';
      }

      if (props.size === 'small') {
        style.padding = '4px 8px';
        style.fontSize = '12px';
      } else if (props.size === 'large') {
        style.padding = '12px 24px';
        style.fontSize = '16px';
      }

      return style;
    });

    return {
      count,
      handleClick,
      buttonStyle
    };
  }
};



button {
  transition: background-color 0.3s ease;
}

button:hover {
  opacity: 0.8;
}


这个组件定义了一个带有不同类型和大小的按钮，并且在点击时会触发一个自定义事件 button-clicked。

3.2 使用 defineCustomElement 转换组件

接下来，我们使用 Vue 3 的 defineCustomElement 方法将 Vue 组件转换为 Custom Element。

// main.js (或者单独的文件，例如：custom-element.js)
import { defineCustomElement } from 'vue';
import MyButton from './MyButton.vue';

const MyButtonElement = defineCustomElement(MyButton);

customElements.define('my-button', MyButtonElement);

这段代码首先导入 defineCustomElement 方法和我们的 Vue 组件 MyButton。然后，我们使用 defineCustomElement 将 MyButton 转换为一个 Custom Element 类 MyButtonElement。最后，我们使用 customElements.define 方法将这个类注册为名为 my-button 的自定义元素。

3.3 在其他框架中使用 Custom Element

现在，我们就可以在其他框架中使用这个 Custom Element 了。例如，我们可以在一个纯 HTML 页面中使用它：




Custom Element Example





Using Custom Element

  <my-button type="primary" size="large">Click Me!</my-button>


    const myButton = document.querySelector('my-button');

    myButton.addEventListener('button-clicked', (event) => {
      console.log('Button clicked!', event.detail.count);
    });




或者在 React 中使用：

import React, { useEffect } from 'react';

function App() {
  useEffect(() => {
    // 确保 Custom Element 已经定义
    if (!customElements.get('my-button')) {
      // 动态加载定义 Custom Element 的 JavaScript 文件
      const script = document.createElement('script');
      script.src = '/custom-element.js'; // 假设 custom-element.js 在 public 目录下
      document.body.appendChild(script);

      script.onload = () => {
        console.log('Custom Element loaded');
      };
    }
  }, []);

  const handleButtonClick = (event) => {
    console.log('Button clicked in React!', event.detail.count);
  };

  return (

React App with Custom Element
      <my-button type="secondary" size="small" onClick={handleButtonClick}>Click Me in React!</my-button>

  );
}

export default App;

注意： 在 React 中，你需要确保 Custom Element 已经定义，否则会报错。 我们这里使用 useEffect 在组件挂载的时候动态加载定义 Custom Element 的 js 文件。 另外，React 事件绑定方式和Custom Element 原生事件监听方式不同，需要使用 onClick 属性绑定一个函数，然后在该函数内部触发 my-button 的事件。

3.4 Custom Elements 生命周期

Custom Elements 提供了一些生命周期钩子，允许我们在元素的不同阶段执行自定义逻辑：

connectedCallback: 当元素被插入到 DOM 时调用。

disconnectedCallback: 当元素从 DOM 中移除时调用。

attributeChangedCallback: 当元素的属性发生变化时调用。

adoptedCallback: 当元素被移动到新的文档时调用。

我们可以通过在 Vue 组件中定义这些生命周期钩子来扩展 Custom Element 的行为。 在 defineCustomElement 生成的 Custom Element 类中，这些钩子会自动映射到 Vue 组件的生命周期。

例如：

// MyButton.vue (修改后的)


    <slot></slot>




import { ref, computed, onMounted, onUnmounted } from 'vue';

export default {
  props: {
    type: {
      type: String,
      default: 'primary'
    },
    size: {
      type: String,
      default: 'medium'
    }
  },
  setup(props) {
    const count = ref(0);

    const handleClick = () => {
      count.value++;
      // 发送自定义事件
      const event = new CustomEvent('button-clicked', {
        detail: {
          count: count.value
        }
      });
      this.$el.dispatchEvent(event);
    };

    const buttonStyle = computed(() => {
      let style = {
        padding: '8px 16px',
        borderRadius: '4px',
        border: 'none',
        cursor: 'pointer',
        fontSize: '14px',
        color: '#fff'
      };

      if (props.type === 'primary') {
        style.backgroundColor = '#409EFF';
      } else if (props.type === 'secondary') {
        style.backgroundColor = '#67C23A';
      } else {
        style.backgroundColor = '#E6A23C';
      }

      if (props.size === 'small') {
        style.padding = '4px 8px';
        style.fontSize = '12px';
      } else if (props.size === 'large') {
        style.padding = '12px 24px';
        style.fontSize = '16px';
      }

      return style;
    });

    onMounted(() => {
      console.log('MyButton connected to DOM!');
    });

    onUnmounted(() => {
      console.log('MyButton disconnected from DOM!');
    });

    return {
      count,
      handleClick,
      buttonStyle
    };
  },
  // 模拟 attributeChangedCallback
  watch: {
    type(newValue, oldValue) {
      console.log(`Type attribute changed from ${oldValue} to ${newValue}`);
    },
    size(newValue, oldValue) {
      console.log(`Size attribute changed from ${oldValue} to ${newValue}`);
    }
  }
};



button {
  transition: background-color 0.3s ease;
}

button:hover {
  opacity: 0.8;
}


在这个例子中，我们使用了 onMounted 和 onUnmounted 钩子来模拟 connectedCallback 和 disconnectedCallback 的行为。 另外，我们通过 watch 监听 type 和 size 属性的变化，模拟 attributeChangedCallback。

4. 样式隔离

Custom Elements 的一个重要特性是 Shadow DOM，它可以为元素创建一个独立的 DOM 树，从而实现样式隔离。Vue 3 的 defineCustomElement 方法默认会使用 Shadow DOM。

这意味着，Custom Element 的样式不会受到外部 CSS 的影响，也不会影响外部 CSS 的样式。这可以有效地避免样式冲突，保证组件的样式一致性。

如果需要禁用 Shadow DOM，可以在 defineCustomElement 的选项中设置 shadowRoot: false：

const MyButtonElement = defineCustomElement(MyButton, { shadowRoot: false });

5. 数据传递

Custom Elements 通过属性 (attributes) 和事件 (events) 与外部环境进行数据传递。

属性: 我们可以通过 HTML 属性来设置 Custom Element 的数据。这些属性会自动映射到 Vue 组件的 props。

事件: Custom Element 可以触发自定义事件，供外部环境监听。

在上面的例子中，我们使用了 type 和 size 属性来设置按钮的类型和大小，并使用了 button-clicked 事件来通知外部环境按钮被点击了。

6. 优势和局限性

使用 Vue 3 Custom Elements 构建跨框架组件有很多优势：

代码复用: 可以在不同的框架之间复用组件，避免重复开发。

技术栈无关: Custom Elements 是浏览器原生支持的技术，不依赖特定的框架。

样式隔离: Shadow DOM 可以避免样式冲突。

易于维护: 组件的逻辑和 UI 都封装在 Custom Element 内部，易于维护和升级。

但也存在一些局限性：

学习成本: 需要学习 Web Components 和 Custom Elements 的相关知识。

兼容性: 虽然现代浏览器都支持 Web Components，但对于一些老旧的浏览器可能需要 polyfill。

SEO: 对于一些搜索引擎来说，Shadow DOM 可能会影响 SEO。

7. 最佳实践

明确组件边界: 在设计 Custom Element 时，要明确组件的职责和边界，避免组件过于复杂。

规范属性和事件: 定义清晰的属性和事件接口，方便外部环境使用。

提供文档: 为 Custom Element 提供详细的文档，包括属性、事件、用法示例等。

测试: 对 Custom Element 进行充分的测试，确保其功能和性能符合要求。

版本控制: 使用版本控制系统管理 Custom Element 的代码，方便维护和升级。

按需加载： 避免一次性加载所有的 Custom Element，可以使用动态导入的方式按需加载，提高页面加载速度。

表格： Vue 组件与 Custom Element 的对比

特性 | Vue 组件 | Custom Element
定义方式 | 使用 Vue 的 template 和 script 定义 | 使用 defineCustomElement 将 Vue 组件转换为类
运行环境 | Vue 应用 | 任何支持 Web Components 的浏览器环境
依赖 | Vue 框架 | 无特定框架依赖
样式隔离 | 可选的 scoped CSS | 默认使用 Shadow DOM (可配置)
数据传递 | props 和 events | attributes 和 events
生命周期 | Vue 组件的生命周期钩子 | Custom Elements 的生命周期钩子 (connectedCallback, disconnectedCallback, attributeChangedCallback 等)

8. 总结：Custom Elements 让组件跨框架成为可能

Vue 3 的 defineCustomElement 方法使得我们可以方便地将 Vue 组件转换为 Custom Elements，从而实现组件在不同框架之间的复用。通过学习 Web Components 和 Custom Elements 的相关知识，我们可以更好地利用这项技术，构建更加灵活和可维护的前端应用。

9. 未来展望：拥抱 Web Components 的未来

Web Components 作为一种标准化的 Web 技术，正在逐渐被越来越多的开发者所接受和使用。随着浏览器对 Web Components 的支持越来越完善，相信 Custom Elements 将会在前端开发中扮演越来越重要的角色。 我们可以利用 Vue 3 提供的工具，更好地拥抱 Web Components 的未来，构建更加灵活和可复用的组件库。