DefineCustomElement を使用して Vue3 でコンポーネントを定義する方法

PHPz
リリース: 2023-05-28 11:29:52
転載
3008 人が閲覧しました

    Vue を使用してカスタム要素を構築する

    Web コンポーネントは、開発者が再利用可能なカスタム要素 (カスタム要素) を作成できるようにする一連の Web ネイティブ API の総称です。要素)。

    カスタム要素を使用する主な利点は、任意のフレーム環境または非フレーム環境でも使用できることです。これらは、異なるフロントエンド テクノロジ スタックを使用している可能性のあるエンド ユーザーをターゲットにする場合、または最終アプリケーションを使用するコンポーネントの実装の詳細から切り離したい場合に最適です。

    Vue と Web コンポーネントは補完的なテクノロジであり、Vue はカスタム要素の使用と作成に対する優れたサポートを提供します。 Vue アプリケーションでは、カスタム要素を統合でき、Vue を使用してカスタム要素を構築および公開することもできます。

    Vue は、Custom Elements Everywhere テストで 100% のスコアを達成しました。 Vue アプリケーションでカスタム要素を使用すると、基本的にネイティブ HTML 要素を使用するのと同じ効果がありますが、機能するには追加の設定が必要です。

    コンポーネントの解析をスキップ

    デフォルトでは、Vue 非ネイティブ HTMLタグは最初に Vue コンポーネントとして扱われ、フォールバック オプションとして「カスタム要素のレンダリング」が使用されます。これにより、Vue は開発中に「コンポーネントの解析に失敗しました」という警告をスローします。

    • 特定の要素をカスタム要素として扱い、コンポーネントの解析をスキップする必要があることを Vue に知らせるには、compilerOptions.isCustomElement オプションを指定できます。このオプション オブジェクトに設定される値は、これはブラウザでのテンプレートのコンパイル中に使用され、構成されたアプリケーションのすべてのコンポーネントに影響します。

    • または、これらのオプションは、compilerOptions オプションを使用してコンポーネントごとにオーバーライドできます (現在のコンポーネントの優先順位が高くなります)。

    これはコンパイル時オプションであるため、ビルド ツールは構成を @vue/compiler-dom に渡す必要があります:

    • vue -loader:compilerOptions ローダーを介して渡されるオプション。

    // 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 オブジェクトの属性のみを使用できます。カスタム要素の props を設定するとき、Vue 3 は in 演算子を通じてプロパティが DOM オブジェクトにすでに存在するかどうかを自動的にチェックし、キーが存在する場合は値を DOM に設定する可能性が高くなります。オブジェクトのプロパティ。つまり、カスタム要素が推奨されるベスト プラクティスに従っている場合、ほとんどの場合、この問題について考える必要はありません。

    ただし、いくつかの特殊なケースもあります。データは 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
    }
    ログイン後にコピー

    通常のコンポーネント オプションに加えて、defineCustomElement() はインライン 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; } },
    ログイン後にコピー

    ライフ サイクル

    • 要素のconnectedCallbackが最初に呼び出されるとき、Vue カスタム要素Vue コンポーネントの内部インスタンスを ShadowRoot にマウントします。

    • この要素の ConnectedCallback が呼び出されると、Vue はマイクロタスクの後に要素がまだドキュメント内にあるかどうかを確認します。

      • 要素がまだドキュメント内にある場合、それは移動操作となり、コンポーネント インスタンスは保持されます。

      • 要素がドキュメント内に存在しなくなった場合、これは削除操作となり、コンポーネント インスタンスは破棄されます。

    Props

    • props オプションで宣言されたすべての props は、カスタム要素のプロパティとして定義されます。プロパティとして反映されるかプロパティとして反映されるかは、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 中的 <style> 在生产环境构建时仍然会被抽取和合并到一个单独的 CSS 文件中。当正在使用 SFC 编写自定义元素时,通常需要改为注入 <style> 标签到自定义元素的 ShadowRoot 上。

    官方的 SFC 工具链支持以“自定义元素模式”导入 SFC (需要 @vitejs/plugin-vue@^1.4.0 或 vue-loader@^16.5.0)。一个以自定义元素模式加载的 SFC 将会内联其 <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 &#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 中国語 Web サイトの他の関連記事を参照してください。

    関連ラベル:
    ソース:yisu.com
    このウェブサイトの声明
    この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。
    最新の問題
    人気のチュートリアル
    詳細>
    最新のダウンロード
    詳細>
    ウェブエフェクト
    公式サイト
    サイト素材
    フロントエンドテンプレート