Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
63 changes: 63 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,69 @@ or as a menu:
</template>
```

## Prefetch Support

This plugin includes built-in support for [Inertia Prefetch](https://inertiajs.com/prefetching) functionality, following Inertia's exact implementation patterns.

### Default Behavior
By default, prefetching is **disabled** for all links (matching Inertia's behavior):

```html
<template>
<!-- This link will NOT prefetch by default -->
<v-btn :to="route('dashboard')">Dashboard</v-btn>
</template>
```

### Enabling Prefetch
You can enable prefetching for specific links using the `prefetch` prop:

```html
<template>
<!-- Enable hover prefetching (default mode when prefetch=true) -->
<v-btn :to="route('dashboard')" :prefetch="true">Dashboard</v-btn>

<!-- Or explicitly specify hover mode -->
<v-btn :to="route('dashboard')" prefetch="hover">Dashboard</v-btn>
</template>
```

### Prefetch Modes
The plugin supports multiple prefetch modes, just like Inertia:

```html
<template>
<!-- Prefetch on hover (75ms delay) -->
<v-btn :to="route('dashboard')" prefetch="hover">Dashboard</v-btn>

<!-- Prefetch on mousedown/click -->
<v-btn :to="route('dashboard')" prefetch="click">Dashboard</v-btn>

<!-- Prefetch immediately when component mounts -->
<v-btn :to="route('dashboard')" prefetch="mount">Dashboard</v-btn>

<!-- Multiple modes -->
<v-btn :to="route('dashboard')" :prefetch="['hover', 'click']">Dashboard</v-btn>
</template>
```

### Caching
Prefetched responses are cached for 30 seconds by default. You can customize this:

```html
<template>
<!-- Cache for 60 seconds (60000ms) -->
<v-btn :to="route('dashboard')" :prefetch="true" :cache-for="60000">Dashboard</v-btn>
</template>
```

### How It Works
- **Hover mode**: Prefetching is triggered 75ms after hovering over a link (matches Inertia's timing)
- **Click mode**: Prefetching happens on mousedown, navigation on mouseup for instant perceived performance
- **Mount mode**: Prefetching happens immediately when the component is rendered
- Prefetching is automatically cancelled when appropriate (e.g., mouse leaves in hover mode)
- Only works for links that point to different pages (not the current page)


Thank you to https://github.com/robjuz for the original idea and implementation posted on a github issue.
https://github.com/vuetifyjs/vuetify/issues/11573#issuecomment-1465046711
102 changes: 96 additions & 6 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,105 @@ const VuetifyInertiaLink = {
useLink(props) {
const href = props.to.value;
const currentUrl = computed(() => usePage().url);

// Default prefetch to false, following Inertia's behavior
const prefetch = props.prefetch?.value ?? false;
const cacheFor = props.cacheFor?.value ?? 30000; // Default 30 seconds like Inertia

let hoverTimeout = null;

// Determine prefetch modes based on prefetch prop
const prefetchModes = computed(() => {
if (prefetch === true) {
return ['hover'];
}

if (prefetch === false) {
return [];
}

if (Array.isArray(prefetch)) {
return prefetch;
}

return [prefetch];
});

const doPrefetch = () => {
if (href !== currentUrl.value) {
router.prefetch(href, {}, { cacheFor });
}
};

// Regular click handlers without prefetch
const regularEvents = {
onClick: (event) => {
if (event.shiftKey || event.metaKey || event.ctrlKey) return;
event.preventDefault();
router.visit(href);
}
};

// Hover prefetch event handlers
const prefetchHoverEvents = {
onMouseenter: () => {
hoverTimeout = setTimeout(() => {
doPrefetch();
}, 75); // Match Inertia's 75ms timeout
},
onMouseleave: () => {
if (hoverTimeout) {
clearTimeout(hoverTimeout);
hoverTimeout = null;
}
},
onClick: regularEvents.onClick
};

// Click prefetch event handlers
const prefetchClickEvents = {
onMousedown: (event) => {
if (event.shiftKey || event.metaKey || event.ctrlKey) return;
event.preventDefault();
doPrefetch();
},
onMouseup: (event) => {
event.preventDefault();
router.visit(href);
},
onClick: (event) => {
if (event.shiftKey || event.metaKey || event.ctrlKey) return;
// Let the mouseup event handle the visit
event.preventDefault();
}
};

// Handle mount prefetching
if (prefetchModes.value.includes('mount')) {
// Vue 3 doesn't have a direct onMounted in this context,
// but we can prefetch immediately
doPrefetch();
}

// Determine which event handlers to use
const eventHandlers = (() => {
if (prefetchModes.value.includes('hover')) {
return prefetchHoverEvents;
}

if (prefetchModes.value.includes('click')) {
return prefetchClickEvents;
}

return regularEvents;
})();

return {
route: computed(() => ({ href })),
isActive: computed(() => currentUrl.value.startsWith(href)),
isExactActive: computed(() => href === currentUrl),
navigate(e) {
if (e.shiftKey || e.metaKey || e.ctrlKey) return;
e.preventDefault();
router.visit(href);
}
isExactActive: computed(() => href === currentUrl.value),
navigate: eventHandlers.onClick,
...eventHandlers
};
}
});
Expand Down