diff --git a/packages/config/src/config/render.js b/packages/config/src/config/render.js
index 508197b549..15d82a5b53 100644
--- a/packages/config/src/config/render.js
+++ b/packages/config/src/config/render.js
@@ -2,7 +2,8 @@
export default () => ({
bundleRenderer: {
- shouldPrefetch: () => false
+ shouldPrefetch: () => false,
+ shouldPreload: (fileWithoutQuery, asType) => ['script', 'style'].includes(asType)
},
resourceHints: true,
ssr: undefined,
diff --git a/packages/vue-renderer/src/spa-meta.js b/packages/vue-renderer/src/spa-meta.js
index 4940477085..512eb08d4e 100644
--- a/packages/vue-renderer/src/spa-meta.js
+++ b/packages/vue-renderer/src/spa-meta.js
@@ -1,3 +1,4 @@
+import { extname } from 'path'
import Vue from 'vue'
import VueMeta from 'vue-meta'
import { createRenderer } from 'vue-server-renderer'
@@ -70,8 +71,8 @@ export default class SPAMetaRenderer {
const clientManifest = this.renderer.context.resources.clientManifest
- const shouldPreload = this.options.render.bundleRenderer.shouldPreload || (() => true)
- const shouldPrefetch = this.options.render.bundleRenderer.shouldPrefetch || (() => true)
+ const shouldPreload = this.options.render.bundleRenderer.shouldPreload
+ const shouldPrefetch = this.options.render.bundleRenderer.shouldPrefetch
if (this.options.render.resourceHints && clientManifest) {
const publicPath = clientManifest.publicPath || '/_nuxt/'
@@ -79,18 +80,25 @@ export default class SPAMetaRenderer {
// Preload initial resources
if (Array.isArray(clientManifest.initial)) {
meta.resourceHints += clientManifest.initial
- .filter(file => shouldPreload(file))
- .map(
- r => ``
- )
+ .map(SPAMetaRenderer.normalizeFile)
+ .filter(({ fileWithoutQuery, asType }) => shouldPreload(fileWithoutQuery, asType))
+ .map(({ file, extension, fileWithoutQuery, asType }) => {
+ let extra = ''
+ if (asType === 'font') {
+ extra = ` type="font/${extension}" crossorigin`
+ }
+ return ``
+ })
.join('')
}
// Prefetch async resources
if (Array.isArray(clientManifest.async)) {
meta.resourceHints += clientManifest.async
- .filter(file => shouldPrefetch(file))
- .map(r => ``)
+ .map(SPAMetaRenderer.normalizeFile)
+ .filter(({ fileWithoutQuery, asType }) => shouldPrefetch(fileWithoutQuery, asType))
+ .map(({ file }) => ``)
.join('')
}
@@ -104,16 +112,36 @@ export default class SPAMetaRenderer {
meta.getPreloadFiles = () =>
clientManifest.initial
.filter(file => shouldPreload(file))
- .map(r => ({
- file: r,
- fileWithoutQuery: r,
- asType: 'script',
- extension: 'js'
- }))
+ .map(SPAMetaRenderer.normalizeFile)
// Set meta tags inside cache
this.cache.set(url, meta)
return meta
}
+
+ static normalizeFile(file) {
+ const withoutQuery = file.replace(/\?.*/, '')
+ const extension = extname(withoutQuery).slice(1)
+ return {
+ file,
+ extension,
+ fileWithoutQuery: withoutQuery,
+ asType: SPAMetaRenderer.getPreloadType(extension)
+ }
+ }
+
+ static getPreloadType(ext) {
+ if (ext === 'js') {
+ return 'script'
+ } else if (ext === 'css') {
+ return 'style'
+ } else if (/jpe?g|png|svg|gif|webp|ico/.test(ext)) {
+ return 'image'
+ } else if (/woff2?|ttf|otf|eot/.test(ext)) {
+ return 'font'
+ } else {
+ return ''
+ }
+ }
}
diff --git a/test/fixtures/spa/nuxt.config.js b/test/fixtures/spa/nuxt.config.js
index d8337c48e8..e3f902b0f4 100644
--- a/test/fixtures/spa/nuxt.config.js
+++ b/test/fixtures/spa/nuxt.config.js
@@ -4,6 +4,15 @@ export default {
render: {
http2: {
push: true
+ },
+ bundleRenderer: {
+ shouldPrefetch: () => true
+ }
+ },
+ build: {
+ filenames: {
+ app: '[name].js',
+ chunk: '[name].js'
}
},
plugins: [
diff --git a/test/unit/spa.test.js b/test/unit/spa.test.js
index 41dfa70b41..34dd665e59 100644
--- a/test/unit/spa.test.js
+++ b/test/unit/spa.test.js
@@ -1,5 +1,5 @@
import consola from 'consola'
-import { loadFixture, getPort, Nuxt } from '../utils'
+import { loadFixture, getPort, Nuxt, wChunk } from '../utils'
let nuxt, port
const url = route => 'http://localhost:' + port + route
@@ -27,6 +27,21 @@ describe('spa', () => {
consola.log.mockClear()
})
+ test('/ (include preload and prefetch resources)', async () => {
+ const { head } = await renderRoute('/')
+ expect(head).toMatch(``)
+ expect(head).toMatch(``)
+ expect(head).toMatch(``)
+ expect(head).toMatch(``)
+ expect(head).toMatch(``)
+ expect(head).toMatch(``)
+ expect(head).toMatch(``)
+ expect(head).toMatch(``)
+ expect(head).toMatch(``)
+ expect(head).toMatch(``)
+ consola.log.mockClear()
+ })
+
test('/custom (custom layout)', async () => {
const { html } = await renderRoute('/custom')
expect(html).toMatch('Custom layout')