ホームページ >ウェブフロントエンド >Vue.js >Vue3 コンポジション API がサードパーティ コンポーネントをエレガントにカプセル化する方法

Vue3 コンポジション API がサードパーティ コンポーネントをエレガントにカプセル化する方法

WBOY
WBOY転載
2023-05-11 19:13:091618ブラウズ

はじめに

サードパーティ コンポーネントの場合、サードパーティ コンポーネントの元の機能 (プロパティ、イベント、スロット、メソッド) を維持しながら機能をエレガントに拡張するにはどうすればよいでしょうか?

Element Plus の el-input を例に挙げます:

おそらく、MyInput コンポーネントをカプセル化し、使用するプロップ、イベント、スロットを配置するという、このようにプレイしたことがあるでしょう。

// MyInput.vue
<template>
  <div class="my-input">
    <el-input v-model="inputVal" :clearable="clearable" @clear="clear">
    <template #prefix>
      <slot name="prefix"></slot>
    </template>
      <template #suffix>
      <slot name="suffix"></slot>
    </template>
    </el-input>
  </div>
</template>
<script setup>
import { computed } from &#39;vue&#39;
const props = defineProps({
  modelValue: {
    type: String,
    default: &#39;&#39;
  },
  clearable: {
    type: Boolean,
    default: false
  }
})
const emits = defineEmits([&#39;update:modelValue&#39;, &#39;clear&#39;])
const inputVal = computed({
  get: () => props.modelValue,
  set: (val) => {
    emits(&#39;update:modelValue&#39;, val)
  }
})
const clear = () => {
  emits(&#39;clear&#39;)
}
</script>

しかし、しばらくすると要件が変わり、el-input コンポーネントの他の関数を MyInput コンポーネントに追加する必要があります。合計20個 属性が5つ、イベントが5つ、スロットが4つあるのですが、どうすればいいでしょうか? 一つずつ渡すべきでしょうか? これでは面倒なだけでなく、可読性も悪くなります。

Vue2 では、このように処理できます。Vue サードパーティ コンポーネントのカプセル化を表示するには、ここをクリックしてください。

この記事は、知識を移行し、Vue3 CombopositionAPI を使用して、エレガントにカプセル化するサードパーティ コンポーネント~

1.サードパーティ コンポーネントのプロパティ props とイベントの場合

Vue2

  • $attrs:親ロール 小道具として認識 (および取得) されない、ドメイン内の属性バインディング (クラスとスタイルを除く)。コンポーネントが props を宣言していない場合、すべての親スコープ バインディング (クラスとスタイルを除く) がここに含まれ、内部コンポーネントは v-bind="$attrs"

  • # を介して渡すことができます。 ##$listeners: 親スコープ内の v-on イベント リスナーが含まれます (.native 修飾子を除く)。 v-on="$listeners" を渡して内部コンポーネント

と Vue3

  • $attrs に渡すことができます: 親ロールが含まれますドメイン内のコンポーネント プロパティまたはカスタム イベントとして使用されない属性バインディングおよびイベント (クラス、スタイル、カスタム イベントを含む) も、v-bind="$attrs" を通じて内部コンポーネントに渡すことができます。

  • $listeners オブジェクトは Vue 3 で削除されました。イベント リスナーは $attrs の一部になりました。

  • 5101c0cdbdc49998c642c71f6b6410a8 の補助関数 useAttrs で $attrs を取得できます。

  • //MyInput.vue 
    <template>
      <div class="my-input">
        <el-input v-bind="attrs"></el-input>
      </div>
    </template>
    <script setup>
    import { useAttrs } from &#39;vue&#39;
    const attrs = useAttrs()
    </script>
もちろん、これだけでは十分ではありません。このように書くだけで、バインドした属性 (class や style を含む) はルート要素にも反映されます (上の例は class="my-input" の Dom ノードです)。このデフォルトの動作を防ぐには、inheritAttrs を false に設定する必要があります。

Vue3 ドキュメントの継承Attrs の説明を見てみましょう

デフォルトでは、props として認識されない親スコープ内の属性バインディングは「ロールバック」され、ルート要素に適用されます。子コンポーネントの通常の HTML 属性として。ターゲット要素または別のコンポーネントをラップするコンポーネントを作成する場合、予期される動作に必ずしも準拠するとは限りません。 InheritAttrs を false に設定すると、これらのデフォルトの動作が削除されます。これらの属性は、インスタンス プロパティ $attrs を通じて有効にすることができ、v-bind を通じて非ルート要素に明示的にバインドできます。

したがって、サードパーティ コンポーネントのプロパティ props とイベントを処理する次のコードを作成できます。

// MyInput.vue 
<template>
  <div class="my-input">
    <el-input v-bind="attrs"></el-input>
  </div>
</template>
<script>
export default {
  name: &#39;MyInput&#39;,
  inheritAttrs: false
}
</script>
<script setup>
import { useAttrs } from &#39;vue&#39;
const attrs = useAttrs()
</script>

2. サードパーティ コンポーネントのスロットの場合

Vue3 Medium

  • #$slots: 親コンポーネントによって渡されたスロットを取得できます

  • $scopedSlots は Vue3 から削除されました。スロットは $slots を通じて関数として公開されます。

  • 5101c0cdbdc49998c642c71f6b6410a8 の補助関数 useSlots は $slots を取得できます。

上記の点に基づいて、サードパーティ コンポーネントのパッケージにスロットを追加せず、サードパーティ コンポーネントのスロットが同じ dom ノード内にある場合、賢いカプセル化方法もあり、$slots を走査してスロットの名前を取得し、サブコンポーネントのスロットを動的に追加します:

//MyInput.vue 
<template>
  <div class="my-input">
    <el-input v-bind="attrs">
      <template v-for="k in Object.keys(slots)" #[k] :key="k">
        <slot :name="k"></slot>
      </template>
    </el-input>
  </div>
</template>
<script>
export default {
  name: &#39;MyInput&#39;,
  inheritAttrs: false
}
</script>
<script setup>
import { useAttrs, useSlots } from &#39;vue&#39;
const attrs = useAttrs()
const slots = useSlots()
</script>

上記の条件が満たされない場合は、次のことを行う必要があります。必要なサードパーティ コンポーネント スロットをコンポーネントに手動で追加します~

3. サードパーティ コンポーネントのメソッド

サードパーティ コンポーネントのメソッドについては、ref を通じて実装します。 。まず、MyInput コンポーネントの el-input コンポーネントに ref="elInputRef" 属性を追加し、defineExpose を通じて elInputRef を親コンポーネントに公開します。

サブコンポーネント: MyInput.vue

// MyInput.vue 
<template>
  <div class="my-input">
    <el-input v-bind="attrs" ref="elInputRef">
      <template v-for="k in Object.keys(slots)" #[k] :key="k">
        <slot :name="k"></slot>
      </template>
    </el-input>
  </div>
</template>
<script>
export default {
  name: &#39;MyInput&#39;,
  inheritAttrs: false
}
</script>
<script setup>
import { useAttrs, useSlots } from &#39;vue&#39;
const attrs = useAttrs()
const slots = useSlots()
const elInputRef = ref(null)
defineExpose({
  elInputRef  // <script setup>的组件里的属性默认是关闭的,需通过defineExpose暴露出去才能被调用
})
</script>

親ページ: Index.vue の呼び出しコードは次のとおりです:

// Index.vue 
<template>
  <my-input v-model=&#39;input&#39; ref="myInput">
    <template #prefix>姓名</template>
  </my-input>
</template>
<script setup>
import MyInput from &#39;./components/MyInput.vue&#39;
import { ref, onMounted } from &#39;vue&#39;
const input = ref(&#39;&#39;)
const myInput = ref(null) // 组件实例
onMounted(()=> {
  myInput.value.elInputRef.focus() // 初始化时调用elInputRef实例的focus方法
})
</script>

以上がVue3 コンポジション API がサードパーティ コンポーネントをエレガントにカプセル化する方法の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事はyisu.comで複製されています。侵害がある場合は、admin@php.cn までご連絡ください。