963 markdown files reformatted with the repository's pinned prettier so pnpm format:check covers the folded tree like every other repo file. The formatter's embedded-language pass also normalized code fences (TS semicolons, closed HTML tags in examples, lowercased CSS hex colors, one renumbered list that skipped an index). Alphanumeric token deltas vs the fold commit were audited file-by-file; all are formatter-equivalent markup normalizations plus the four sanitized skills.
4.4 KiB
4.4 KiB
title, impact, impactDescription, type, tags
| title | impact | impactDescription | type | tags | |||||
|---|---|---|---|---|---|---|---|---|---|
| Teleport Preserves Logical Component Hierarchy | LOW | Understanding that Teleport only changes DOM structure, not Vue's component tree | best-practice |
|
Teleport Preserves Logical Component Hierarchy
Impact: LOW - A common misconception is that Teleport breaks the Vue component relationship. In reality, <Teleport> only alters the rendered DOM structure, not the logical hierarchy. Props, events, provide/inject, and Vue Devtools all work as if the component wasn't teleported.
Task Checklist
- Continue using props and events normally with teleported components
- Use provide/inject across teleported boundaries
- Check Vue Devtools for component location (shows logical parent, not DOM location)
Key Concept:
<!-- ParentComponent.vue -->
<template>
<div class="parent">
<Teleport to="body">
<!-- ChildComponent is logically still a child of ParentComponent -->
<!-- even though it renders in <body> -->
<ChildComponent :message="parentMessage" @update="handleUpdate" />
</Teleport>
</div>
</template>
<script setup>
import { provide, ref } from 'vue';
const parentMessage = ref('Hello from parent');
// Provide still works across teleport
provide('theme', 'dark');
function handleUpdate(value) {
// Events bubble up through logical hierarchy, not DOM
console.log('Received from teleported child:', value);
}
</script>
<!-- ChildComponent.vue -->
<template>
<div class="modal">
<p>{{ message }}</p>
<p>Theme: {{ theme }}</p>
<button @click="$emit('update', 'new value')">Update</button>
</div>
</template>
<script setup>
import { inject } from 'vue';
// Props work normally
defineProps(['message']);
// Inject works across teleport boundary
const theme = inject('theme');
// Emit works normally
defineEmits(['update']);
</script>
What Teleport Changes vs. Preserves
| Aspect | Changed? | Notes |
|---|---|---|
| DOM position | Yes | Content moves to to target |
| CSS inheritance | Yes | Styles inherit from new DOM parent |
| Props | No | Work exactly as without Teleport |
| Events (emit) | No | Bubble through logical hierarchy |
| Provide/Inject | No | Work across teleport boundaries |
| Vue Devtools | No | Shows logical component tree |
| Slots | No | Work normally |
| Template refs | No | Parent can ref teleported content |
Practical Example: Modal with Form
<!-- ModalForm.vue -->
<template>
<Teleport to="body">
<div v-if="visible" class="modal-overlay">
<form @submit.prevent="handleSubmit" class="modal-form">
<!-- Slot content receives parent's scope -->
<slot :formData="formData" />
<button type="submit">Submit</button>
<button type="button" @click="$emit('close')">Cancel</button>
</form>
</div>
</Teleport>
</template>
<script setup>
import { reactive } from 'vue';
defineProps(['visible']);
const emit = defineEmits(['close', 'submit']);
const formData = reactive({});
function handleSubmit() {
emit('submit', formData);
}
</script>
<!-- ParentPage.vue -->
<template>
<button @click="showModal = true">Add User</button>
<!-- Events and slots work as expected despite teleportation -->
<ModalForm :visible="showModal" @close="showModal = false" @submit="handleFormSubmit">
<template #default="{ formData }">
<input v-model="formData.name" placeholder="Name" />
<input v-model="formData.email" placeholder="Email" />
</template>
</ModalForm>
</template>
<script setup>
import { ref } from 'vue';
const showModal = ref(false);
function handleFormSubmit(data) {
console.log('Form submitted:', data);
showModal.value = false;
}
</script>
Vue Devtools Behavior
In Vue Devtools, teleported components appear under their logical parent:
App
└── ParentPage
└── ModalForm <-- Shows here (logical parent)
└── form content
Not under their DOM location:
// This is NOT how it appears in Devtools
body
└── ModalForm <-- Does NOT show here