nuxt-link.client.js 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128
  1. import Vue from 'vue'
  2. const requestIdleCallback = window.requestIdleCallback ||
  3. function (cb) {
  4. const start = Date.now()
  5. return setTimeout(function () {
  6. cb({
  7. didTimeout: false,
  8. timeRemaining: () => Math.max(0, 50 - (Date.now() - start))
  9. })
  10. }, 1)
  11. }
  12. const cancelIdleCallback = window.cancelIdleCallback || function (id) {
  13. clearTimeout(id)
  14. }
  15. const observer = window.IntersectionObserver && new window.IntersectionObserver((entries) => {
  16. entries.forEach(({ intersectionRatio, target: link }) => {
  17. if (intersectionRatio <= 0 || !link.__prefetch) {
  18. return
  19. }
  20. link.__prefetch()
  21. })
  22. })
  23. <%= isTest ? '// @vue/component' : '' %>
  24. export default {
  25. name: 'NuxtLink',
  26. extends: Vue.component('RouterLink'),
  27. props: {
  28. prefetch: {
  29. type: Boolean,
  30. default: <%= router.prefetchLinks ? 'true' : 'false' %>
  31. },
  32. noPrefetch: {
  33. type: Boolean,
  34. default: false
  35. }<% if (router.linkPrefetchedClass) { %>,
  36. prefetchedClass: {
  37. type: String,
  38. default: '<%= router.linkPrefetchedClass %>'
  39. }<% } %>
  40. },
  41. mounted () {
  42. if (this.prefetch && !this.noPrefetch) {
  43. this.handleId = requestIdleCallback(this.observe, { timeout: 2e3 })
  44. }
  45. },
  46. beforeDestroy () {
  47. cancelIdleCallback(this.handleId)
  48. if (this.__observed) {
  49. observer.unobserve(this.$el)
  50. delete this.$el.__prefetch
  51. }
  52. },
  53. methods: {
  54. observe () {
  55. // If no IntersectionObserver, avoid prefetching
  56. if (!observer) {
  57. return
  58. }
  59. // Add to observer
  60. if (this.shouldPrefetch()) {
  61. this.$el.__prefetch = this.prefetchLink.bind(this)
  62. observer.observe(this.$el)
  63. this.__observed = true
  64. }<% if (router.linkPrefetchedClass) { %> else {
  65. this.addPrefetchedClass()
  66. }<% } %>
  67. },
  68. shouldPrefetch () {
  69. <% if (isFullStatic && router.prefetchPayloads) { %>
  70. const ref = this.$router.resolve(this.to, this.$route, this.append)
  71. const Components = ref.resolved.matched.map(r => r.components.default)
  72. return Components.filter(Component => ref.href || (typeof Component === 'function' && !Component.options && !Component.__prefetched)).length
  73. <% } else { %>return this.getPrefetchComponents().length > 0<% } %>
  74. },
  75. canPrefetch () {
  76. const conn = navigator.connection
  77. const hasBadConnection = this.<%= globals.nuxt %>.isOffline || (conn && ((conn.effectiveType || '').includes('2g') || conn.saveData))
  78. return !hasBadConnection
  79. },
  80. getPrefetchComponents () {
  81. const ref = this.$router.resolve(this.to, this.$route, this.append)
  82. const Components = ref.resolved.matched.map(r => r.components.default)
  83. return Components.filter(Component => typeof Component === 'function' && !Component.options && !Component.__prefetched)
  84. },
  85. prefetchLink () {
  86. if (!this.canPrefetch()) {
  87. return
  88. }
  89. // Stop observing this link (in case of internet connection changes)
  90. observer.unobserve(this.$el)
  91. const Components = this.getPrefetchComponents()
  92. <% if (router.linkPrefetchedClass) { %>const promises = []<% } %>
  93. for (const Component of Components) {
  94. const componentOrPromise = Component()
  95. if (componentOrPromise instanceof Promise) {
  96. componentOrPromise.catch(() => {})
  97. <% if (router.linkPrefetchedClass) { %>promises.push(componentOrPromise)<% } %>
  98. }
  99. Component.__prefetched = true
  100. }
  101. <% if (isFullStatic && router.prefetchPayloads) { %>
  102. // Preload the data only if not in preview mode
  103. if (!this.$root.isPreview) {
  104. const { href } = this.$router.resolve(this.to, this.$route, this.append)
  105. if (this.<%= globals.nuxt %>)
  106. this.<%= globals.nuxt %>.fetchPayload(href, true).catch(() => {})
  107. }
  108. <% } %>
  109. <% if (router.linkPrefetchedClass) { %>
  110. return Promise.all(promises).then(() => this.addPrefetchedClass())
  111. <% } %>
  112. }<% if (router.linkPrefetchedClass) { %>,
  113. addPrefetchedClass () {
  114. if (this.prefetchedClass !== 'false') {
  115. this.$el.className = (this.$el.className + ' ' + this.prefetchedClass).trim()
  116. }
  117. }<% } %>
  118. }
  119. }