Next.js next/image 관련 코드 까보기 (23.11.19)
packages/next/src/shared/lib/get-img-props.ts
파일 상단에 보면 ImageProps 라는 타입이 정의되어있다. Omit
은 특정 속성만 제거한 타입을 선언한다. JSX.IntrinsicElements['img']
타입에서 'src' | 'srcSet' | 'ref' | 'alt' | 'width' | 'height' | 'loading'
을 제거하고, &
로 추가 타입들을 선언한다.
export type ImageProps = Omit<
JSX.IntrinsicElements["img"],
"src" | "srcSet" | "ref" | "alt" | "width" | "height" | "loading"
> & {
src: string | StaticImport;
alt: string;
width?: number | `${number}`;
height?: number | `${number}`;
fill?: boolean;
loader?: ImageLoader;
quality?: number | `${number}`;
priority?: boolean;
loading?: LoadingValue;
placeholder?: PlaceholderValue;
blurDataURL?: string;
unoptimized?: boolean;
/**
* @deprecated Use `onLoad` instead.
* @see https://nextjs.org/docs/app/api-reference/components/image#onload
*/
onLoadingComplete?: OnLoadingComplete;
/**
* @deprecated Use `fill` prop instead of `layout="fill"` or change import to `next/legacy/image`.
* @see https://nextjs.org/docs/api-reference/next/legacy/image
*/
layout?: string;
/**
* @deprecated Use `style` prop instead.
*/
objectFit?: string;
/**
* @deprecated Use `style` prop instead.
*/
objectPosition?: string;
/**
* @deprecated This prop does not do anything.
*/
lazyBoundary?: string;
/**
* @deprecated This prop does not do anything.
*/
lazyRoot?: string;
};
getWidths
해당 함수에서는 next.config.js
에 설정되어 있는 deviceSizes
와 allSizes
를 참조하여 width를 설정한다.
function getWidths(
{ deviceSizes, allSizes }: ImageConfig,
width: number | undefined,
sizes: string | undefined
): { widths: number[]; kind: "w" | "x" } {
if (sizes) {
// Find all the "vw" percent sizes used in the sizes prop
const viewportWidthRe = /(^|\s)(1?\d?\d)vw/g;
const percentSizes = [];
for (let match; (match = viewportWidthRe.exec(sizes)); match) {
// match : [" 100vw", " ", "100"]
// match[2] : "100"
percentSizes.push(parseInt(match[2]));
// percentSizes = [100, 50, 33];
}
if (percentSizes.length) {
const smallestRatio = Math.min(...percentSizes) * 0.01;
return {
// allSizes에 설정한 width를 순회하며 size가 deviceSizes의 첫번째 요소 * smallestRatio 보다 크거나 같은것들만 필터한다.
widths: allSizes.filter((s) => s >= deviceSizes[0] * smallestRatio),
kind: "w",
};
}
return { widths: allSizes, kind: "w" };
}
if (typeof width !== "number") {
return { widths: deviceSizes, kind: "w" };
}
const widths = [
...new Set(
// > This means that most OLED screens that say they are 3x resolution,
// > are actually 3x in the green color, but only 1.5x in the red and
// > blue colors. Showing a 3x resolution image in the app vs a 2x
// > resolution image will be visually the same, though the 3x image
// > takes significantly more data. Even true 3x resolution screens are
// > wasteful as the human eye cannot see that level of detail without
// > something like a magnifying glass.
// https://blog.twitter.com/engineering/en_us/topics/infrastructure/2019/capping-image-fidelity-on-ultra-high-resolution-devices.html
// 3배까지 지원하지 않는 이유는 OLED 화질에서 육안으로 확인하기 힘들 정도의 차이이기 때문
[width, width * 2 /*, width * 3*/].map(
(w) => allSizes.find((p) => p >= w) || allSizes[allSizes.length - 1]
)
),
];
return { widths, kind: "x" };
}
Last updated