>  기사  >  웹 프론트엔드  >  DefineCustomElement를 사용하여 Vue3에서 구성 요소를 정의하는 방법

DefineCustomElement를 사용하여 Vue3에서 구성 요소를 정의하는 방법

PHPz
PHPz앞으로
2023-05-28 11:29:522988검색

    Vue를 사용하여 사용자 정의 요소 구축

    웹 구성 요소는 개발자가 재사용 가능한 사용자 정의 요소를 만들 수 있는 웹 네이티브 API 집합의 총칭입니다.

    맞춤 요소 사용의 가장 큰 장점은 모든 프레임 또는 프레임이 아닌 환경에서도 사용할 수 있다는 것입니다. 다른 프런트 엔드 기술 스택을 사용하는 최종 사용자를 대상으로 하거나 사용하는 구성 요소의 구현 세부 사항에서 최종 애플리케이션을 분리하려는 경우에 이상적입니다.

    Vue와 웹 구성 요소는 상호 보완적인 기술이며 Vue는 사용자 정의 요소를 사용하고 생성하는 데 탁월한 지원을 제공합니다. Vue 애플리케이션에서는 사용자 정의 요소를 통합할 수 있으며 Vue를 사용하여 사용자 정의 요소를 구축하고 게시할 수도 있습니다.

    Vue는 Custom Elements Everywhere 테스트에서 100% 점수를 획득했습니다. Vue 애플리케이션에서 사용자 정의 요소를 사용하는 것은 기본적으로 기본 HTML 요소를 사용하는 것과 동일한 효과가 있지만 작동하려면 몇 가지 추가 구성이 필요합니다.

    구성 요소 구문 분석 건너뛰기

    기본적으로 Vue는 기본이 아닌 HTML 태그를 모두 제거합니다. Vue로 취급 구성 요소를 먼저 사용하고 "사용자 정의 요소 렌더링"을 대체 옵션으로 남겨 둡니다. 이로 인해 Vue는 개발 중에 "구성 요소 구문 분석 실패" 경고를 발생시킵니다.

    • 특정 요소가 사용자 정의 요소로 처리되어야 함을 Vue에 알리고 구성 요소 구문 분석을 건너뛰려면 컴파일러Options.isCustomElement 옵션을 지정할 수 있습니다. 이 옵션 객체에 설정된 값은 브라우저에서 템플릿 컴파일 중에 사용됩니다. 구성된 애플리케이션의 모든 구성 요소에 사용되고 영향을 줍니다.

    • 또는 컴파일러 옵션 옵션을 통해 구성 요소별로 이러한 옵션을 재정의할 수 있습니다(현재 구성 요소의 우선 순위가 더 높음).

    컴파일 타임 옵션이기 때문에 빌드 도구는 @vue/compiler-dom에 구성을 전달해야 합니다.

    • vue-loader:compileOptions 로더의 옵션을 통해 전달됩니다.

    // 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 속성은 문자열 값만 가능하므로 DOM 객체의 속성만 사용하여 복잡한 데이터를 전달할 수 있습니다. 사용자 정의 요소에 대한 소품을 설정할 때 Vue 3는 in 연산자를 통해 DOM 객체에 속성이 이미 존재하는지 자동으로 확인하고 키가 존재하면 값을 속성으로 설정하는 것을 선호합니다. DOM 객체의 이는 대부분의 경우 맞춤 요소가 권장 모범 사례를 따르는 경우 이 문제에 대해 생각할 필요가 없음을 의미합니다. in 操作符自动检查该属性是否已经存在于 DOM 对象上,并且在这个 key 存在时,更倾向于将值设置为一个 DOM 对象的属性。这意味着,在大多数情况下,如果自定义元素遵循推荐的最佳实践,你就不需要考虑这个问题。

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

    그러나 몇 가지 특별한 경우가 있습니다. 데이터는 DOM 객체 속성으로 전달되어야 하지만 맞춤 요소는 이 속성을 올바르게 정의/반영할 수 없습니다(in 검사가 실패했기 때문에). 이 경우 v-bind 바인딩을 강제로 사용하고 .prop 수정자를 통해 DOM 객체의 속성을 설정할 수 있습니다:

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

    defineCustomElement()

    Vue는 사용자 정의 요소 생성을 지원하기 위해 일반 Vue 구성 요소를 정의하는 것과 거의 동일한 DefineCustomElement 메소드를 제공합니다. DefineComponent는 이 메소드에서 수신한 매개변수와 정확히 동일합니다. 그러나 HTMLElement(customElements.define()를 통해 등록할 수 있음)에서 상속되는 기본 사용자 정의 요소 클래스에 대한 생성자를 반환합니다.

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

    definCustomElement()는 일반 구성 요소 옵션 외에도 인라인 CSS 문자열 배열인 특수 옵션 스타일도 지원합니다. 제공된 CSS는 요소의 ShadowRoot에 삽입됩니다.

    <my-vue-element></my-vue-element>
    import { defineCustomElement } from &#39;vue&#39;
    const MyVueElement = defineCustomElement({
      // 这里是同平常一样的 Vue 组件选项
      props: {},
      emits: {},
      template: `...`,
      // defineCustomElement 特有的:注入进 ShadowRoot 的 CSS
      styles: [`/* css */`]
    })
    // 注册自定义元素之后,所有此页面中的 `<my-vue-element>` 标签都会被升级
    customElements.define(&#39;my-vue-element&#39;, MyVueElement)
    // 也可以在注册之后实例化元素:
    document.body.appendChild(
      new MyVueElement({
        // 初始化 props(可选)
      })
    )

    지금 콘솔이 오류를 보고하는 경우: color{red}{지금 콘솔이 오류를 보고하는 경우:} 콘솔이 이때 오류를 보고하는 경우: 구성 요소 제공 템플릿 옵션이 있지만 런타임 컴파일은 지원되지 않습니다. vite.config.js 구성에 다음을 추가합니다:

    resolve: { alias: { &#39;vue&#39;: &#39;vue/dist/vue.esm-bundler.js&#39; } },

    Lifecycle
    • 요소의 ConnectedCallback이 처음 호출되면 Vue 사용자 정의 요소는 내부적으로 Vue 구성 요소 인스턴스를 ShadowRoot에 마운트합니다.
    • 이 요소의 connectedCallback이 호출되면 Vue는 마이크로태스크 후에 해당 요소가 아직 문서에 있는지 확인합니다.
      • 요소가 여전히 문서에 있으면 이동 작업이고 구성 요소 인스턴스가 유지됩니다.
      • 요소가 문서에 더 이상 존재하지 않으면 제거 작업이며 구성 요소 인스턴스가 삭제됩니다.

    Props
    • props 옵션에 선언된 모든 props는 custom 요소의 속성으로 정의됩니다. Vue는 속성으로 반영되는지, 속성으로 반영되는지 자동으로 적절하게 처리할 수 있습니다. 🎜
    • attribute 总是根据需要反射为相应的属性类型。原话已经非常清晰明了,可以直接重述。重新表述如下: 基本数据类型的属性值(例如字符串、布尔值或数字)可以通过反射反映为属性。

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

    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 中的 c9ccee2e6ea535a969eb3f532ad9fe89 在生产环境构建时仍然会被抽取和合并到一个单独的 CSS 文件中。当正在使用 SFC 编写自定义元素时,通常需要改为注入 c9ccee2e6ea535a969eb3f532ad9fe89 标签到自定义元素的 ShadowRoot 上。

    官方的 SFC 工具链支持以“自定义元素模式”导入 SFC (需要 @vitejs/plugin-vue@^1.4.0 或 vue-loader@^16.5.0)。一个以自定义元素模式加载的 SFC 将会内联其 c9ccee2e6ea535a969eb3f532ad9fe89 标签为 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 &#39;vue&#39;
    import Example from &#39;./Example.ce.vue&#39;
    console.log(Example.styles)
    // 转换为自定义元素构造器
    const ExampleElement = defineCustomElement(Example)
    // 注册
    customElements.define(&#39;my-example&#39;, ExampleElement)

    基于 Vue 构建自定义元素库

    按元素分别导出构造函数,以便用户可以灵活地按需导入它们,还可以通过导出一个函数来方便用户自动注册所有元素。

    // Vue 自定义元素库的入口文件
    import { defineCustomElement } from &#39;vue&#39;
    import Foo from &#39;./MyFoo.ce.vue&#39;
    import Bar from &#39;./MyBar.ce.vue&#39;
    const MyFoo = defineCustomElement(Foo)
    const MyBar = defineCustomElement(Bar)
    // 分别导出元素
    export { MyFoo, MyBar }
    export function register() {
      customElements.define(&#39;my-foo&#39;, MyFoo)
      customElements.define(&#39;my-bar&#39;, MyBar)
    }

    defineComponent()

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

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

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

    function defineComponent(
      component: ComponentOptions | ComponentOptions[&#39;setup&#39;]
    ): ComponentConstructor

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

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

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

    defineAsyncComponent()

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

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

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

    与普通组件一样,异步组件可以使用 app.component() 全局注册:

    app.component(&#39;MyComponent&#39;, defineAsyncComponent(() =>
      import(&#39;./components/MyComponent.vue&#39;)
    ))

    也可以直接在父组件中直接定义它们:

    <script setup>
    import { defineAsyncComponent } from &#39;vue&#39;
    const AdminPage = defineAsyncComponent(() =>
      import(&#39;./components/AdminPageComponent.vue&#39;)
    )
    </script>
    <template>
      <AdminPage />
    </template>

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

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

    위 내용은 DefineCustomElement를 사용하여 Vue3에서 구성 요소를 정의하는 방법의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

    성명:
    이 기사는 yisu.com에서 복제됩니다. 침해가 있는 경우 admin@php.cn으로 문의하시기 바랍니다. 삭제