Frontend Development 7 min read

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.

Rare Earth Juejin Tech Community
Rare Earth Juejin Tech Community
Rare Earth Juejin Tech Community
Advanced Vue 3 Component Encapsulation: v-model, Slot Passing, and Ref Access

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.

proxycomponentVuerefslotsv-model
Rare Earth Juejin Tech Community
Written by

Rare Earth Juejin Tech Community

Juejin, a tech community that helps developers grow.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.