Créer des applications angulaires rapides avec SSG

Apprenez à améliorer les performances de votre projet angulaire RSE en passant à la RSS et à la prétention (SSG) dans Angular 20.
De nos jours, lorsque nous créons des applications en angulaire, nous devons réfléchir à plus que du code. Notre produit doit être rapide, et s’il s’agit d’un produit numérique, être découvrable par les moteurs de recherche est essentiel pour atteindre un bon rang dans les résultats de Google.
Mais, lorsque nous construisons nos applications, prenons-nous tout cela en considération ?? Par défaut, Angular crée un SPA avec CSR au lieu de SSR ou SSG. Considérez-vous des mesures de score de phare comme FCP, LCP ou CLS? 😵 Tenez une seconde! Que signifient tous ces termes? Pourquoi sont-ils importants?
Ne t’inquiète pas! Aujourd’hui, commençons un voyage d’exploration de tous ces termes et pourquoi ils sont importants pour nous, et comment Angular nous aide à résoudre la plupart de ces cas. Comme toujours, nous allons apprendre cela avec un véritable projet existant, passant d’un score lent et bas à un score rapide et excellent.
Mais pourquoi ne pas apprendre à en créer un nouveau à partir de zéro? Dans un nouveau champ vert, c’est toujours parfait, mais je veux partager l’expérience du monde réel de déplacer un projet existant construit avec les options par défaut d’Angular.
Commençons!
Configuration du projet
Tout d’abord, clonez notre projet existant, Kendo Store, mais au lieu de commencer par la branche maître, nous commençons à partir de la branche de point de départ store-angular-20-2-1
. Exécuter la commande git clone --branch store-angular-20-2-1 --single-branch https://gitlab.com/kendo-ui/kendo-angular-ssg.git
. Il clones le projet dans la branche spécifique.
Ensuite, déplacez-vous dans le kendo-angular-ssg
annuaire. Pour être sûr que nous travaillons dans le même état, exécutez la commande Git Branch – elle doit montrer le start-point-20 branche et courir npm i
pour installer toutes les dépendances.
dany@dany:~/lab$ git clone --branch start-point-20 --single-branch https://gitlab.com/kendo-ui/kendo-angular-ssg.git
Cloning into 'kendo-angular-ssg'...
remote: Enumerating objects: 176, done.
remote: Counting objects: 100% (176/176), done.
remote: Compressing objects: 100% (111/111), done.
remote: Total 176 (delta 95), reused 113 (delta 59), pack-reused 0 (from 0)
Receiving objects: 100% (176/176), 276.55 KiB | 2.51 MiB/s, done.
Resolving deltas: 100% (95/95), done.
dany@dany:~/lab$ cd kendo-angular-ssg/
dany@dany:~/lab/kendo-angular-ssg$ npm i
Nous sommes prêts, alors voyons notre projet existant en exécutant ng serve
dans le terminal.
dany@dany:~/lab/kendo-angular-ssg$ ng serve
Initial chunk files | Names | Raw size
main.js | main | 24.14 kB |
styles.css | styles | 96 bytes |
polyfills.js | polyfills | 95 bytes |
| Initial total | 24.33 kB
Application bundle generation complete. [1.457 seconds]
Watch mode enabled. Watching for file changes...
NOTE: Raw file sizes do not reflect development server per-request transformations.
➜ Local: http://localhost:4200/
➜ press h + enter to show help
Parfait! Notre première impression ressemble à une belle application qui fonctionne et a également deux pages de navigation. Mais allons approfondir le statut réel de ce projet.
CSR: rendu côté client
Par défaut, Angular utilise le rendu côté client (CSR). Cela signifie que le client ou le navigateur prend la responsabilité de créer l’intégralité de l’application, de sorte que le serveur enverra la quantité minimale de HTML et inclura le code JavaScript (bundle) et notre navigateur générera dynamiquement l’interface (Dom Elements).
Par exemple, voyons notre projet construit en utilisant CSR dans Angular. Aller à http:localhost:4200
et appuyez sur F12 (ou ⌘ ⌥ i
pour Mac) utiliser Devtools. Dans l’onglet réseau, effacez les journaux et rechargez la page. Puis cliquez sur le localhost
fichier et ouvrir l’onglet Aperçu. Vous verrez que le serveur ne renvoie qu’un fichier html très simple avec le <app-root>
composant.
En savoir plus sur Activité du réseau dans Chrome Devtools.
Que se passe-t-il lorsque le Crawler Google essaie de lire les informations sur notre magasin? Il n’obtient aucun produit. 🙁 Donc, cela signifie que pour Google, notre page ne fournit aucun produit.
Lorsque nous utilisons la RSE, il construit l’application dans le navigateur, mais après avoir chargé et construit le DOM, il fonctionne plus rapidement car nous avons tous les éléments du client et n’avons pas besoin de nouvelles demandes pour le serveur pour naviguer dans l’application. Mais le prix caché à payer est l’impact SEO.
Voyons comment notre application fonctionne avec Pharece qui nous aide à améliorer la qualité de nos applications en vérifiant les performances, l’accessibilité, le référencement et plus encore.
Ouvrez à nouveau le navigateur et cliquez sur l’onglet Lighthouse. Sélectionnez Desktop et les catégories pour les meilleures pratiques, le référencement et les performances et cliquez sur «Analyser le chargement de la page». 🤞
Oups. On dirait que la performance n’est pas assez bonne. 🙁 Pas de soucis! Nous allons l’améliorer en le combinant avec le pouvoir d’Angular. Faisons-le!
SSR et SSG: rendu côté serveur et génération de sites statiques
Après avoir compris la RSE, apprenons d’autres façons dont Angular fournit pour construire et servir nos applications. L’un d’eux est le rendu côté serveur (SSR) et la production de sites (SSG) de prétention ou statique.
SSR améliore la vitesse car au lieu que le client ait besoin de construire le HTML, il se charge sur le serveur à la demande. Mais qu’est-ce que cela signifie? Eh bien, pour chaque demande, le serveur créera notre application HTML et le renverra au client.
C’est bien car les Crawlers peuvent lire les informations et accélérer les performances, et le serveur obtient les nouvelles données et les renvoie au client. Mais l’impact est le coût pour le serveur. Que se passe-t-il si nous avons 1 000 utilisateurs? Il fait que notre serveur construit le HTML 1 000 fois, augmentant les coûts, ce qui n’est pas agréable pour le budget. ? Ouais.
Parfois, notre contenu est plus statique et ne change pas beaucoup, comme un blog ou peut-être notre magasin. Nous ne changeons pas le prix toutes les heures ou tous les jours.
Dans ce cas, nous pouvons utiliser la prétention, ou SSG.
Lorsque nous utilisons SSG, Angular construit la page complète du processus de construction, donc au lieu de créer l’application chaque fois qu’un utilisateur demande la page, la page est déjà prête, faisant des charges de page plus rapides. Le préressering crée des documents HTML au moment de la construction, et le serveur répond directement aux demandes avec le document HTML statique sans aucun travail supplémentaire.
Gardez à l’esprit que le prétention Exige que toutes les informations nécessaires pour rendre une page soient disponibles au moment de la construction. Il est donc parfait pour les pages qui sont les mêmes pour tous les utilisateurs de votre application.
Déménager à SSR avec prérendeur
Nous allons transformer notre projet existant de la RSE en SSR avec le prétention. L’équipe angulaire a créé un schéma incroyable pour nous en @angular/ssr
. Il faut la responsabilité d’installer, de créer des fichiers et de modifier les fichiers requis.
Ouvrir le terminal et courir ng add @angular/ssr
commande:
Remarque: vous pouvez construire un nouveau projet à partir de zéro
ng new --ssr
commande.
Après cela, il a apporté des modifications à tsconfig.json, app.config et package.json
Le ng add @angular/ssr
Commande schématique a ajouté automatiquement main.server.ts et server.ts à tsconfig.app.json
puisque SSR utilise le nœud avec Express pour ajouter les types de nœuds.
{
"extends": "./tsconfig.json",
"compilerOptions": {
"outDir": "./out-tsc/app",
"types": [
"node"
]
},
"files": [
"src/main.ts",
"src/server.ts",
"src/main.server.ts"
],
"include": [
"src/**/*.d.ts"
]
}
Le ng add @angular/ssr
Les schémas ont également automatiquement importé le provideClientHydration
et withEventReplay
fonctions dans le app.config
déposer.
import { ApplicationConfig } from '@angular/core';
import {provideRouter, withComponentInputBinding} from '@angular/router';
import { routes } from './app.routes';
import { provideHttpClient } from '@angular/common/http';
import { provideClientHydration, withEventReplay } from '@angular/platform-browser';
export const appConfig: ApplicationConfig = {
providers: [
provideRouter(routes, withComponentInputBinding()),
provideHttpClient(), provideClientHydration(withEventReplay())
]
};
Dans angular.json, le schéma a également changé la propriété Use Prerender en true, donc server.ts, combiné avec la puissance d’Express node.js, utilise AngularNodeAppEngine
Pour fournir notre application angulaire du serveur.
"prefix": "app",
"architect": {
"build": {
"builder": "@angular-devkit/build-angular:application",
"options": {
"outputPath": "dist/kendo-store",
"index": "src/index.html",
"browser": "src/main.ts",
"polyfills": [
"zone.js"
],
"tsConfig": "tsconfig.app.json",
"inlineStyleLanguage": "scss",
"assets": [
"src/favicon.ico",
"src/assets"
],
"styles": [
"src/styles.scss"
],
"scripts": [],
"server": "src/main.server.ts",
"outputMode": "server",
"ssr": {
"entry": "src/server.ts"
}
},
Il crée également le app.config.server ts
pour combiner le serverRendering()
et combiner le appConfig
avec serverConfig
.
import { mergeApplicationConfig, ApplicationConfig } from '@angular/core';
import { provideServerRendering, withRoutes } from '@angular/ssr';
import { appConfig } from './app.config';
import { serverRoutes } from './app.routes.server';
const serverConfig: ApplicationConfig = {
providers: [
provideServerRendering(withRoutes(serverRoutes))
]
};
export const config = mergeApplicationConfig(appConfig, serverConfig);
Nous avons tous les changements, alors construisons notre application (et traversons nos doigts)! 🤞
Construire SSR et PREENDER
Avant de commencer, Si vous utilisez une version Angular 20.0.2 ou plusvous pouvez rencontrer l’erreur suivante après avoir exécuté la commande de construction ng build
. Ce problème a été résolu dans la dernière version problème.
Vous pouvez résoudre ce problème en important le main.server.ts
dans le tsconfig.app.json.
{
"extends": "./tsconfig.json",
"compilerOptions": {
"outDir": "./out-tsc/app",
"types": [
"node"
]
},
"files": [
"src/main.ts",
"src/server.ts",
"src/main.server.ts"
],
"include": [
"src/**/*.d.ts"
]
}
Si vous utilisez la dernière version d’Angular ou apportez les modifications précédentes, exécutez le ng build
Commande à nouveau dans le terminal.
Oups. Il a construit le projet mais a montré une erreur! L’itinéraire product-detail/:id
a besoin de ses informations, vous vous souvenez-vous: vous vous souvenez: La prétention exige que toutes les informations nécessaires pour rendre une page soient disponibles au moment de la construction.
Nous devons fournir cela. Il est temps d’écrire un peu de code!
Le routeur avec des paramètres
Ouvrir le app.routes.server.ts
déposer. Il est responsable de la gestion des pages à prétendre. Dans notre cas, nous devons spécifier le chemin 'product-detail/:id'
et indique qu’il a besoin du id
paramètre en fournissant le getPrerenderParams
fonction.
Enregistrer l’itinéraire 'product-detail/:id'
Réglez le renderMode
à RenderMode.Prerender
et définissez le getPrerenderParams
Fonctionne comme un paramètre. Dans cette fonction, nous appelons le service pour récupérer tous les ID de produit.
{
path: 'product-detail/:id',
renderMode: RenderMode.Prerender,
async getPrerenderParams() {
const productService = inject(ProductsService);
const productIds = toSignal(productService.products$,{ initialValue: []} );
return productIds().map(product => ({
id: product.id
}));
}
}
Le code final ressemble:
import { RenderMode, ServerRoute } from '@angular/ssr';
import {ProductsService} from "./services/products.service";
import {inject} from "@angular/core";
import {toSignal} from "@angular/core/rxjs-interop";
export const serverRoutes: ServerRoute[] = [
{
path: '**',
renderMode: RenderMode.Prerender
},
{
path: 'product-detail/:id',
renderMode: RenderMode.Prerender,
async getPrerenderParams() {
const productService = inject(ProductsService);
const productIds = toSignal(productService.products$,{ initialValue: []} );
return productIds().map(product => ({
id: product.id
}));
}
}
];
Parfait, enregistrez les modifications! Et créez l’application en exécutant ng build
:
Parce que le schéma a créé un nouveau script NPM pour exécuter la version SSR de notre application, il est temps d’exécuter l’application et de voir les résultats en exécutant npm run serve:ssr:kendo-store
dans le terminal. Cela sert l’application sur un autre port, 4000.
En savoir plus sur GetPrerender Params.
Ouvrez le navigateur et accédez à http: // localhost: 4000. Notre application fonctionne !! Ouais! Mais voyons les performances, ouvrez les Devtools, cliquez dans l’onglet Lighthouse et appuyez sur le bouton de chargement de la page Analyser.
Peut-être que dans le premier rechargement est inférieur à 80, car Angular n’a pas construit et caché les fichiers, réessayez (cela dépend également de votre machine, de votre connexion et de vos extensions). Mais exécutez à nouveau le test et Tada !!
Nous avons 😍 !!! 94 !!! Super!!! De 74 à 94, nous avons augmenté la vitesse de 20%! Seulement en ajoutant du prétention à notre application !!! Ouais!!!
Hmm, mais cela pourrait-il être encore mieux?
Utilisation de la puissance angulaire avec NgoptimizedImage
Nous voulons être plus rapides, et Angular nous donne le NgOptimizedImage
Directive pour améliorer les images. Ajoutons-le aux pages du produit et des détails.
Ouvrir products.component.ts
et importer le NgOptimizedImage
directive dans le imports
section.
import {Component, inject} from '@angular/core';
import {ProductsService} from "../../services/products.service";
import {AsyncPipe, NgOptimizedImage} from "@angular/common";
import {RouterLink} from "@angular/router";
@Component({
selector: 'app-products',
imports: [AsyncPipe, RouterLink, NgOptimizedImage],
templateUrl: './products.component.html',
styleUrl: './products.component.scss'
})
export class ProductsComponent {
public products$ = inject(ProductsService).products$;
}
Ouvrez le modèle products.component.html
et mettre à jour l’image avec le ngSrc
directif; Il nécessite une largeur et une hauteur.
<img
[ngSrc]="product.image"
width="164"
height="164"
[alt]="product.title"
class="rounded-lg object-contain h-64 w-64 shadow"
/>
La version finale de ProductS.component.html ressemble à:
@if (products$ | async; as products) {
<div class="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 gap-8">
@for(product of products; track product) {
<div [routerLink]="['product-detail', product.id]" class="bg-white border border-gray-200 rounded-xl shadow hover:shadow-xl transition-shadow p-6 flex flex-col items-center">
<img
[ngSrc]="product.image"
width="164"
height="164"
[alt]="product.title"
class="rounded-lg object-contain h-64 w-64 shadow"
/>
<h3 class="text-lg font-semibold mb-2 text-gray-800 text-center line-clamp-2">{{product.title}}</h3>
<span class="text-green-600 font-bold text-2xl mb-2">${{product.price}}</span>
</div>
}
</div>
}
Répéter les mêmes étapes dans le products-details.component
Importation du NgOptimizedImage
.
import {Component, inject, input} from '@angular/core';
import {ProductsService} from "../../services/products.service";
import {toObservable} from "@angular/core/rxjs-interop";
import {switchMap} from "rxjs";
import {AsyncPipe, NgOptimizedImage} from "@angular/common";
@Component({
selector: 'app-product-details',
imports: [
AsyncPipe,
NgOptimizedImage
],
templateUrl: './product-details.component.html',
styleUrl: './product-details.component.scss',
standalone: true,
})
export class ProductDetailsComponent {
#productService = inject(ProductsService);
id = input.required<string>();
product$ = toObservable(this.id).pipe(
switchMap(productId => this.#productService.productById(productId))
)
}
Mettre à jour le product-details.component.html
en utilisant ngSrc
Directive et ajout de largeur et de hauteur.
<img
[ngSrc]="product.image"
width="64"
height="64"
[alt]="product.title"
class="rounded-lg object-contain h-64 w-64 shadow"
/>
La version finale de product-details.component.html
On dirait:
<div class="min-h-screen bg-gray-50 font-sans flex items-start justify-center py-12 px-4">
@if(product$ |async; as product){
<div class="bg-white rounded-2xl shadow-lg max-w-3xl w-full flex flex-col md:flex-row overflow-hidden">
<div class="md:w-1/2 flex items-center justify-center bg-gray-100 p-8">
<img
[ngSrc]="product.image"
width="64"
height="64"
[alt]="product.title"
class="rounded-lg object-contain h-64 w-64 shadow"
/>
</div>
<div class="md:w-1/2 p-8 flex flex-col justify-center">
<h1 class="text-2xl md:text-3xl font-bold text-gray-900 mb-4">{{ product.title }}</h1>
<span class="text-green-600 font-extrabold text-2xl mb-4 block">${{ product.price }}</span>
<p class="text-gray-700 mb-6">
{{ product.description || 'No description available.' }}
</p>
<button
class="bg-blue-600 hover:bg-blue-700 text-white font-semibold py-3 px-6 rounded-lg shadow transition-colors w-full md:w-auto"
>
Add to Cart
</button>
</div>
</div>
}
</div>
Enregistrez les modifications et exécutez le phare. Tada! 🎉 Nous obtenons 97% sur la page d’accueil et 99% sur les pages de détails!
C’est incroyable! Nous avons obtenu des points supplémentaires sur le score en combinant SSR avec le Optimisation directive et a obtenu un meilleur résultat!
Apprenez plus de trucs avec Optimiser la directive.
Résumer
Ce fut un grand voyage, passant d’un site Web lent avec un score faible qui a eu un impact sur les performances de l’utilisation du rendu côté client (RSE), puis de passer à la SSR combinée à la prétention (SSG), ce qui rend notre site Web rapide. Enfin, nous l’avons rendu encore plus rapide en le combinant avec la directive NGoptimizeDImage pour améliorer les images, obtenir des données d’une API et envoyer des données entre les itinéraires.
J’espère que cet article vous aidera à améliorer les performances et la vitesse des projets existants ou commencer à jouer avec SSR et à prétendre dans Angular 20! 👍🏼
Nous avons construit ce projet avec l’interface utilisateur de Kendo pour Bibliothèque de composants angulaires Du progrès, ce qui en a fait un cliché à réunir. La bibliothèque de l’interface utilisateur est livrée avec un essai gratuit de 30 jours, alors essayez-le:
Source link