最近開了一個讀者回饋表單郵箱,無論是對文章的感想或是對部落格的感想,有什麼想回饋的都可以發郵箱跟我說:i_kkkp@163.com

Vue Components 和 Web 组件Custom Elements

前言

Web 组件是一组 Web 原生 API 的总称,允许开发人员创建可重用的自定义元素。

Vue 和 Web Components 主要是互补技术。无论是将自定义元素集成到现有的 Vue 应用程序中,还是使用 Vue 构建和分发自定义元素,Vue 对使用和创建自定义元素都有出色的支持。

什么是Custom Elements

Web 组件的一个关键特性是创建自定义元素:即由 Web 开发人员定义行为的 HTML 元素,扩展了浏览器中可用的元素集。

Custom Elements有两种类型的自定义元素:

  • 自定义内置元素(Customized built-in element) 继承自标准的 HTML 元素,例如 HTMLImageElement 或 HTMLParagraphElement。它们的实现定义了标准元素的行为。
  • 独立自定义元素(Autonomous custom element) 继承自 HTML 元素基类 HTMLElement。你必须从头开始实现它们的行为。

自定义元素生命周期回调

当然Custom Elements也有自定义元素生命周期回调

一旦你的自定义元素被注册,当页面中的代码以特定方式与你的自定义元素交互时,浏览器将调用你的类的某些方法。 通过提供这些方法的实现,规范称之为生命周期回调,你可以运行代码来响应这些事件。

自定义元素生命周期回调包括:

  • connectedCallback():每当元素添加到文档中时调用。规范建议开发人员尽可能在此回调中实现自定义元素的设定,而不是在构造函数中实现。
  • disconnectedCallback():每当元素从文档中移除时调用。
  • adoptedCallback():每当元素被移动到新文档中时调用。
  • attributeChangedCallback():在属性更改、添加、移除或替换时调用。有关此回调的更多详细信息,请参见响应属性变化。

以下是一个记录这些生命周期事件的最小自定义元素示例:

// 为这个元素创建类
class MyCustomElement extends HTMLElement {
  static observedAttributes = ["color", "size"];

  constructor() {
    // 必须首先调用 super 方法
    super();
  }

  connectedCallback() {
    console.log("自定义元素添加至页面。");
  }

  disconnectedCallback() {
    console.log("自定义元素从页面中移除。");
  }

  adoptedCallback() {
    console.log("自定义元素移动至新页面。");
  }

  attributeChangedCallback(name, oldValue, newValue) {
    console.log(`属性 ${name} 已变更。`);
  }
}

customElements.define("my-custom-element", MyCustomElement);

在 Vue 中使用自定义元素​

在 Vue 应用程序中使用自定义元素在很大程度上与使用原生 HTML 元素相同,但需要记住以下几点:

跳过组件解析​

默认情况下,Vue 会尝试将非原生 HTML 标签解析为已注册的 Vue 组件,然后再将其渲染为自定义元素。 这将导致 Vue 在开发过程中发出“无法解析组件”警告。为了让 Vue 知道某些元素应该被视为自定义元素并跳过组件解析,我们可以指定compilerOptions.isCustomElement选项。

如果您使用 Vue 进行构建设置,则该选项应通过构建配置传递,因为它是编译时选项。

浏览器内配置示例:

// Only works if using in-browser compilation.
// If using build tools, see config examples below.
app.config.compilerOptions.isCustomElement = (tag) => tag.includes('-')

Vite 配置示例​

// vite.config.js
import vue from '@vitejs/plugin-vue'

export default {
  plugins: [
    vue({
      template: {
        compilerOptions: {
          // treat all tags with a dash as custom elements
          isCustomElement: (tag) => tag.includes('-')
        }
      }
    })
  ]
}

Vue CLI 配置示例​

// vue.config.js
module.exports = {
  chainWebpack: config => {
    config.module
      .rule('vue')
      .use('vue-loader')
      .tap(options => ({
        ...options,
        compilerOptions: {
          // treat any tag that starts with ion- as custom elements
          isCustomElement: tag => tag.startsWith('ion-')
        }
      }))
  }
}

使用 Vue 构建自定义元素​

自定义元素的主要好处是它们可以与任何框架一起使用,甚至可以在没有框架的情况下使用。这使得它们非常适合分发最终消费者可能不使用相同前端堆栈的组件,或者当您希望将最终应用程序与其使用的组件的实现细节隔离时。

定义自定义元素​
Vue 支持通过该方法使用完全相同的 Vue 组件 API 创建自定义元素defineCustomElement。该方法接受与 相同的参数defineComponent,但返回一个扩展的自定义元素构造函数HTMLElement:

模板

<my-vue-element></my-vue-element>
import { defineCustomElement } from 'vue'

const MyVueElement = defineCustomElement({
  // normal Vue component options here
  props: {},
  emits: {},
  template: `...`,

  // defineCustomElement only: CSS to be injected into shadow root
  styles: [`/* inlined css */`]
})

// Register the custom element.
// After registration, all `<my-vue-element>` tags
// on the page will be upgraded.
customElements.define('my-vue-element', MyVueElement)

// You can also programmatically instantiate the element:
// (can only be done after registration)
document.body.appendChild(
  new MyVueElement({
    // initial props (optional)
  })
)

当我们谈论自定义元素和Vue组件时,实际上在讨论构建网页应用程序时使用的两种不同方式。自定义元素是一种Web标准,就像HTML元素一样,而Vue组件是Vue.js框架提供的一种更高级的构建方式。

有人认为只使用自定义元素是更“未来”的方式,但这段文字指出这种看法过于简单。它列举了一些原因,说明为什么Vue组件模型更为实用。其中一些关键点包括:

Vue组件提供了更多功能,如方便的模板系统、管理状态的方法,以及在服务器上渲染组件的高效方式。这些功能对于构建复杂的应用程序是必要的。

Vue组件支持强大的组合机制,而自定义元素在这方面有一些局限。这意味着使用Vue,你更容易构建灵活而强大的组件结构。使用Vue,你能够借助一个成熟的框架和庞大的社区,而不必自己构建和维护一套内部框架。

自定义元素和 Vue 组件之间确实存在一定程度的功能重叠:它们都允许我们定义具有数据传递、事件发出和生命周期管理的可重用组件。然而,Web 组件 API 的级别相对较低且简单。要构建实际的应用程序,我们需要一些该平台未涵盖的附加功能:

  • 声明性且高效的模板系统;

  • 反应式状态管理系统,有利于跨组件逻辑提取和重用;

  • 一种在服务器上渲染组件并在客户端 (SSR) 上进行组合的高性能方法,这对于 SEO 和LCP 等 Web Vitals 指标非常重要。原生自定义元素 SSR 通常涉及在 Node.js 中模拟 DOM,然后序列化变异的 DOM,而 Vue SSR 会尽可能编译为字符串连接,这更加高效。

defineCustomElement API Vue 组件转化

使用 defineCustomElement API 将 Vue 组件转化为可以注册的自定义元素类有一些好处:

  1. 跨框架集成: 通过将 Vue 组件转化为自定义元素类,你可以在不同的前端框架和库中使用这个组件。这种方式使得你的组件更具通用性,可以与其他技术栈集成。

  2. 独立使用: 将 Vue 组件注册为自定义元素后,它可以独立于 Vue 应用使用。这意味着你可以在没有整个 Vue 应用的情况下使用该组件,以及在不同的构建系统和模块系统中引入它。

  3. 逐步迁移: 如果你的应用是逐步迁移到 Vue 的,你可以通过将某些组件转化为自定义元素来实现渐进式迁移。这使得你可以逐步地将 Vue 组件引入到一个已经存在的项目中,而无需一次性重写整个应用。

  4. Web Components 标准兼容性: 将 Vue 组件注册为自定义元素使其与 Web Components 标准兼容。这意味着你可以利用 Web Components 生态系统的其他工具和库,使你的组件更具互操作性。

也就是说defineCustomElement API 的作用是将 Vue 组件编译为可以在浏览器中使用的自定义元素(Custom Element)。这意味着你不需要依赖 Vue 编译器在浏览器端实时编译 Vue 组件。

在使用 defineCustomElement API 时,Vue 组件会被提前编译成原生的自定义元素,这样就可以在浏览器中直接使用,而无需在运行时进行编译。

总体而言,通过使用 defineCustomElement API,你可以将 Vue 组件与自定义元素相结合,从而在更广泛的上下文中使用和共享这些组件,提高了组件的可复用性和灵活性。这在跨端组件开发集成上有很大的好处,你大可以先将组件开发成自定义元素,然后再在不同的端中使用。一个很典型的例子是我们之前提到的vue2和vue3的集成实现,这意味着你只需要将vue3的组件编译成自定义元素,然后在vue2中使用即可。

浏览器事件的冒泡和捕获 Vue 2/3 共存开发的思路

評論