mirror of
https://github.com/nuxt/nuxt.git
synced 2024-11-30 09:27:13 +00:00
refactor: spa renderer (#4316)
This commit is contained in:
parent
d590858ff5
commit
70f0dc3825
@ -2,7 +2,8 @@
|
|||||||
|
|
||||||
export default () => ({
|
export default () => ({
|
||||||
bundleRenderer: {
|
bundleRenderer: {
|
||||||
shouldPrefetch: () => false
|
shouldPrefetch: () => false,
|
||||||
|
shouldPreload: (fileWithoutQuery, asType) => ['script', 'style'].includes(asType)
|
||||||
},
|
},
|
||||||
resourceHints: true,
|
resourceHints: true,
|
||||||
ssr: undefined,
|
ssr: undefined,
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import { extname } from 'path'
|
||||||
import Vue from 'vue'
|
import Vue from 'vue'
|
||||||
import VueMeta from 'vue-meta'
|
import VueMeta from 'vue-meta'
|
||||||
import { createRenderer } from 'vue-server-renderer'
|
import { createRenderer } from 'vue-server-renderer'
|
||||||
@ -70,8 +71,8 @@ export default class SPAMetaRenderer {
|
|||||||
|
|
||||||
const clientManifest = this.renderer.context.resources.clientManifest
|
const clientManifest = this.renderer.context.resources.clientManifest
|
||||||
|
|
||||||
const shouldPreload = this.options.render.bundleRenderer.shouldPreload || (() => true)
|
const shouldPreload = this.options.render.bundleRenderer.shouldPreload
|
||||||
const shouldPrefetch = this.options.render.bundleRenderer.shouldPrefetch || (() => true)
|
const shouldPrefetch = this.options.render.bundleRenderer.shouldPrefetch
|
||||||
|
|
||||||
if (this.options.render.resourceHints && clientManifest) {
|
if (this.options.render.resourceHints && clientManifest) {
|
||||||
const publicPath = clientManifest.publicPath || '/_nuxt/'
|
const publicPath = clientManifest.publicPath || '/_nuxt/'
|
||||||
@ -79,18 +80,25 @@ export default class SPAMetaRenderer {
|
|||||||
// Preload initial resources
|
// Preload initial resources
|
||||||
if (Array.isArray(clientManifest.initial)) {
|
if (Array.isArray(clientManifest.initial)) {
|
||||||
meta.resourceHints += clientManifest.initial
|
meta.resourceHints += clientManifest.initial
|
||||||
.filter(file => shouldPreload(file))
|
.map(SPAMetaRenderer.normalizeFile)
|
||||||
.map(
|
.filter(({ fileWithoutQuery, asType }) => shouldPreload(fileWithoutQuery, asType))
|
||||||
r => `<link rel="preload" href="${publicPath}${r}" as="script" />`
|
.map(({ file, extension, fileWithoutQuery, asType }) => {
|
||||||
)
|
let extra = ''
|
||||||
|
if (asType === 'font') {
|
||||||
|
extra = ` type="font/${extension}" crossorigin`
|
||||||
|
}
|
||||||
|
return `<link rel="preload" href="${publicPath}${file}"${
|
||||||
|
asType !== '' ? ` as="${asType}"` : ''}${extra}>`
|
||||||
|
})
|
||||||
.join('')
|
.join('')
|
||||||
}
|
}
|
||||||
|
|
||||||
// Prefetch async resources
|
// Prefetch async resources
|
||||||
if (Array.isArray(clientManifest.async)) {
|
if (Array.isArray(clientManifest.async)) {
|
||||||
meta.resourceHints += clientManifest.async
|
meta.resourceHints += clientManifest.async
|
||||||
.filter(file => shouldPrefetch(file))
|
.map(SPAMetaRenderer.normalizeFile)
|
||||||
.map(r => `<link rel="prefetch" href="${publicPath}${r}" />`)
|
.filter(({ fileWithoutQuery, asType }) => shouldPrefetch(fileWithoutQuery, asType))
|
||||||
|
.map(({ file }) => `<link rel="prefetch" href="${publicPath}${file}">`)
|
||||||
.join('')
|
.join('')
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -104,16 +112,36 @@ export default class SPAMetaRenderer {
|
|||||||
meta.getPreloadFiles = () =>
|
meta.getPreloadFiles = () =>
|
||||||
clientManifest.initial
|
clientManifest.initial
|
||||||
.filter(file => shouldPreload(file))
|
.filter(file => shouldPreload(file))
|
||||||
.map(r => ({
|
.map(SPAMetaRenderer.normalizeFile)
|
||||||
file: r,
|
|
||||||
fileWithoutQuery: r,
|
|
||||||
asType: 'script',
|
|
||||||
extension: 'js'
|
|
||||||
}))
|
|
||||||
|
|
||||||
// Set meta tags inside cache
|
// Set meta tags inside cache
|
||||||
this.cache.set(url, meta)
|
this.cache.set(url, meta)
|
||||||
|
|
||||||
return 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 ''
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
9
test/fixtures/spa/nuxt.config.js
vendored
9
test/fixtures/spa/nuxt.config.js
vendored
@ -4,6 +4,15 @@ export default {
|
|||||||
render: {
|
render: {
|
||||||
http2: {
|
http2: {
|
||||||
push: true
|
push: true
|
||||||
|
},
|
||||||
|
bundleRenderer: {
|
||||||
|
shouldPrefetch: () => true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
build: {
|
||||||
|
filenames: {
|
||||||
|
app: '[name].js',
|
||||||
|
chunk: '[name].js'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
plugins: [
|
plugins: [
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import consola from 'consola'
|
import consola from 'consola'
|
||||||
import { loadFixture, getPort, Nuxt } from '../utils'
|
import { loadFixture, getPort, Nuxt, wChunk } from '../utils'
|
||||||
|
|
||||||
let nuxt, port
|
let nuxt, port
|
||||||
const url = route => 'http://localhost:' + port + route
|
const url = route => 'http://localhost:' + port + route
|
||||||
@ -27,6 +27,21 @@ describe('spa', () => {
|
|||||||
consola.log.mockClear()
|
consola.log.mockClear()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test('/ (include preload and prefetch resources)', async () => {
|
||||||
|
const { head } = await renderRoute('/')
|
||||||
|
expect(head).toMatch(`<link rel="preload" href="/_nuxt/runtime.js" as="script">`)
|
||||||
|
expect(head).toMatch(`<link rel="preload" href="/_nuxt/commons.app.js" as="script">`)
|
||||||
|
expect(head).toMatch(`<link rel="preload" href="/_nuxt/app.js" as="script">`)
|
||||||
|
expect(head).toMatch(`<link rel="prefetch" href="/_nuxt/${wChunk('pages/custom.js')}">`)
|
||||||
|
expect(head).toMatch(`<link rel="prefetch" href="/_nuxt/${wChunk('pages/error-handler-async.js')}">`)
|
||||||
|
expect(head).toMatch(`<link rel="prefetch" href="/_nuxt/${wChunk('pages/error-handler-object.js')}">`)
|
||||||
|
expect(head).toMatch(`<link rel="prefetch" href="/_nuxt/${wChunk('pages/error-handler-string.js')}">`)
|
||||||
|
expect(head).toMatch(`<link rel="prefetch" href="/_nuxt/${wChunk('pages/error-handler.js')}">`)
|
||||||
|
expect(head).toMatch(`<link rel="prefetch" href="/_nuxt/${wChunk('pages/index.js')}">`)
|
||||||
|
expect(head).toMatch(`<link rel="prefetch" href="/_nuxt/${wChunk('pages/mounted.js')}">`)
|
||||||
|
consola.log.mockClear()
|
||||||
|
})
|
||||||
|
|
||||||
test('/custom (custom layout)', async () => {
|
test('/custom (custom layout)', async () => {
|
||||||
const { html } = await renderRoute('/custom')
|
const { html } = await renderRoute('/custom')
|
||||||
expect(html).toMatch('Custom layout')
|
expect(html).toMatch('Custom layout')
|
||||||
|
Loading…
Reference in New Issue
Block a user