Skip to content

Angular NgOptimizedImage Deep Practice: LCP Optimization from Theory to Production

The NgOptimizedImage directive built into Angular 15 is Angular's official answer to Core Web Vitals. It's more than just adding lazy loading to an img tag — this article explores how to use it in real projects and the optimization logic behind it.

Quick Recap: What NgOptimizedImage Does ​

typescript
import { NgOptimizedImage } from "@angular/common";

@Component({
  standalone: true,
  imports: [NgOptimizedImage],
  template: `
    <!-- 使用 ngSrc 替代 src -->
    <img ngSrc="hero-banner.jpg" width="1200" height="600" priority />
  `,
})
export class HeroComponent {}

底层优化列表:

  • priority 图片:自动注入 <link rel="preload">,提升 LCP
  • 非 priority 图片:自动添加 loading="lazy" 和 decoding="async"
  • 强制尺寸:要求显式指定 width/height,防止 CLS
  • 过大图片警告:检测请求图片尺寸是否远大于渲染尺寸
  • 自动 srcset:根据常见屏幕密度生成多种尺寸

CDN Loader Configuration ​

NgOptimizedImage 与 CDN 集成后效果最好——它会生成正确的 CDN URL 来请求合适尺寸的图片:

typescript
// app.config.ts(Standalone 应用)
import { provideImgixLoader } from "@angular/common"; // 或其他 CDN

bootstrapApplication(AppComponent, {
  providers: [
    // Imgix
    provideImgixLoader("https://my-bucket.imgix.net"),

    // Cloudinary
    // provideCloudinaryLoader('https://res.cloudinary.com/my-cloud'),

    // Cloudflare Images
    // provideImageKitLoader('https://ik.imagekit.io/my-id'),
  ],
});

配置后,ngSrc="products/hero.jpg" 会自动变成:

html
<!-- 自动生成适合不同屏幕的 srcset -->
<img
  src="https://my-bucket.imgix.net/products/hero.jpg?w=1200&auto=format"
  srcset="
    https://my-bucket.imgix.net/products/hero.jpg?w=600&auto=format   600w,
    https://my-bucket.imgix.net/products/hero.jpg?w=960&auto=format   960w,
    https://my-bucket.imgix.net/products/hero.jpg?w=1200&auto=format 1200w
  "
  loading="eager"
  fetchpriority="high"
/>

Custom CDN Loader ​

对于私有 CDN 或对象存储,可以实现自定义 loader:

typescript
// providers/image-loader.provider.ts
import { IMAGE_LOADER, ImageLoaderConfig } from "@angular/common";

// 假设 CDN 格式:https://cdn.example.com/image/width,height/path
export const customCdnLoader = {
  provide: IMAGE_LOADER,
  useValue: (config: ImageLoaderConfig) => {
    const { src, width } = config;
    if (!width) return `https://cdn.example.com/${src}`;
    return `https://cdn.example.com/resize,w_${width}/${src}`;
  },
};

// 注册
bootstrapApplication(AppComponent, {
  providers: [customCdnLoader],
});

Responsive Images and the sizes Attribute ​

typescript
@Component({
  standalone: true,
  imports: [NgOptimizedImage],
  template: `
    <!-- 商品列表:移动端全宽,桌面端 33vw -->
    <img
      ngSrc="products/{{ product.slug }}.jpg"
      width="400"
      height="400"
      sizes="(max-width: 768px) 100vw, (max-width: 1200px) 50vw, 33vw"
      [alt]="product.name"
    />

    <!-- 对话框中的图片(固定 300px) -->
    <img
      ngSrc="avatars/{{ user.id }}.jpg"
      width="300"
      height="300"
      sizes="300px"
      [alt]="user.name"
    />
  `,
})
export class ProductCardComponent {
  product!: Product;
  user!: User;
}

Placeholder Images and Progressive Loading ​

typescript
@Component({
  standalone: true,
  imports: [NgOptimizedImage, NgIf],
  template: `
    <div class="image-wrapper">
      <!-- 使用 LQIP(低质量图片占位)-->
      <img
        ngSrc="articles/{{ article.cover }}"
        width="800"
        height="450"
        [placeholder]="true"
        placeholderConfig="{ blur: 20 }"
        [alt]="article.title"
      />
    </div>
  `,
})
export class ArticleCardComponent {
  article!: Article;
}

Real-World LCP Improvement Data ​

在一个典型的商品列表页(桌面端,首屏 9 张商品图):

指标优化前优化后(NgOptimizedImage + Imgix)
LCP3.8s1.6s
CLS0.120.01
图片总字节1.8MB420KB

LCP 提升的主要原因:priority 图片的 <link rel="preload"> 让浏览器在 HTML 解析阶段就开始下载 LCP 图片,而不是等到渲染树构建完成。

Common Issue Troubleshooting ​

typescript
// 警告:Image with src "..." has a ratio mismatch
// 原因:width/height 比例与实际图片不符
// 解决:设置正确的宽高比,或使用 fill 模式

// fill 模式(适合不知道尺寸的场景)
<div style="position: relative; width: 100%; height: 300px;">
  <img ngSrc="cover.jpg" fill [alt]="title" />
</div>

Summary ​

NgOptimizedImage 将图片优化的最佳实践内置到框架层——开发者不再需要手动处理 srcset、preload hint、lazy loading。对 LCP 指标有要求的项目(电商、内容类),配合 CDN Loader 使用,是目前 Angular 中改善 Core Web Vitals 成本最低的方案。

MIT Licensed