Skip to content

Commit

Permalink
fix!: improve prerendering support (#725)
Browse files Browse the repository at this point in the history
Co-Authored-By: Pascal <posixpascal@googlemail.com>
Co-Authored-By:  Jim Hlad <jimhlad@gmail.com>
  • Loading branch information
3 people authored Jan 27, 2023
1 parent 5a49fab commit 96fd3d0
Show file tree
Hide file tree
Showing 5 changed files with 51 additions and 24 deletions.
2 changes: 1 addition & 1 deletion src/module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ export default defineNuxtModule<ModuleOptions>({
name: '@nuxt/image',
configKey: 'image',
compatibility: {
nuxt: '^3.0.0-rc.4'
nuxt: '^3.1.0'
}
},
async setup (options, nuxt) {
Expand Down
11 changes: 4 additions & 7 deletions src/runtime/components/nuxt-img.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { h, defineComponent, ref, computed, onMounted } from 'vue'
import { appendHeader } from 'h3'
import { useImage } from '../composables'
import { parseSize } from '../utils'
import { prerenderStaticImages } from '../utils/prerender'
import { baseImageProps, useBaseImage } from './_base'
import { useHead, useRequestEvent } from '#imports'
import { useHead } from '#imports'

export const imgProps = {
...baseImageProps,
Expand Down Expand Up @@ -87,12 +87,9 @@ export default defineComponent({
})
}

// Prerender static images
if (process.server && process.env.prerender) {
const sources = [
src.value,
...(sizes.value.srcset || '').split(',').map(s => s.split(' ')[0])
].filter(s => s && s.includes('/_ipx/'))
appendHeader(useRequestEvent(), 'X-Nitro-Prerender', sources.join(','))
prerenderStaticImages(src.value, sizes.value.srcset)
}

const imgEl = ref<HTMLImageElement>()
Expand Down
39 changes: 24 additions & 15 deletions src/runtime/components/nuxt-picture.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { h, defineComponent, ref, computed, onMounted } from 'vue'
import { prerenderStaticImages } from '../utils/prerender'
import { useBaseImage, baseImageProps } from './_base'
import { useImage, useHead } from '#imports'
import { getFileExtension } from '#image'
Expand Down Expand Up @@ -32,32 +33,33 @@ export default defineComponent({
return formats[format.value] || originalFormat.value
})

const nSources = computed<Array<{ srcset: string, src?: string, type?: string, sizes?: string }>>(() => {
type Source = { srcset: string, src?: string, type?: string, sizes?: string }
const sources = computed<Source[]>(() => {
if (format.value === 'svg') {
return [{ srcset: props.src }]
return [<Source>{ srcset: props.src }]
}

const formats = legacyFormat.value !== format.value
? [legacyFormat.value, format.value]
: [format.value]

return formats.map((format) => {
const { srcset, sizes, src } = $img.getSizes(props.src, {
const { srcset, sizes, src } = $img.getSizes(props.src!, {
..._base.options.value,
sizes: props.sizes || $img.options.screens,
modifiers: { ..._base.modifiers.value, format }
})

return { src, type: `image/${format}`, sizes, srcset }
return <Source> { src, type: `image/${format}`, sizes, srcset }
})
})

if (props.preload) {
const srcKey = nSources.value?.[1] ? 1 : 0
const srcKey = sources.value?.[1] ? 1 : 0

const link: any = { rel: 'preload', as: 'image', imagesrcset: nSources.value[srcKey].srcset }
const link: any = { rel: 'preload', as: 'image', imagesrcset: sources.value[srcKey].srcset }

if (nSources.value?.[srcKey]?.sizes) { link.imagesizes = nSources.value[srcKey].sizes }
if (sources.value?.[srcKey]?.sizes) { link.imagesizes = sources.value[srcKey].sizes }

useHead({ link: [link] })
}
Expand All @@ -72,27 +74,34 @@ export default defineComponent({

const imgEl = ref<HTMLImageElement>()

// Prerender static images
if (process.server && process.env.prerender) {
for (const src of sources.value as Source[]) {
prerenderStaticImages(src.src, src.srcset)
}
}

onMounted(() => {
imgEl.value!.onload = (event) => {
ctx.emit('load', event)
}
})

return () => h('picture', { key: nSources.value[0].src }, [
...(nSources.value?.[1]
return () => h('picture', { key: sources.value[0].src }, [
...(sources.value?.[1]
? [h('source', {
type: nSources.value[1].type,
sizes: nSources.value[1].sizes,
srcset: nSources.value[1].srcset
type: sources.value[1].type,
sizes: sources.value[1].sizes,
srcset: sources.value[1].srcset
})]
: []),
h('img', {
ref: imgEl,
..._base.attrs.value,
...imgAttrs,
src: nSources.value[0].src,
sizes: nSources.value[0].sizes,
srcset: nSources.value[0].srcset
src: sources.value[0].src,
sizes: sources.value[0].sizes,
srcset: sources.value[0].srcset
})
])
}
Expand Down
2 changes: 1 addition & 1 deletion src/runtime/providers/ipx.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ const operationsGenerator = createOperationsGenerator({
quality: 'q',
background: 'b'
},
joinWith: ',',
joinWith: '&',
formatter: (key, val) => encodeParam(key) + '_' + encodeParam(val)
})

Expand Down
21 changes: 21 additions & 0 deletions src/runtime/utils/prerender.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { appendHeader } from 'h3'
import { useRequestEvent } from '#imports'

export function prerenderStaticImages (src = '', srcset = '') {
if (!process.server || !process.env.prerender) { return }

const paths = [
src,
...srcset.split(', ').map(s => s.trim().split(' ')[0].trim())
].filter(s => s && s.includes('/_ipx/'))

if (!paths.length) {
return
}

appendHeader(
useRequestEvent(),
'x-nitro-prerender',
paths.map(p => encodeURIComponent(p)).join(', ')
)
}

0 comments on commit 96fd3d0

Please sign in to comment.