ใน NextJS หากเรามีการใช้งานรูปภาพ NextJS จะแนะนำให้ เราจะใช้ next/image
เพื่อช่วยให้เราสามารถจัดการและแสดงรูปภาพบนเว็บไซต์ได้อย่างง่ายดายและมีประสิทธิภาพ
เช่น:
- Responsive Images: ปรับขนาดรูปภาพอัตโนมัติให้เหมาะสมกับอุปกรณ์ที่ใช้งานโดยไม่ต้องเขียนโค้ดเอง
- Lazy Loading: โหลดรูปภาพเฉพาะเมื่อถูกเลื่อนไปถึงเพื่อปรับปรุงประสิทธิภาพของหน้าเว็บ
- Automatic Optimization: Optimize images for web formats like WebP for enhanced performance
- SEO Friendly: สร้าง markup ที่เหมาะสมสำหรับเครื่องมือค้นหา
แต่เมื่อเราต้องการทดสอบ component ที่มีการใช้งาน next/image ปรากฏว่ามันไม่แสดงผล ดังภาพ
import Image from 'next/image'
import React from 'react'
type Props = {}
const CustomImage = (props: Props) => {
return (
<div>
<h1>Custom Image</h1>
{/* public/example.webp */}
<Image src='/example.webp' alt='example image' width={300} height={165} />
</div>
)
}
export default CustomImage
custom-image.tsx

ถ้าเราลอง inspect ดูจะพบว่า มันไปเรียกไฟล์ ที่ /_next/image?url=%2Fexample.webp&w=640&q=75
ซึ่งมันไม่ใช่ที่อยู่ของไฟล์ ที่เรากำหนดไว้

ที่เป็นแบบนี้ เป็นเพราะว่า เวลา run NextJS (next dev / next build) NextJS จะทำการจัดการไฟล์รูปภาพแล้วนำไปเก็บไว้ที่โฟลเดอร์ next/static/media/*
ซึ่งเมื่อเราทำสอบด้วย cypress ตัว cypress จะไปหาไฟล์นั้นไม่เจอ
วิธีจัดการกับไฟล์ภาพ
การจัดการกับการเข้าถึงภาพเหล่านั้น เราจะไปเข้าไปหาไฟล์ภาพใน _next
โดยตรง เพราะชื่อภาพจะเปลี่ยนแปลงทุกครั้งที่มีการ run หรือ build ใหม่ ทำให้ยากต่อการจัดการ และบางถาพเราอาจจะไม่รู้ว่า ภาพที่เราใช้มันชื่อว่าอะไร เพราะมันจะเพิ่มชื่อให้ภาพที่ผ่านการจัดการแล้วเราด้วย
ดังนั้นสิ่งที่เราจะทำคือ เปลี่ยนที่อยู่ของภาพให้มัน link ไปยังไฟล์ภาพต้นฉบับ ซึ่งวิธีนี้เราจะต้องทำตัว mock next/image
ขึ้นมา โดยมีวิธีการดังนี้
ติดตั้ง webpack
npm install -D webpack
หรือ
yarn add -D webpack
install webpack
กำหนดที่อยู่ของไฟล์ Mock Image
จากนั้นให้เราไปกำหนดค่าในไฟล์ cypress.config.ts
import { defineConfig } from "cypress";
import path from "path";
import { NormalModuleReplacementPlugin } from "webpack";
export default defineConfig({
component: {
devServer: {
framework: "next",
bundler: "webpack",
// Add webpackConfig
webpackConfig: {
plugins: [
new NormalModuleReplacementPlugin(
/next\/image/,
require.resolve(
path.join(__dirname, "cypress", "mocks", "next", "image")
)
),
],
},
},
},
});
cypress.config.ts
new NormalModuleReplacementPlugin
เป็นการ replece ตัว module ที่เราต้องการ โดยจะกำหนดให้ไปเรียกใช้งานไฟล์ image ที่อยู่ที่ cypress/mocks/next/image
แทน
สร้างไฟล์ Mock Image
เมื่อกำหนด config เรียบร้อยแล้ว ให้เราไปเพิ่มไฟล์ image.ts
ในโฟล์เดอร์ cypress/mocks/next/image.ts
// cypress/mocks/next/image.tsx
import type { ImageProps } from 'next/image'
/**
* Converts the next/image static image URL to a regular path.
*
* Example:
*
* /_next/static/media/404.ea2b1f50.png -> /assets/images/404.png
*/
const convertURL = (url: string) => {
const newURL = url
.replace(/\/_next\/static\/media\//, '/public/') // Use actual images location
.replace(/(?<=\.)(.+)(?=png|jp?eg|tiff?|png|webp|bmp|gif|svg)/, '')
return newURL
}
const Image = (props: ImageProps) => {
// Regular path to image resource
if (typeof props.src === 'string') {
return (
// eslint-disable-next-line @next/next/no-img-element
<img
src={props.src}
alt={props.alt}
width={props.width}
height={props.height}
style={props.style}
/>
)
}
let src: string
// StaticImageData - an import of image resource
if ('src' in props.src) {
src = props.src.src
} else {
// StaticRequire
src = props.src.default.src
}
// eslint-disable-next-line @next/next/no-img-element
return <img src={convertURL(src)} alt={props.alt} style={props.style} />
}
export default Image
image.ts
เพียงเท่านี้ เราก็สามารถ link ภาพไปยังไฟล์ภาพต้นฉบับได้แล้ว
