mirror of https://github.com/vuejs/vitepress
parent
7a90c4f870
commit
da4852a61b
@ -1,9 +1,14 @@
|
||||
import { h } from 'vue'
|
||||
import { useRoute } from '../router'
|
||||
import { usePrefetch } from '../composables/preFetch'
|
||||
|
||||
export const Content = {
|
||||
setup() {
|
||||
const route = useRoute()
|
||||
if (!__DEV__) {
|
||||
// in prod mode, enable intersectionObserver based pre-fetch.
|
||||
usePrefetch()
|
||||
}
|
||||
return () => (route.contentComponent ? h(route.contentComponent) : null)
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,89 @@
|
||||
// Customized pre-fetch for page chunks based on
|
||||
// https://github.com/GoogleChromeLabs/quicklink
|
||||
|
||||
import { onMounted, onUnmounted, onUpdated } from 'vue'
|
||||
import { inBrowser, pathToFile } from '../utils'
|
||||
|
||||
const hasFetched = new Set<string>()
|
||||
const createLink = () => document.createElement('link')
|
||||
|
||||
const viaDOM = (url: string) => {
|
||||
const link = createLink()
|
||||
link.rel = `prefetch`
|
||||
link.href = url
|
||||
document.head.appendChild(link)
|
||||
}
|
||||
|
||||
const viaXHR = (url: string) => {
|
||||
const req = new XMLHttpRequest()
|
||||
req.open('GET', url, (req.withCredentials = true))
|
||||
req.send()
|
||||
}
|
||||
|
||||
let link
|
||||
const doFetch: (url: string) => void =
|
||||
inBrowser &&
|
||||
(link = createLink()) &&
|
||||
link.relList &&
|
||||
link.relList.supports &&
|
||||
link.relList.supports('prefetch')
|
||||
? viaDOM
|
||||
: viaXHR
|
||||
|
||||
export function usePrefetch() {
|
||||
if (!inBrowser) {
|
||||
return
|
||||
}
|
||||
|
||||
if (!window.IntersectionObserver) {
|
||||
return
|
||||
}
|
||||
|
||||
let conn
|
||||
if (
|
||||
(conn = (navigator as any).connection) &&
|
||||
(conn.saveData || /2g/.test(conn.effectiveType))
|
||||
) {
|
||||
// Don't prefetch if using 2G or if Save-Data is enabled.
|
||||
return
|
||||
}
|
||||
|
||||
const rIC = (window as any).requestIdleCallback || setTimeout
|
||||
let observer: IntersectionObserver | null = null
|
||||
|
||||
const observeLinks = () => {
|
||||
if (observer) {
|
||||
observer.disconnect()
|
||||
}
|
||||
|
||||
observer = new IntersectionObserver((entries) => {
|
||||
entries.forEach((entry) => {
|
||||
if (entry.isIntersecting) {
|
||||
const link = entry.target as HTMLAnchorElement
|
||||
observer!.unobserve(link)
|
||||
const { pathname } = link
|
||||
if (!hasFetched.has(pathname)) {
|
||||
hasFetched.add(pathname)
|
||||
const pageChunkPath = pathToFile(pathname)
|
||||
doFetch(pageChunkPath)
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
rIC(() => {
|
||||
document.querySelectorAll('.vitepress-content a').forEach((link) => {
|
||||
if ((link as HTMLAnchorElement).hostname === location.hostname) {
|
||||
observer!.observe(link)
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
onMounted(observeLinks)
|
||||
onUpdated(observeLinks)
|
||||
|
||||
onUnmounted(() => {
|
||||
observer && observer.disconnect()
|
||||
})
|
||||
}
|
@ -0,0 +1,32 @@
|
||||
export const inBrowser = typeof window !== 'undefined'
|
||||
|
||||
/**
|
||||
* Converts a url path to the corresponding js chunk filename.
|
||||
*/
|
||||
export function pathToFile(path: string): string {
|
||||
let pagePath = path.replace(/\.html$/, '')
|
||||
if (pagePath.endsWith('/')) {
|
||||
pagePath += 'index'
|
||||
}
|
||||
|
||||
if (__DEV__) {
|
||||
// awlays force re-fetch content in dev
|
||||
pagePath += `.md?t=${Date.now()}`
|
||||
} else {
|
||||
// in production, each .md file is built into a .md.js file following
|
||||
// the path conversion scheme.
|
||||
// /foo/bar.html -> ./foo_bar.md
|
||||
if (inBrowser) {
|
||||
pagePath = pagePath.slice(__BASE__.length).replace(/\//g, '_') + '.md'
|
||||
// client production build needs to account for page hash, which is
|
||||
// injected directly in the page's html
|
||||
const pageHash = __VP_HASH_MAP__[pagePath]
|
||||
pagePath = `${__BASE__}_assets/${pagePath}.${pageHash}.js`
|
||||
} else {
|
||||
// ssr build uses much simpler name mapping
|
||||
pagePath = `./${pagePath.slice(1).replace(/\//g, '_')}.md.js`
|
||||
}
|
||||
}
|
||||
|
||||
return pagePath
|
||||
}
|
Loading…
Reference in new issue