Skip to content

Tối ưu hóa hình ảnh trong Astro

Hình ảnh chiếm phần lớn dung lượng trang web. Tối ưu hóa hình ảnh đúng cách có thể giảm 50-80% kích thước trang, cải thiện đáng kể thờ gian tải và trải nghiệm ngườ dùng.

  • 50% dung lượng trang web trung bình là hình ảnh
  • WebP nhỏ hơn PNG khoảng 26% và JPEG khoảng 25-35%
  • Lazy loading có thể giảm LCP (Largest Contentful Paint) đến 50%
  • Mỗi 100ms cải thiện tốc độ tải trang có thể tăng 1% chuyển đổi
  1. Hình ảnh quá lớn: Upload ảnh 4000px cho thumbnail 200px
  2. Format không tối ưu: Dùng PNG cho ảnh chụp, JPEG cho ảnh có trong suốt
  3. Không lazy load: Tải tất cả ảnh ngay từ đầu
  4. Thiếu responsive: Một kích thước cho tất cả thiết bị
  5. Không có placeholder: Layout shift khi ảnh tải xong

Astro cung cấp component <Image /> tích hợp sẵn để tối ưu hóa tự động:

---
import { Image } from 'astro:assets';
import myImage from '../assets/my-image.jpg';
---
<!-- Tự động tối ưu -->
<Image
src={myImage}
alt="Mô tả hình ảnh"
width={800}
height={600}
/>
  • Tự động chuyển đổi format: Sang WebP/AVIF nếu trình duyệt hỗ trợ
  • Tự động resize: Theo width/height bạn chỉ định
  • Lazy loading: Mặc định cho ảnh below-the-fold
  • Placeholder: Tự động tạo blur placeholder
  • Srcset tự động: Cho responsive images

2. Tối ưu với định dạng hiện đại

Section titled “2. Tối ưu với định dạng hiện đại”

WebP - Lựa chọn tốt nhất hiện nay

Section titled “WebP - Lựa chọn tốt nhất hiện nay”
<Image
src={myImage}
alt="Mô tả"
format="webp"
quality={80}
/>

Hỗ trợ trình duyệt: Chrome, Firefox, Safari, Edge (đều hỗ trợ từ 2020+)

<Image
src={myImage}
alt="Mô tả"
format="avif"
quality={75}
/>

Ưu điểm:

  • Nhỏ hơn WebP ~20-30%
  • Chất lượng cao hơn ở cùng kích thước

Nhược điểm:

  • Encode chậm hơn WebP
  • Chưa được hỗ trợ rộng rãi bằng WebP (nhưng Astro tự động fallback)
<Image
src={myImage}
alt="Mô tả"
widths={[400, 800, 1200]}
sizes="(max-width: 800px) 400px, (max-width: 1200px) 800px, 1200px"
/>
---
import { Picture } from 'astro:assets';
---
<Picture
src={myImage}
alt="Mô tả"
widths={[400, 800, 1200]}
sizes="(max-width: 800px) 400px, (max-width: 1200px) 800px, 1200px"
formats={['avif', 'webp', 'jpeg']}
fallbackFormat="jpeg"
/>

Eager loading cho ảnh quan trọng (LCP)

Section titled “Eager loading cho ảnh quan trọng (LCP)”
<!-- Ảnh hero, logo - tải ngay lập tức -->
<Image
src={heroImage}
alt="Hero"
loading="eager"
fetchpriority="high"
width={1920}
height={1080}
/>
<!-- Ảnh trong bài viết, gallery - lazy load -->
<Image
src={contentImage}
alt="Content"
loading="lazy"
decoding="async"
width={800}
height={600}
/>
<Image
src={myImage}
alt="Mô tả"
placeholder="dominantColor"
/>
<Image
src={myImage}
alt="Mô tả"
placeholder="blurhash"
/>
import { defineConfig } from 'astro/config';
import image from '@astrojs/image';
export default defineConfig({
integrations: [
image({
// Service mặc định
serviceEntryPoint: '@astrojs/image/sharp',
// Cấu hình cache
cacheDir: './node_modules/.astro/image',
// Log chi tiết
logLevel: 'debug',
}),
],
// Cấu hình build
vite: {
build: {
assetsInlineLimit: 0,
},
},
});

Sharp là thư viện xử lý ảnh nhanh nhất cho Node.js:

Terminal window
npm install sharp
astro.config.mjs
export default defineConfig({
image: {
service: {
entrypoint: 'astro/assets/services/sharp',
},
},
});
---
const imageUrl = `https://imagedelivery.net/ACCOUNT/${imageId}/w=800,h=600`;
---
<img
src={imageUrl}
alt="Mô tả"
loading="lazy"
width={800}
height={600}
/>
---
import { Cloudinary } from '@cloudinary/url-gen';
const cld = new Cloudinary({
cloud: {
cloudName: 'your-cloud-name'
}
});
const myImage = cld.image('sample').format('auto').quality('auto');
---
<img
src={myImage.toURL()}
alt="Mô tả"
/>
  • Kích thước tối đa: 2x kích thước hiển thị lớn nhất
  • Format gốc: PNG cho ảnh có trong suốt, JPEG cho ảnh chụp
  • Quality: 80-90% cho ảnh gốc
❌ IMG_20240305_123456.jpg
✅ astro-image-optimization-guide.webp
<!-- Tốt -->
<Image
src={dog}
alt="Golden Retriever đang chơi trong công viên"
/>
<!-- Không tốt -->
<Image
src={dog}
alt="dog"
/>
<!-- Không tốt -->
<Image
src={dog}
alt="image-123"
/>
src/
├── assets/
│ ├── images/
│ │ ├── blog/ # Ảnh bài viết
│ │ ├── products/ # Ảnh sản phẩm
│ │ └── team/ # Ảnh đội ngũ
│ └── icons/ # Icons SVG

Chạy trong DevTools:

  1. F12 → Lighthouse tab
  2. Chọn “Performance”
  3. Click “Analyze page load”
MetricGoodNeeds ImprovementPoor
LCP≤2.5s≤4s>4s
FID≤100ms≤300ms>300ms
CLS≤0.1≤0.25>0.25
---
import { Image } from 'astro:assets';
import { getCollection } from 'astro:content';
const posts = await getCollection('blog');
---
<div class="blog-grid">
{posts.map(post => (
<article>
<Image
src={post.data.heroImage}
alt={post.data.title}
width={800}
height={450}
format="webp"
quality={80}
loading="lazy"
class="hero-image"
/>
<h2>{post.data.title}</h2>
<p>{post.data.description}</p>
</article>
))}
</div>
<style>
.hero-image {
width: 100%;
height: auto;
border-radius: 8px;
}
</style>
---
import { Image } from 'astro:assets';
import { getImage } from 'astro:assets';
const images = await Promise.all(
[1, 2, 3, 4, 5].map(async (i) => {
const img = await getImage({
src: import(`../assets/gallery/${i}.jpg`),
width: 400,
height: 300,
format: 'webp',
});
return img;
})
);
---
<div class="gallery">
{images.map((img, i) => (
<a href={img.src} data-lightbox="gallery">
<Image
src={img}
alt={`Gallery image ${i + 1}`}
width={400}
height={300}
loading="lazy"
/>
</a>
))}
</div>
<!-- Sai -->
<Image src="/images/photo.jpg" />
<!-- Đúng -->
<Image src={import('../assets/photo.jpg')} />

Kiểm tra loading="lazy" và đảm bảo ảnh below-the-fold.

  • Giảm số lượng ảnh cần xử lý
  • Sử dụng cache
  • Chạy build trên máy có nhiều RAM
  • Sử dụng <Image /> thay vì <img>
  • Cung cấp width và height
  • Sử dụng format WebP/AVIF
  • Lazy load ảnh không quan trọng
  • Eager load ảnh LCP
  • Cung cấp alt text đầy đủ
  • Sử dụng placeholder
  • Test với Lighthouse
  • Kiểm tra Core Web Vitals

Tài liệu liên quan: