-
-
Notifications
You must be signed in to change notification settings - Fork 4.8k
/
nuxt-link.client.js
111 lines (103 loc) · 3.18 KB
/
nuxt-link.client.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
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) {
return
}
link.__prefetch()
})
})
<%= isTest ? '// @vue/component' : '' %>
export default {
name: 'NuxtLink',
extends: Vue.component('RouterLink'),
props: {
prefetch: {
type: Boolean,
default: <%= router.prefetchLinks ? 'true' : 'false' %>
},
noPrefetch: {
type: Boolean,
default: false
}<% if (router.linkPrefetchedClass) { %>,
prefetchedClass: {
type: String,
default: '<%= router.linkPrefetchedClass %>'
}<% } %>
},
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
}<% if (router.linkPrefetchedClass) { %> else {
this.addPrefetchedClass()
}<% } %>
},
shouldPrefetch () {
return this.getPrefetchComponents().length > 0
},
canPrefetch () {
const conn = navigator.connection
const hasBadConnection = this.<%= globals.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
}<% if (router.linkPrefetchedClass) { %>
this.addPrefetchedClass()<% } %>
}<% if (router.linkPrefetchedClass) { %>,
addPrefetchedClass () {
if (this.prefetchedClass !== 'false') {
this.$el.className = (this.$el.className + ' ' + this.prefetchedClass).trim()
}
}<% } %>
}
}