feat(vue-app): add prefetch prop to <nuxt-link> (#6292)

This commit is contained in:
Louis-Marie Michelin 2019-09-18 17:06:46 +02:00 committed by Pooya Parsa
parent ac5066c4e0
commit 7b3155347c
7 changed files with 182 additions and 5 deletions

View File

@ -17,7 +17,7 @@ import {
globalHandleError
} from './utils.js'
import { createApp<% if (features.layouts) { %>, NuxtError<% } %> } from './index.js'
import NuxtLink from './components/nuxt-link.<%= features.clientPrefetch && router.prefetchLinks ? "client" : "server" %>.js' // should be included after ./index.js
import NuxtLink from './components/nuxt-link.<%= features.clientPrefetch ? "client" : "server" %>.js' // should be included after ./index.js
<% if (isDev) { %>import consola from 'consola'<% } %>
<% if (isDev) { %>consola.wrapConsole()

View File

@ -29,6 +29,10 @@ export default {
name: 'NuxtLink',
extends: Vue.component('RouterLink'),
props: {
prefetch: {
type: Boolean,
default: <%= router.prefetchLinks ? 'true' : 'false' %>
},
noPrefetch: {
type: Boolean,
default: false
@ -39,7 +43,7 @@ export default {
}<% } %>
},
mounted () {
if (!this.noPrefetch) {
if (this.prefetch && !this.noPrefetch) {
this.handleId = requestIdleCallback(this.observe, { timeout: 2e3 })
}
},
@ -59,7 +63,7 @@ export default {
}
// Add to observer
if (this.shouldPrefetch()) {
this.$el.__prefetch = this.prefetch.bind(this)
this.$el.__prefetch = this.prefetchLink.bind(this)
observer.observe(this.$el)
this.__observed = true
}<% if (router.linkPrefetchedClass) { %> else {
@ -81,7 +85,7 @@ export default {
return Components.filter(Component => typeof Component === 'function' && !Component.options && !Component.__prefetched)
},
prefetch () {
prefetchLink () {
if (!this.canPrefetch()) {
return
}

View File

@ -5,6 +5,10 @@ export default {
name: 'NuxtLink',
extends: Vue.component('RouterLink'),
props: {
prefetch: {
type: Boolean,
default: <%= router.prefetchLinks ? 'true' : 'false' %>
},
noPrefetch: {
type: Boolean,
default: false

View File

@ -32,7 +32,7 @@ export async function compileTemplate (template, destination, options = {}) {
if (typeof template === 'string') {
return {
src: path.resolve(rootDir, '../template', template),
dst: path.join(rootDir, '.nuxt', path.basename(template)),
dst: path.join(rootDir, '.nuxt', destination || path.basename(template)),
custom: false
}
}

View File

@ -0,0 +1,163 @@
/**
* @jest-environment jsdom
*/
import { mount, RouterLinkStub } from '@vue/test-utils'
import { vmTick, compileTemplate, importComponent } from './__utils__'
/* eslint-disable no-console */
describe('nuxt-link prefetch', () => {
beforeAll(() => {
jest.useFakeTimers()
jest.spyOn(console, 'warn')
jest.spyOn(console, 'error')
})
afterAll(() => jest.restoreAllMocks())
test('when router.prefetchLinks is set to false, link with no prop should not be prefetched',
async () => {
const compiledTemplatePath = await compileTemplate(
'components/nuxt-link.client.js',
'nuxt-link.client.prefetch.0.js',
{ router: { prefetchLinks: false } }
)
const Component = await importComponent(compiledTemplatePath)
Component.extends = RouterLinkStub
const methods = { observe: jest.fn() }
const wrapper = mount(Component, {
propsData: { to: '/link' },
methods
})
jest.runAllTimers()
await vmTick(wrapper.vm)
expect(console.warn).not.toHaveBeenCalled()
expect(console.error).not.toHaveBeenCalled()
expect(wrapper.props('prefetch')).toBe(false)
expect(wrapper.props('noPrefetch')).toBe(false)
expect(methods.observe).not.toHaveBeenCalled()
})
test('when router.prefetchLinks is set to false, link with prefetch prop set to true should be prefetched',
async () => {
const compiledTemplatePath = await compileTemplate(
'components/nuxt-link.client.js',
'nuxt-link.client.prefetch.1.js',
{ router: { prefetchLinks: false } }
)
const Component = await importComponent(compiledTemplatePath)
Component.extends = RouterLinkStub
const methods = { observe: jest.fn() }
const wrapper = mount(Component, {
propsData: { to: '/link', prefetch: true },
methods
})
jest.runAllTimers()
await vmTick(wrapper.vm)
expect(console.warn).not.toHaveBeenCalled()
expect(console.error).not.toHaveBeenCalled()
expect(wrapper.props('prefetch')).toBe(true)
expect(wrapper.props('noPrefetch')).toBe(false)
expect(methods.observe).toHaveBeenCalled()
})
test('when router.prefetchLinks is set to true (default), link with no prop should be prefetched',
async () => {
const compiledTemplatePath = await compileTemplate(
'components/nuxt-link.client.js',
'nuxt-link.client.prefetch.2.js',
{}
)
const Component = await importComponent(compiledTemplatePath)
Component.extends = RouterLinkStub
const methods = { observe: jest.fn() }
const wrapper = mount(Component, {
propsData: { to: '/link' },
methods
})
jest.runAllTimers()
await vmTick(wrapper.vm)
expect(console.warn).not.toHaveBeenCalled()
expect(console.error).not.toHaveBeenCalled()
expect(wrapper.props('prefetch')).toBe(true)
expect(wrapper.props('noPrefetch')).toBe(false)
expect(methods.observe).toHaveBeenCalled()
})
test('when router.prefetchLinks is set to true (default), link with prefetch prop set to false should not be prefetched',
async () => {
const compiledTemplatePath = await compileTemplate(
'components/nuxt-link.client.js',
'nuxt-link.client.prefetch.3.js',
{}
)
const Component = await importComponent(compiledTemplatePath)
Component.extends = RouterLinkStub
const methods = { observe: jest.fn() }
const wrapper = mount(Component, {
propsData: { to: '/link', prefetch: false },
methods
})
jest.runAllTimers()
await vmTick(wrapper.vm)
expect(console.warn).not.toHaveBeenCalled()
expect(console.error).not.toHaveBeenCalled()
expect(wrapper.props('prefetch')).toBe(false)
expect(wrapper.props('noPrefetch')).toBe(false)
expect(methods.observe).not.toHaveBeenCalled()
})
test('when router.prefetchLinks is set to true (default), link with noPrefetch prop should not be prefetched',
async () => {
const compiledTemplatePath = await compileTemplate(
'components/nuxt-link.client.js',
'nuxt-link.client.prefetch.4.js',
{}
)
const Component = await importComponent(compiledTemplatePath)
Component.extends = RouterLinkStub
const methods = { observe: jest.fn() }
const wrapper = mount(Component, {
propsData: { to: '/link', noPrefetch: true },
methods
})
jest.runAllTimers()
await vmTick(wrapper.vm)
expect(console.warn).not.toHaveBeenCalled()
expect(console.error).not.toHaveBeenCalled()
expect(wrapper.props('prefetch')).toBe(true)
expect(wrapper.props('noPrefetch')).toBe(true)
expect(methods.observe).not.toHaveBeenCalled()
})
})
/* eslint-enable no-console */

View File

@ -5,6 +5,10 @@
"to": {
"description": "Denotes the target route of the link. When clicked, the value of the to prop will be passed to router.push() internally, so the value can be either a string or a location descriptor object."
},
"prefetch": {
"type": "boolean",
"description": "Prefetch route target (overrides router.prefetchLinks value in nuxt.config.js)."
},
"no-prefetch": {
"description": "Avoid prefetching route target."
},

View File

@ -21,6 +21,7 @@
"exact",
"event",
"exact-active-class",
"prefetch",
"no-prefetch"
],
"description": "Component for navigating between Nuxt pages."
@ -35,6 +36,7 @@
"exact",
"event",
"exact-active-class",
"prefetch",
"no-prefetch"
],
"description": "Component for navigating between Nuxt pages."