feat(common): make the warning for lazy-loaded lcp image an error (#51748)

upgrade the warning for lazy-loaded lcp images when using NgOptimizedImage to an error

BREAKING CHANGE:

Previously when NgOptimizedImage directive detected that an LCP image is lazy-loaded, a console warning was produced. Now the directive throws an error to make it more discoverable in a console. If you receive this error, refer to this guide for additional information: https://angular.io/guide/image-directive#step-4-mark-images-as-priority

PR Close #51748
This commit is contained in:
Alex Castle 2023-09-12 15:45:14 -07:00 committed by Pawel Kozlowski
parent 545db6d22d
commit fe2fd7e1a8
3 changed files with 14 additions and 15 deletions

View file

@ -65,7 +65,7 @@ Marking an image as `priority` applies the following optimizations:
* Sets `loading=eager` (read more about native lazy loading [here](https://web.dev/browser-level-image-lazy-loading))
* Automatically generates a [preload link element](https://developer.mozilla.org/en-US/docs/Web/HTML/Link_types/preload) if [rendering on the server](/guide/universal).
Angular displays a warning during development if the LCP element is an image that does not have the `priority` attribute. A pages LCP element can vary based on a number of factors - such as the dimensions of a user's screen, so a page may have multiple images that should be marked `priority`. See [CSS for Web Vitals](https://web.dev/css-web-vitals/#images-and-largest-contentful-paint-lcp) for more details.
Angular throws an error during development if the LCP element is an image that does not have the `priority` attribute, as this can hurt loading performance significantly. A pages LCP element can vary based on a number of factors - such as the dimensions of a user's screen, so a page may have multiple images that should be marked `priority`. See [CSS for Web Vitals](https://web.dev/css-web-vitals/#images-and-largest-contentful-paint-lcp) for more details.
#### Step 5: Include Height and Width

View file

@ -6,7 +6,7 @@
* found in the LICENSE file at https://angular.io/license
*/
import {inject, Injectable, OnDestroy, ɵformatRuntimeError as formatRuntimeError} from '@angular/core';
import {inject, Injectable, OnDestroy, ɵformatRuntimeError as formatRuntimeError, ɵRuntimeError as RuntimeError} from '@angular/core';
import {DOCUMENT} from '../../dom_tokens';
import {RuntimeErrorCode} from '../../errors';
@ -74,7 +74,7 @@ export class LCPImageObserver implements OnDestroy {
if (!img) return;
if (!img.priority && !img.alreadyWarnedPriority) {
img.alreadyWarnedPriority = true;
logMissingPriorityWarning(imgSrc);
throwMissingPriorityError(imgSrc);
}
if (img.modified && !img.alreadyWarnedModified) {
img.alreadyWarnedModified = true;
@ -118,14 +118,14 @@ export class LCPImageObserver implements OnDestroy {
}
}
function logMissingPriorityWarning(ngSrc: string) {
function throwMissingPriorityError(ngSrc: string) {
const directiveDetails = imgDirectiveDetails(ngSrc);
console.warn(formatRuntimeError(
throw new RuntimeError(
RuntimeErrorCode.LCP_IMG_MISSING_PRIORITY,
`${directiveDetails} this image is the Largest Contentful Paint (LCP) ` +
`element but was not marked "priority". This image should be marked ` +
`"priority" in order to prioritize its loading. ` +
`To fix this, add the "priority" attribute.`));
`${directiveDetails} this image is the Largest Contentful Paint (LCP) element ` +
`but was not marked "priority". This can have a strong negative impact ` +
`on the LCP score of the entire application. This error can be fixed by ` +
`adding the 'priority' attribute to all images which are possibly the LCP element.`);
}
function logModifiedWarning(ngSrc: string) {

View file

@ -27,12 +27,11 @@ describe('NgOptimizedImage directive', () => {
srcB = await imgs.get(2).getAttribute('src');
expect(srcB.endsWith('b.png')).toBe(true);
// Make sure that only one warning is in the console for image `a.png`,
// Make sure that only one error is in the console for image `a.png`,
// since the `b.png` should be below the fold and not treated as an LCP element.
const logs = await collectBrowserLogs(logging.Level.WARNING);
expect(logs.length).toEqual(2);
// Verify that the error code and the image src are present in the error message.
expect(logs[0].message).toMatch(/NG02955.*?a\.png/);
expect(logs[1].message).toMatch(/NG02964.*?logo-500w\.jpg/);
const logs = await collectBrowserLogs(logging.Level.SEVERE);
expect(logs.length).toEqual(1);
expect(logs[0].message).toMatch(/RuntimeError: NG02955/);
});
});