Home  >  Article  >  Web Front-end  >  How Vue3 Composition API elegantly encapsulates third-party components

How Vue3 Composition API elegantly encapsulates third-party components

WBOY
WBOYforward
2023-05-11 19:13:091580browse

Preface

For third-party components, how to elegantly extend the functions while maintaining the original functions of the third-party components (properties, events, slots, methods)?

Take Element Plus’ el-input as an example:

Most likely you have played like this before, encapsulating a MyInput component and putting the props, events and slots to be used . Write the methods again according to your own needs:

// 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>

But after a period of time, the requirements change, and other functions of the el-input component need to be added to the MyInput component. The el-input component has a total of 20 There are multiple attributes, 5 events, and 4 slots. What should we do? Should we pass them in one by one? This is not only cumbersome but also has poor readability.

In Vue2, we can handle it like this, click here to view the encapsulation of Vue third-party components

This article is to help you migrate knowledge and explore how to use Vue3 CompositionAPI to encapsulate elegantly Third-party components~

1. For the property props and events of third-party components

In Vue2

  • $attrs: includes the parent role Attribute bindings (except class and style) in the domain that are not recognized (and obtained) as props. When a component does not declare any props, all parent scope bindings (except class and style) will be included here, and internal components can be passed in via v-bind="$attrs"

  • $listeners: Contains v-on event listeners in the parent scope (excluding .native modifiers). It can pass v-on="$listeners" to pass in internal components

and in Vue3

  • $attrs: contains the parent role Attribute bindings and events (including class, style and custom events) that are not used as component props or custom events in the domain can also be passed into internal components through v-bind="$attrs".

  • $listeners object has been removed in Vue 3. Event listeners are now part of $attrs.

  • The auxiliary function useAttrs in 5101c0cdbdc49998c642c71f6b6410a8 can obtain $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>

Of course, this is not enough. Just writing like this, the attributes we bind (including class and style) will also take effect on the root element (the above example is the Dom node with class="my-input"). To prevent this default behavior, we need to set inheritAttrs to false.

Let’s take a look at the Vue3 document’s explanation of inheritAttrs

By default, attribute bindings in the parent scope that are not recognized as props will be “rolled back” and Applies to the root element of a child component as a normal HTML attribute. When writing a component that wraps a target element or another component, this may not always conform to the expected behavior. By setting inheritAttrs to false, these default behaviors will be removed. These attributes can be made effective through instance property $attrs, and can be explicitly bound to non-root elements through v-bind.

So, we can write the following code for processing the property props and events of third-party components:

// 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. For the slots of third-party components

Vue3 Medium

  • #$slots: We can get the slot passed in by the parent component

  • $scopedSlots has been removed from Vue3, all Slots are exposed as functions through $slots

  • The auxiliary function useSlots in 5101c0cdbdc49998c642c71f6b6410a8 can obtain $slots.

Based on the above points, if we do not add additional slots to the packaging of third-party components, and the slots of third-party components are in the same dom node, we also have a A clever encapsulation method????, get the name of the slot by traversing $slots, and dynamically add the slot of the subcomponent:

//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>

If the above conditions are not met, we have to be honest. Manually add the required third-party component slots in the component~

3. Methods for third-party components

For the methods of third-party components, we implement them through ref. First, add a ref="elInputRef" attribute to the el-input component in the MyInput component, and then expose elInputRef to the parent component through defineExpose.

Subcomponent: 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>

Parent page: The calling code of Index.vue is as follows:

// 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>

The above is the detailed content of How Vue3 Composition API elegantly encapsulates third-party components. For more information, please follow other related articles on the PHP Chinese website!

Statement:
This article is reproduced at:yisu.com. If there is any infringement, please contact admin@php.cn delete