You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
98 lines
2.6 KiB
98 lines
2.6 KiB
import Vue from 'vue' |
|
|
|
const requestIdleCallback = window.requestIdleCallback || |
|
function (cb) { |
|
const start = Date.now() |
|
return setTimeout(function () { |
|
cb({ |
|
didTimeout: false, |
|
timeRemaining: () => Math.max(0, 50 - (Date.now() - start)) |
|
}) |
|
}, 1) |
|
} |
|
|
|
const cancelIdleCallback = window.cancelIdleCallback || function (id) { |
|
clearTimeout(id) |
|
} |
|
|
|
const observer = window.IntersectionObserver && new window.IntersectionObserver((entries) => { |
|
entries.forEach(({ intersectionRatio, target: link }) => { |
|
if (intersectionRatio <= 0 || !link.__prefetch) { |
|
return |
|
} |
|
link.__prefetch() |
|
}) |
|
}) |
|
|
|
export default { |
|
name: 'NuxtLink', |
|
extends: Vue.component('RouterLink'), |
|
props: { |
|
prefetch: { |
|
type: Boolean, |
|
default: true |
|
}, |
|
noPrefetch: { |
|
type: Boolean, |
|
default: false |
|
} |
|
}, |
|
mounted () { |
|
if (this.prefetch && !this.noPrefetch) { |
|
this.handleId = requestIdleCallback(this.observe, { timeout: 2e3 }) |
|
} |
|
}, |
|
beforeDestroy () { |
|
cancelIdleCallback(this.handleId) |
|
|
|
if (this.__observed) { |
|
observer.unobserve(this.$el) |
|
delete this.$el.__prefetch |
|
} |
|
}, |
|
methods: { |
|
observe () { |
|
// If no IntersectionObserver, avoid prefetching |
|
if (!observer) { |
|
return |
|
} |
|
// Add to observer |
|
if (this.shouldPrefetch()) { |
|
this.$el.__prefetch = this.prefetchLink.bind(this) |
|
observer.observe(this.$el) |
|
this.__observed = true |
|
} |
|
}, |
|
shouldPrefetch () { |
|
return this.getPrefetchComponents().length > 0 |
|
}, |
|
canPrefetch () { |
|
const conn = navigator.connection |
|
const hasBadConnection = this.$nuxt.isOffline || (conn && ((conn.effectiveType || '').includes('2g') || conn.saveData)) |
|
|
|
return !hasBadConnection |
|
}, |
|
getPrefetchComponents () { |
|
const ref = this.$router.resolve(this.to, this.$route, this.append) |
|
const Components = ref.resolved.matched.map(r => r.components.default) |
|
|
|
return Components.filter(Component => typeof Component === 'function' && !Component.options && !Component.__prefetched) |
|
}, |
|
prefetchLink () { |
|
if (!this.canPrefetch()) { |
|
return |
|
} |
|
// Stop observing this link (in case of internet connection changes) |
|
observer.unobserve(this.$el) |
|
const Components = this.getPrefetchComponents() |
|
|
|
for (const Component of Components) { |
|
const componentOrPromise = Component() |
|
if (componentOrPromise instanceof Promise) { |
|
componentOrPromise.catch(() => {}) |
|
} |
|
Component.__prefetched = true |
|
} |
|
} |
|
} |
|
}
|
|
|