0

0

Vue3中如何使用defineCustomElement定义组件

PHPz

PHPz

发布时间:2023-05-28 11:29:52

|

3766人浏览过

|

来源于亿速云

转载

    使用 vue 构建自定义元素

    web components 是一组 web 原生 api 的统称,允许开发者创建可复用的自定义元素 (custom elements)。

    使用自定义元素的主要优势在于,它们可以在任何框架甚至非框架环境中使用。当你面向的最终用户可能使用了不同的前端技术栈,或是当你希望将最终的应用与它使用的组件实现细节解耦时,它们会是理想的选择。

    Vue 和 Web Components 是互补的技术,Vue 为使用和创建自定义元素提供了出色的支持。在 Vue 应用中,你可以集成自定义元素,也可以使用 Vue 来构建和发布自定义元素。

    Vue 在 Custom Elements Everywhere 测试中取得了 100% 的分数。在 Vue 应用中使用自定义元素基本上与使用原生 HTML 元素的效果相同,但需要进行一些额外的配置才能工作:

    跳过组件解析

    默认情况下,Vue 会将任何非原生的 HTML 标签优先当作 Vue 组件处理,而将“渲染一个自定义元素”作为后备选项。这会在开发时导致 Vue 抛出一个“解析组件失败”的警告。

    立即学习前端免费学习笔记(深入)”;

    • 要让 Vue 知晓特定元素应该被视为自定义元素并跳过组件解析,我们可以指定 compilerOptions.isCustomElement 这个选项,设置在此选项对象上的值将会在浏览器内进行模板编译时使用,并会影响到所配置应用的所有组件。

    • 另外也可以通过 compilerOptions 选项在每个组件的基础上覆盖这些选项(针对当前组件有更高的优先级)。

    因为它是一个编译时选项,构建工具需要将配置传递给 @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- 的标签名都视为自定义元素
              isCustomElement: tag => tag.startsWith('ion-')
            }
          }))
      }
    }
    • vite:通过 @vitejs/plugin-vue 的选项传递。

    // vite.config.js
    import vue from '@vitejs/plugin-vue'
    export default {
      plugins: [
        vue({
          template: {
            compilerOptions: {
              // 将所有带短横线的标签名都视为自定义元素
              isCustomElement: (tag) => tag.includes('-')
            }
          }
        })
      ]
    }
    • 浏览器内编译时的配置。

    // src/main.js
    // 仅在浏览器内编译时才会工作
    const app = createApp(App)
    app.config.compilerOptions.isCustomElement = (tag) => tag.includes('-')

    传递 DOM 属性

    由于 DOM attribute 只能为字符串值,因此我们只能使用 DOM 对象的属性来传递复杂数据。当为自定义元素设置 props 时,Vue 3 将通过 in 操作符自动检查该属性是否已经存在于 DOM 对象上,并且在这个 key 存在时,更倾向于将值设置为一个 DOM 对象的属性。这意味着,在大多数情况下,如果自定义元素遵循推荐的最佳实践,你就不需要考虑这个问题。

    然而,也会有一些特别的情况:必须将数据以一个 DOM 对象属性的方式传递,但该自定义元素无法正确地定义/反射这个属性 (因为 in 检查失败)。在这种情况下,你可以强制使用一个 v-bind 绑定、通过 .prop 修饰符来设置该 DOM 对象的属性:

    <my-element :user.prop="{ name: 'jack' }"></my-element>
    <!-- 等价简写 -->
    <my-element .user="{ name: 'jack' }"></my-element>

    defineCustomElement()

    Vue 提供了一个和定义一般 Vue 组件几乎完全一致的 defineCustomElement 方法来支持创建自定义元素。defineComponent 和这个方法所接收的参数完全一致。但它会返回一个继承自 HTMLElement 的原生自定义元素类的构造器(可以通过 customElements.define() 注册)。

    function defineCustomElement(
      component:
        | (ComponentOptions & { styles?: string[] })
        | ComponentOptions['setup']
    ): {
      new (props?: object): HTMLElement
    }

    除了常规的组件选项,defineCustomElement() 还支持一个特别的选项 styles,它是一个内联 CSS 字符串的数组,所提供的 CSS 会被注入到该元素的 ShadowRoot 上。

    <my-vue-element></my-vue-element>
    import { defineCustomElement } from 'vue'
    const MyVueElement = defineCustomElement({
      // 这里是同平常一样的 Vue 组件选项
      props: {},
      emits: {},
      template: `...`,
      // defineCustomElement 特有的:注入进 ShadowRoot 的 CSS
      styles: [`/* css */`]
    })
    // 注册自定义元素之后,所有此页面中的 `<my-vue-element>` 标签都会被升级
    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' } },

    生命周期

    • 当该元素的 connectedCallback 初次调用时,一个 Vue 自定义元素会在内部挂载一个 Vue 组件实例到它的 ShadowRoot 上。

    • 当此元素的 disconnectedCallback 被调用时,Vue 会在一个微任务后检查元素是否还留在文档中。

      • 如果元素仍然在文档中,那么说明它是一次移动操作,组件实例将被保留;

      • 如果该元素不再存在于文档中,那么说明这是一次移除操作,组件实例将被销毁。

    Props

    • 所有在 props 选项中声明的 props 都会被定义为自定义元素的属性。Vue 能够自动地适当处理它是否反射为属性或属性。

    • attribute 总是根据需要反射为相应的属性类型。原话已经非常清晰明了,可以直接重述。重新表述如下: 基本数据类型的属性值(例如字符串、布尔值或数字)可以通过反射反映为属性。

    • 当它们被设为 attribute 时 (永远是字符串),Vue 也会自动将以 Boolean 或 Number 类型声明的 prop 转换为所期望的类型。比如下面这样的 props 声明:

      eMart 网店系统
      eMart 网店系统

      功能列表:底层程序与前台页面分离的效果,对页面的修改无需改动任何程序代码。完善的标签系统,支持自定义标签,公用标签,快捷标签,动态标签,静态标签等等,支持标签内的vbs语法,原则上运用这些标签可以制作出任何想要的页面效果。兼容原来的栏目系统,可以很方便的插入一个栏目或者一个栏目组到页面的任何位置。底层模版解析程序具有非常高的效率,稳定性和容错性,即使模版中有错误的标签也不会影响页面的显示。所有的标

      下载
    props: {
      selected: Boolean,
      index: Number
    }

    并以下面这样的方式使用自定义元素:

    <my-element selected index="1"></my-element>

    在组件中,selected 会被转换为 true (boolean 类型值) 而 index 会被转换为 1 (number 类型值)。

    事件

    • emit 触发的事件都会通过以 CustomEvents 的形式从自定义元素上派发。

    • 额外的事件参数 (payload) 将会被暴露为 CustomEvent 对象上的一个 detail 数组。

    插槽

    • 在一个组件中,插槽将会照常使用 渲染。但是,使用最终元素时,只能使用原生插槽的语法,无法使用作用域插槽。

    • 当传递具名插槽时,应使用 slot attribute 而不是 v-slot 指令:

    <my-element>
      <div slot="named">hello</div>
    </my-element>

    依赖注入

    • Provide / Inject API 和相应的组合式 API 在 Vue 定义的自定义元素中都可以正常工作。

    • 但是,依赖关系只在自定义元素之间起作用。一个由常规 Vue 组件提供的属性无法被注入到由 Vue 定义的自定义元素中。

    将 SFC 编译为自定义元素

    defineCustomElement 也可以搭配 Vue 单文件组件 (SFC) 使用。但是,根据默认的工具链配置,SFC 中的 <style></style> 在生产环境构建时仍然会被抽取和合并到一个单独的 CSS 文件中。当正在使用 SFC 编写自定义元素时,通常需要改为注入 <style></style> 标签到自定义元素的 ShadowRoot 上。

    官方的 SFC 工具链支持以“自定义元素模式”导入 SFC (需要 @vitejs/plugin-vue@^1.4.0 或 vue-loader@^16.5.0)。一个以自定义元素模式加载的 SFC 将会内联其 <style></style> 标签为 CSS 字符串,并将其暴露为组件的 styles 选项。这会被 defineCustomElement 提取使用,并在初始化时注入到元素的 ShadowRoot 上。

    要开启这个模式,将组件文件以 .ce.vue 结尾即可:

    // 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)
    // 转换为自定义元素构造器
    const ExampleElement = defineCustomElement(Example)
    // 注册
    customElements.define('my-example', ExampleElement)

    基于 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)
    }

    defineComponent()

    用来在定义 Vue 组件时为 TypeScript 提供类型推导的辅助函数。

    • 对于一个 ts 文件,如果我们直接写 export default {},无法有针对性的提示 vue 组件里应该有哪些属性。

    • 但是,增加一层 defineComponet 的话,export default defineComponent({}),就可以对参数进行一些类型推导和属性的提示等操作。

    function defineComponent(
      component: ComponentOptions | ComponentOptions['setup']
    ): ComponentConstructor

    参数是一个组件选项对象。返回值将是该选项对象本身,因为该函数实际上在运行时没有任何操作,仅用于提供类型推导,注意返回值的类型有一点特别:它是一个构造函数类型,它是根据选项推断出的组件实例类型。这样做可以在 TSX 中将返回值用作标签时提供类型推断支持。

    你可以像这样从 defineComponent() 的返回类型中提取出一个组件的实例类型 (与其选项中的 this 的类型等价):

    const Foo = defineComponent(/* ... */)
    type FooInstance = InstanceType<typeof Foo>

    defineAsyncComponent()

    用来定义一个异步组件。在大型项目中,我们可能会将应用拆分成更小的模块,并在需要时从服务器加载相关组件。defineAsyncComponent 在运行时是懒加载的,参数可以是一个返回 Promise 的异步加载函数(resolve 回调方法应该在从服务器获得组件定义时调用),或是对加载行为进行更具体定制的一个选项对象。

    import { defineAsyncComponent } from 'vue'
    const AsyncComp = defineAsyncComponent(() => {
      return new Promise((resolve, reject) => {
        // ...从服务器获取组件
        resolve(/* 获取到的组件 */)
      })
    })
    // ... 像使用其他一般组件一样使用 `AsyncComp`
    // 也可以使用 ES 模块动态导入
    const AsyncComp = defineAsyncComponent(() =>
      import('./components/MyComponent.vue')
    )

    得到的 AsyncComp 是一个外层包装过的组件,仅在页面需要它渲染时才会调用加载内部实际组件的函数。它会将接收到的 props 和插槽传给内部组件,所以你可以使用这个异步的包装组件无缝地替换原始组件,同时实现延迟加载。

    与普通组件一样,异步组件可以使用 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>

    异步操作不可避免地会涉及到加载和错误状态,因此 defineAsyncComponent() 也支持在高级选项中处理这些状态:

    const AsyncComp = defineAsyncComponent({
      // 加载函数
      loader: () => import('./Foo.vue'),
      // 加载异步组件时使用的组件
      loadingComponent: LoadingComponent,
      // 展示加载组件前的延迟时间,默认为 200ms
      delay: 200,
      // 加载失败后展示的组件
      errorComponent: ErrorComponent,
      // 如果提供了一个时间限制,并超时了,也会显示这里配置的报错组件,默认值是:Infinity
      timeout: 3000
    })

    热门AI工具

    更多
    DeepSeek
    DeepSeek

    幻方量化公司旗下的开源大模型平台

    豆包大模型
    豆包大模型

    字节跳动自主研发的一系列大型语言模型

    通义千问
    通义千问

    阿里巴巴推出的全能AI助手

    腾讯元宝
    腾讯元宝

    腾讯混元平台推出的AI助手

    文心一言
    文心一言

    文心一言是百度开发的AI聊天机器人,通过对话可以生成各种形式的内容。

    讯飞写作
    讯飞写作

    基于讯飞星火大模型的AI写作工具,可以快速生成新闻稿件、品宣文案、工作总结、心得体会等各种文文稿

    即梦AI
    即梦AI

    一站式AI创作平台,免费AI图片和视频生成。

    ChatGPT
    ChatGPT

    最最强大的AI聊天机器人程序,ChatGPT不单是聊天机器人,还能进行撰写邮件、视频脚本、文案、翻译、代码等任务。

    相关专题

    更多
    JavaScript浏览器渲染机制与前端性能优化实践
    JavaScript浏览器渲染机制与前端性能优化实践

    本专题围绕 JavaScript 在浏览器中的执行与渲染机制展开,系统讲解 DOM 构建、CSSOM 解析、重排与重绘原理,以及关键渲染路径优化方法。内容涵盖事件循环机制、异步任务调度、资源加载优化、代码拆分与懒加载等性能优化策略。通过真实前端项目案例,帮助开发者理解浏览器底层工作原理,并掌握提升网页加载速度与交互体验的实用技巧。

    23

    2026.03.06

    Rust内存安全机制与所有权模型深度实践
    Rust内存安全机制与所有权模型深度实践

    本专题围绕 Rust 语言核心特性展开,深入讲解所有权机制、借用规则、生命周期管理以及智能指针等关键概念。通过系统级开发案例,分析内存安全保障原理与零成本抽象优势,并结合并发场景讲解 Send 与 Sync 特性实现机制。帮助开发者真正理解 Rust 的设计哲学,掌握在高性能与安全性并重场景中的工程实践能力。

    68

    2026.03.05

    PHP高性能API设计与Laravel服务架构实践
    PHP高性能API设计与Laravel服务架构实践

    本专题围绕 PHP 在现代 Web 后端开发中的高性能实践展开,重点讲解基于 Laravel 框架构建可扩展 API 服务的核心方法。内容涵盖路由与中间件机制、服务容器与依赖注入、接口版本管理、缓存策略设计以及队列异步处理方案。同时结合高并发场景,深入分析性能瓶颈定位与优化思路,帮助开发者构建稳定、高效、易维护的 PHP 后端服务体系。

    162

    2026.03.04

    AI安装教程大全
    AI安装教程大全

    2026最全AI工具安装教程专题:包含各版本AI绘图、AI视频、智能办公软件的本地化部署手册。全篇零基础友好,附带最新模型下载地址、一键安装脚本及常见报错修复方案。每日更新,收藏这一篇就够了,让AI安装不再报错!

    84

    2026.03.04

    Swift iOS架构设计与MVVM模式实战
    Swift iOS架构设计与MVVM模式实战

    本专题聚焦 Swift 在 iOS 应用架构设计中的实践,系统讲解 MVVM 模式的核心思想、数据绑定机制、模块拆分策略以及组件化开发方法。内容涵盖网络层封装、状态管理、依赖注入与性能优化技巧。通过完整项目案例,帮助开发者构建结构清晰、可维护性强的 iOS 应用架构体系。

    113

    2026.03.03

    C++高性能网络编程与Reactor模型实践
    C++高性能网络编程与Reactor模型实践

    本专题围绕 C++ 在高性能网络服务开发中的应用展开,深入讲解 Socket 编程、多路复用机制、Reactor 模型设计原理以及线程池协作策略。内容涵盖 epoll 实现机制、内存管理优化、连接管理策略与高并发场景下的性能调优方法。通过构建高并发网络服务器实战案例,帮助开发者掌握 C++ 在底层系统与网络通信领域的核心技术。

    29

    2026.03.03

    Golang 测试体系与代码质量保障:工程级可靠性建设
    Golang 测试体系与代码质量保障:工程级可靠性建设

    Go语言测试体系与代码质量保障聚焦于构建工程级可靠性系统。本专题深入解析Go的测试工具链(如go test)、单元测试、集成测试及端到端测试实践,结合代码覆盖率分析、静态代码扫描(如go vet)和动态分析工具,建立全链路质量监控机制。通过自动化测试框架、持续集成(CI)流水线配置及代码审查规范,实现测试用例管理、缺陷追踪与质量门禁控制,确保代码健壮性与可维护性,为高可靠性工程系统提供质量保障。

    79

    2026.02.28

    Golang 工程化架构设计:可维护与可演进系统构建
    Golang 工程化架构设计:可维护与可演进系统构建

    Go语言工程化架构设计专注于构建高可维护性、可演进的企业级系统。本专题深入探讨Go项目的目录结构设计、模块划分、依赖管理等核心架构原则,涵盖微服务架构、领域驱动设计(DDD)在Go中的实践应用。通过实战案例解析接口抽象、错误处理、配置管理、日志监控等关键工程化技术,帮助开发者掌握构建稳定、可扩展Go应用的最佳实践方法。

    62

    2026.02.28

    Golang 性能分析与运行时机制:构建高性能程序
    Golang 性能分析与运行时机制:构建高性能程序

    Go语言以其高效的并发模型和优异的性能表现广泛应用于高并发、高性能场景。其运行时机制包括 Goroutine 调度、内存管理、垃圾回收等方面,深入理解这些机制有助于编写更高效稳定的程序。本专题将系统讲解 Golang 的性能分析工具使用、常见性能瓶颈定位及优化策略,并结合实际案例剖析 Go 程序的运行时行为,帮助开发者掌握构建高性能应用的关键技能。

    51

    2026.02.28

    热门下载

    更多
    网站特效
    /
    网站源码
    /
    网站素材
    /
    前端模板

    精品课程

    更多
    相关推荐
    /
    热门推荐
    /
    最新课程
    Vue3.x 工具篇--十天技能课堂
    Vue3.x 工具篇--十天技能课堂

    共26课时 | 1.6万人学习

    Vue3.x 核心篇--十天技能课堂
    Vue3.x 核心篇--十天技能课堂

    共30课时 | 1.6万人学习

    Vue3.x新特性篇--十天基础课堂
    Vue3.x新特性篇--十天基础课堂

    共20课时 | 1.2万人学习

    关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
    php中文网:公益在线php培训,帮助PHP学习者快速成长!
    关注服务号 技术交流群
    PHP中文网订阅号
    每天精选资源文章推送

    Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号