|
| 1 | +// Customized pre-fetch for page chunks based on |
| 2 | +// https://github.com/GoogleChromeLabs/quicklink |
| 3 | + |
| 4 | +import { onMounted, onUnmounted, onUpdated } from 'vue' |
| 5 | +import { inBrowser, pathToFile } from '../utils' |
| 6 | + |
| 7 | +const hasFetched = new Set<string>() |
| 8 | +const createLink = () => document.createElement('link') |
| 9 | + |
| 10 | +const viaDOM = (url: string) => { |
| 11 | + const link = createLink() |
| 12 | + link.rel = `prefetch` |
| 13 | + link.href = url |
| 14 | + document.head.appendChild(link) |
| 15 | +} |
| 16 | + |
| 17 | +const viaXHR = (url: string) => { |
| 18 | + const req = new XMLHttpRequest() |
| 19 | + req.open('GET', url, (req.withCredentials = true)) |
| 20 | + req.send() |
| 21 | +} |
| 22 | + |
| 23 | +let link |
| 24 | +const doFetch: (url: string) => void = |
| 25 | + inBrowser && |
| 26 | + (link = createLink()) && |
| 27 | + link.relList && |
| 28 | + link.relList.supports && |
| 29 | + link.relList.supports('prefetch') |
| 30 | + ? viaDOM |
| 31 | + : viaXHR |
| 32 | + |
| 33 | +export function usePrefetch() { |
| 34 | + if (!inBrowser) { |
| 35 | + return |
| 36 | + } |
| 37 | + |
| 38 | + if (!window.IntersectionObserver) { |
| 39 | + return |
| 40 | + } |
| 41 | + |
| 42 | + let conn |
| 43 | + if ( |
| 44 | + (conn = (navigator as any).connection) && |
| 45 | + (conn.saveData || /2g/.test(conn.effectiveType)) |
| 46 | + ) { |
| 47 | + // Don't prefetch if using 2G or if Save-Data is enabled. |
| 48 | + return |
| 49 | + } |
| 50 | + |
| 51 | + const rIC = (window as any).requestIdleCallback || setTimeout |
| 52 | + let observer: IntersectionObserver | null = null |
| 53 | + |
| 54 | + const observeLinks = () => { |
| 55 | + if (observer) { |
| 56 | + observer.disconnect() |
| 57 | + } |
| 58 | + |
| 59 | + observer = new IntersectionObserver((entries) => { |
| 60 | + entries.forEach((entry) => { |
| 61 | + if (entry.isIntersecting) { |
| 62 | + const link = entry.target as HTMLAnchorElement |
| 63 | + observer!.unobserve(link) |
| 64 | + const { pathname } = link |
| 65 | + if (!hasFetched.has(pathname)) { |
| 66 | + hasFetched.add(pathname) |
| 67 | + const pageChunkPath = pathToFile(pathname) |
| 68 | + doFetch(pageChunkPath) |
| 69 | + } |
| 70 | + } |
| 71 | + }) |
| 72 | + }) |
| 73 | + |
| 74 | + rIC(() => { |
| 75 | + document.querySelectorAll('.vitepress-content a').forEach((link) => { |
| 76 | + if ((link as HTMLAnchorElement).hostname === location.hostname) { |
| 77 | + observer!.observe(link) |
| 78 | + } |
| 79 | + }) |
| 80 | + }) |
| 81 | + } |
| 82 | + |
| 83 | + onMounted(observeLinks) |
| 84 | + onUpdated(observeLinks) |
| 85 | + |
| 86 | + onUnmounted(() => { |
| 87 | + observer && observer.disconnect() |
| 88 | + }) |
| 89 | +} |
0 commit comments