Cross-framework compatibility utilities and adapters for building Vue components that work seamlessly across Vue 2, Vue 2.7, and Vue 3
—
Quality
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
The Core Composition Hooks provide essential Vue composition utilities for component relationships, slot management, and performance optimization. These hooks are built on top of the adapter system and work seamlessly across Vue 2 and Vue 3.
Hook for managing component slots with cross-framework compatibility and reactive updates.
/**
* Create instance slots manager with cross-framework compatibility
* @param hooks - Adapter hooks system
* @returns Instance slots manager
*/
function useInstanceSlots(hooks: AdapterHooks): any;Usage Examples:
import { useInstanceSlots, hooks } from "@opentiny/vue-common";
export default {
setup(props, context) {
const instanceSlots = useInstanceSlots(hooks);
// Access slots reactively
const hasDefaultSlot = hooks.computed(() => {
return !!instanceSlots.default;
});
const hasHeaderSlot = hooks.computed(() => {
return !!instanceSlots.header;
});
return {
instanceSlots,
hasDefaultSlot,
hasHeaderSlot
};
},
template: `
<div class="component">
<header v-if="hasHeaderSlot">
<slot name="header" />
</header>
<main v-if="hasDefaultSlot">
<slot />
</main>
</div>
`
};Hook for managing parent-child component relationships and communication patterns.
/**
* Create component relation manager for parent-child communication
* @param hooks - Adapter hooks system
* @returns Component relation manager
*/
function useRelation(hooks: AdapterHooks): any;Usage Examples:
import { useRelation, hooks } from "@opentiny/vue-common";
// Parent component
export default {
name: 'ParentComponent',
setup() {
const relation = useRelation(hooks);
const children = hooks.ref([]);
const registerChild = (child) => {
children.value.push(child);
};
const unregisterChild = (child) => {
const index = children.value.indexOf(child);
if (index > -1) {
children.value.splice(index, 1);
}
};
// Provide registration methods to children
hooks.provide('parentRelation', {
registerChild,
unregisterChild
});
return {
children,
relation
};
}
};
// Child component
export default {
name: 'ChildComponent',
setup() {
const relation = useRelation(hooks);
const parentRelation = hooks.inject('parentRelation', null);
const instance = hooks.getCurrentInstance();
hooks.onMounted(() => {
if (parentRelation) {
parentRelation.registerChild(instance);
}
});
hooks.onBeforeUnmount(() => {
if (parentRelation) {
parentRelation.unregisterChild(instance);
}
});
return {
relation
};
}
};Performance optimization hook for progressive component rendering with frame-based control.
/**
* Create deferred rendering controller for performance optimization
* @param maxCount - Maximum frame count before stopping (default: 100)
* @returns Deferred rendering controller
*/
function useDefer(maxCount?: number): {
/** Check if rendering should be deferred for given frame number */
defer(n: number): boolean;
/** Reset frame counter to start over */
reset(): void;
/** Cancel animation frames and stop counting */
cancel(): void;
};Usage Examples:
import { useDefer, hooks } from "@opentiny/vue-common";
export default {
setup() {
const deferrer = useDefer(50); // Limit to 50 frames
// Progressive rendering of list items
const items = hooks.ref(Array.from({ length: 1000 }, (_, i) => `Item ${i}`));
const visibleItems = hooks.computed(() => {
return items.value.filter((_, index) => {
// Show first 10 items immediately
if (index < 10) return true;
// Progressive reveal based on frame count
const frameThreshold = Math.floor(index / 10) + 1;
return deferrer.defer(frameThreshold);
});
});
// Reset deferred rendering on data change
hooks.watch(items, () => {
deferrer.reset();
});
return {
visibleItems,
deferrer
};
},
template: `
<div class="large-list">
<div v-for="item in visibleItems" :key="item" class="list-item">
{{ item }}
</div>
</div>
`
};Using instance slots for flexible component composition.
import { useInstanceSlots, hooks } from "@opentiny/vue-common";
export default {
setup() {
const slots = useInstanceSlots(hooks);
// Dynamic slot analysis
const slotAnalysis = hooks.computed(() => {
const analysis = {
hasContent: false,
hasActions: false,
hasCustom: false,
customSlots: []
};
if (slots.default) analysis.hasContent = true;
if (slots.actions) analysis.hasActions = true;
// Find custom slots
Object.keys(slots).forEach(slotName => {
if (!['default', 'actions'].includes(slotName)) {
analysis.hasCustom = true;
analysis.customSlots.push(slotName);
}
});
return analysis;
});
return {
slots,
slotAnalysis
};
},
template: `
<div class="flexible-component">
<div v-if="slotAnalysis.hasContent" class="content">
<slot />
</div>
<div v-for="customSlot in slotAnalysis.customSlots"
:key="customSlot"
:class="'custom-' + customSlot">
<slot :name="customSlot" />
</div>
<div v-if="slotAnalysis.hasActions" class="actions">
<slot name="actions" />
</div>
</div>
`
};Building a comprehensive parent-child communication system.
import { useRelation, hooks } from "@opentiny/vue-common";
// Communication mixin
const useParentChildComm = () => {
const relation = useRelation(hooks);
// For parent components
const createParentComm = () => {
const children = hooks.ref(new Map());
const addChild = (id, child) => {
children.value.set(id, child);
};
const removeChild = (id) => {
children.value.delete(id);
};
const broadcastToChildren = (message, data) => {
children.value.forEach(child => {
if (child.receiveMessage) {
child.receiveMessage(message, data);
}
});
};
hooks.provide('parentComm', {
addChild,
removeChild,
broadcastToChildren
});
return {
children,
broadcastToChildren
};
};
// For child components
const createChildComm = (childId) => {
const parentComm = hooks.inject('parentComm', null);
const messages = hooks.ref([]);
const receiveMessage = (message, data) => {
messages.value.push({ message, data, timestamp: Date.now() });
};
const sendToParent = (message, data) => {
if (parentComm) {
parentComm.receiveFromChild?.(childId, message, data);
}
};
hooks.onMounted(() => {
if (parentComm) {
parentComm.addChild(childId, { receiveMessage });
}
});
hooks.onBeforeUnmount(() => {
if (parentComm) {
parentComm.removeChild(childId);
}
});
return {
messages,
receiveMessage,
sendToParent
};
};
return {
createParentComm,
createChildComm
};
};Using deferred rendering for large datasets.
import { useDefer, hooks } from "@opentiny/vue-common";
export default {
props: {
items: Array,
chunkSize: {
type: Number,
default: 20
}
},
setup(props) {
const deferrer = useDefer();
// Chunk items for progressive rendering
const itemChunks = hooks.computed(() => {
const chunks = [];
for (let i = 0; i < props.items.length; i += props.chunkSize) {
chunks.push(props.items.slice(i, i + props.chunkSize));
}
return chunks;
});
// Progressive chunk rendering
const visibleChunks = hooks.computed(() => {
return itemChunks.value.filter((_, chunkIndex) => {
return deferrer.defer(chunkIndex + 1);
});
});
const visibleItems = hooks.computed(() => {
return visibleChunks.value.flat();
});
// Loading state
const isLoading = hooks.computed(() => {
return visibleItems.value.length < props.items.length;
});
// Reset on items change
hooks.watch(() => props.items, () => {
deferrer.reset();
});
return {
visibleItems,
isLoading,
totalItems: hooks.computed(() => props.items.length),
loadedItems: hooks.computed(() => visibleItems.value.length)
};
}
};These hooks integrate seamlessly with the component setup system:
import { setup, useInstanceSlots, useRelation, useDefer } from "@opentiny/vue-common";
export default {
setup(props, context) {
return setup({
props,
context,
renderless: (props, hooks, utils) => {
// Use composition hooks within renderless functions
const slots = useInstanceSlots(hooks);
const relation = useRelation(hooks);
const deferrer = useDefer(30);
return {
slots,
relation,
deferrer
};
},
api: ['slots', 'relation', 'deferrer']
});
}
};Install with Tessl CLI
npx tessl i tessl/npm-opentiny--vue-common