Advanced Vue 3 Component Encapsulation: v-model, Slot Passing, and Ref Access
This article demonstrates how to create a reusable Vue 3 component with Vite and Element‑Plus, covering two‑way data binding via v‑model, slot forwarding using the h function, and exposing child component refs through a Proxy, complete with code examples and explanations.
The tutorial shows how to set up a Vue 3 project with Vite and Element‑Plus, then create a reusable component that supports two‑way data binding, slot transmission, and child ref access.
Two‑way data binding is illustrated using the standard v-model="xxxx" syntax and the newer defineModel() helper, which simplifies model handling in custom components.
Passing slots to child components is achieved by creating a WrapInput.vue component that iterates over $slots and renders them, and by using the component element with is="h(ElInput, $attrs, $slots)" to forward slots via the h function. Example usage in app.vue demonstrates #prepend and #append slots.
<script setup lang="ts">
import { ref } from "vue";
import WrapInput from "./components/WrapInput.vue";
const inputText = ref('');
</script>
<template>
<WrapInput v-model="inputText">
<template #prepend>Http://</template>
<template #append>.com</template>
</WrapInput>
</template>Accessing child component refs is first shown with a manual getRef method, then refined using a Proxy to expose all properties of the underlying input element. The updated WrapInput.vue defines a ref and uses defineExpose(new Proxy(...)) to forward method calls.
<script setup lang="ts">
import { h, ref } from "vue";
import { ElInput } from "element-plus";
const model = defineModel();
const inputRef = ref();
defineExpose(new Proxy({}, {
get(_target, prop) { return inputRef.value?.[prop]; },
has(_target, prop) { return prop in inputRef.value; }
}));
</script>
<template>
<component is="h(ElInput, $attrs, $slots)" v-model="model" ref="inputRef"></component>
</template>The parent App.vue uses the component, updates slot content dynamically, and calls the exposed focus method via a ref, demonstrating full interaction between parent and child.
<script setup lang="ts">
import { ref } from "vue";
import WrapInput from "./components/WrapInput.vue";
const inputText = ref('');
const prependSlotText = ref('Http://');
const appendSlotText = ref('.com');
function updateSlotInfo() {
prependSlotText.value = 'https://';
appendSlotText.value = `${new Date().getTime()}`;
}
const wrapInputRef = ref();
function setWrapInputFocus() { wrapInputRef.value?.focus(); }
</script>
<template>
<WrapInput v-model="inputText" ref="wrapInputRef">
<template #prepend>{{ prependSlotText }}</template>
<template #append>{{ appendSlotText }}</template>
</WrapInput>
<div style="margin: 20px 0;">{{inputText}}</div>
<el-button type="primary" @click="updateSlotInfo">Update Slots</el-button>
<el-button type="primary" @click="setWrapInputFocus">Set Input Focus</el-button>
</template>In summary, the article demonstrates practical techniques for encapsulating Vue 3 components, handling v-model, forwarding slots via the h function, and exposing child refs using a Proxy , providing a solid foundation for building reusable UI components.
Rare Earth Juejin Tech Community
Juejin, a tech community that helps developers grow.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.