浏览代码

affichage rendu SSR

DESKTOP-SMCIPAV\falko 5 月之前
父节点
当前提交
89f382d355

+ 5 - 5
angular-client/angular.json

@@ -27,12 +27,12 @@
               "src/favicon.ico",
               "src/assets",
               "src/manifest.webmanifest",
-              "src/robots.txt",      
-              "src/sitemap.xml"      
+              "src/robots.txt",
+              "src/sitemap.xml"
             ],
             "styles": [
-              "./node_modules/@angular/material/prebuilt-themes/indigo-pink.css",
-               "./node_modules/leaflet/dist/leaflet.css",
+              "node_modules/@angular/material/prebuilt-themes/indigo-pink.css",
+              "node_modules/leaflet/dist/leaflet.css",
               "src/styles.scss"
             ],
             "scripts": []
@@ -108,7 +108,7 @@
             "browserTarget": "fatboar:build:production",
             "serverTarget": "fatboar:server:production",
             "routes": [
-              "/",                   
+              "/",
               "/concours",
               "/menus",
               "/contact"

文件差异内容过多而无法显示
+ 1 - 1
angular-client/dist/fatboar/server/main.js


+ 15 - 0
angular-client/package-lock.json

@@ -11481,6 +11481,21 @@
       "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==",
       "dev": true
     },
+    "preboot": {
+      "version": "8.0.0",
+      "resolved": "https://registry.npmjs.org/preboot/-/preboot-8.0.0.tgz",
+      "integrity": "sha512-unyVkACJDK9Y1Y2gJpKef9z+d/w0XRbVRU493Nbc38E5QaTCJuZUmsosVjXxILHVq0r/HLJzHxIDRj7omKJeZA==",
+      "requires": {
+        "tslib": "^2.0.0"
+      },
+      "dependencies": {
+        "tslib": {
+          "version": "2.8.1",
+          "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
+          "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="
+        }
+      }
+    },
     "prepend-http": {
       "version": "1.0.4",
       "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-1.0.4.tgz",

+ 1 - 0
angular-client/package.json

@@ -40,6 +40,7 @@
     "karma-phantomjs-launcher": "^1.0.4",
     "leaflet": "^1.6.0",
     "ng2-charts": "^2.3.0",
+    "preboot": "^8.0.0",
     "rxjs": "~6.5.4",
     "saturn-datepicker": "^8.0.5",
     "tslib": "^1.10.0",

+ 34 - 97
angular-client/server.ts

@@ -4,29 +4,25 @@ import * as express from 'express';
 import { join } from 'path';
 import { AppServerModule } from './src/main.server';
 import { APP_BASE_HREF } from '@angular/common';
-import { existsSync } from 'fs';
+import { existsSync, readFileSync } from 'fs';
 
-// Pour simuler le DOM côté serveur (utile pour les librairies qui accèdent à window/document)
+// Simulation DOM (nécessaire pour certaines librairies côté serveur)
 const domino = require('domino');
-const fs = require('fs');
-const path = require('path');
-
-// Dossiers de build
 const distFolder = join(process.cwd(), 'dist/fatboar/browser');
 const indexHtml = existsSync(join(distFolder, 'index.html')) ? 'index.html' : 'index';
-const template = fs.readFileSync(join(distFolder, indexHtml)).toString();
+const template = readFileSync(join(distFolder, indexHtml)).toString();
 
-// Simule le DOM pour SSR
+// Simuler un DOM global pour les libs (Leaflet, Material, etc.)
 const win = domino.createWindow(template);
 global['window'] = win;
 global['document'] = win.document;
 global['navigator'] = win.navigator;
-global['CSS'] = null; // Certaines libs vérifient CSS
+global['CSS'] = null;
 
 export function app() {
   const server = express();
 
-  // Configure le moteur de rendu Angular Universal
+  // Moteur de rendu Angular Universal
   server.engine('html', ngExpressEngine({
     bootstrap: AppServerModule,
   }));
@@ -34,31 +30,50 @@ export function app() {
   server.set('view engine', 'html');
   server.set('views', distFolder);
 
-  // Sert les fichiers statiques (assets, images, etc.)
-  server.get('*.*', express.static(distFolder, {
-    maxAge: '1y'
-  }));
+  // Fichiers statiques (CSS, JS, images…)
+  server.get('*.*', express.static(distFolder, { maxAge: '1y' }));
 
-  // Sert sitemap.xml pour le SEO
+  // Routes SEO
   server.get('/sitemap.xml', (req, res) => {
     res.header('Content-Type', 'application/xml');
     res.sendFile(join(distFolder, 'sitemap.xml'));
   });
 
-  // Sert robots.txt pour le SEO
   server.get('/robots.txt', (req, res) => {
     res.sendFile(join(distFolder, 'robots.txt'));
   });
 
-  // Sert toutes les autres routes via Angular Universal
+  // Toutes les autres routes => rendu SSR
   server.get('*', (req, res) => {
     res.render(indexHtml, {
       req,
       providers: [
         { provide: APP_BASE_HREF, useValue: req.baseUrl },
-        // Fournit l'URL canonique pour les balises <link rel="canonical">
         { provide: 'CANONICAL_URL', useValue: `https://angular-dev.foodgame.fr${req.originalUrl}` }
       ]
+    }, (err, html) => {
+      if (err) {
+        console.error(err);
+        return res.status(500).send(err);
+      }
+
+      // Injection Preboot pour capturer les interactions avant bootstrap Angular
+      const prebootScript = `
+        <script src="https://unpkg.com/preboot"></script>
+        <script>preboot.init({ appRoot: 'app-root' });</script>
+      `;
+
+      // Injection des CSS directement dans le SSR pour éviter le flash
+      const styles = `
+        <link rel="stylesheet" href="styles.css">
+        <link rel="stylesheet" href="assets/theme.css">
+      `;
+
+      res.send(
+        html
+          .replace('</head>', `${styles}</head>`)
+          .replace('</body>', `${prebootScript}</body>`)
+      );
     });
   });
 
@@ -73,7 +88,7 @@ function run() {
   });
 }
 
-// Démarre le serveur si ce fichier est lancé directement
+// Exécuter le serveur si lancé directement
 declare const __non_webpack_require__: NodeRequire;
 const mainModule = __non_webpack_require__.main;
 const moduleFilename = mainModule && mainModule.filename || '';
@@ -82,81 +97,3 @@ if (moduleFilename === __filename || moduleFilename.includes('iisnode')) {
 }
 
 export * from './src/main.server';
-
-// import 'zone.js/dist/zone-node';
-// import { ngExpressEngine } from '@nguniversal/express-engine';
-// import * as express from 'express';
-// import { join } from 'path';
-// import { AppServerModule } from './src/main.server';
-// import { APP_BASE_HREF } from '@angular/common';
-// import { existsSync } from 'fs';
-
-// const domino = require('domino');
-// const fs = require('fs');
-// const path = require('path');
-
-// const distFolder = join(process.cwd(), 'dist/fatboar/browser');
-// const indexHtml = existsSync(join(distFolder, 'index.html')) ? 'index.html' : 'index';
-// const template = fs.readFileSync(join(distFolder, indexHtml)).toString();
-
-// const win = domino.createWindow(template);
-// global['window'] = win;
-// global['document'] = win.document;
-// global['navigator'] = win.navigator;
-// global['CSS'] = null;
-
-// export function app() {
-//   const server = express();
-
-//   server.engine('html', ngExpressEngine({
-//     bootstrap: AppServerModule,
-//   }));
-
-//   server.set('view engine', 'html');
-//   server.set('views', distFolder);
-
-//   // Fichiers statiques
-//   server.get('*.*', express.static(distFolder, {
-//     maxAge: '1y'
-//   }));
-
-//   // Routes SEO (déplacées depuis Express)
-//   server.get('/sitemap.xml', (req, res) => {
-//     res.header('Content-Type', 'application/xml');
-//     res.sendFile(join(distFolder, 'sitemap.xml'));
-//   });
-
-//   server.get('/robots.txt', (req, res) => {
-//     res.sendFile(join(distFolder, 'robots.txt'));
-//   });
-
-//   // Toutes les autres routes
-//   server.get('*', (req, res) => {
-//     res.render(indexHtml, {
-//       req,
-//       providers: [
-//         { provide: APP_BASE_HREF, useValue: req.baseUrl },
-//         { provide: 'CANONICAL_URL', useValue: `https://angular-dev.foodgame.fr${req.originalUrl}` }
-//       ]
-//     });
-//   });
-
-//   return server;
-// }
-
-// function run() {
-//   const port = process.env.PORT || 4000;
-//   const server = app();
-//   server.listen(port, () => {
-//     console.log(`Node Express server listening on http://localhost:${port}`);
-//   });
-// }
-
-// declare const __non_webpack_require__: NodeRequire;
-// const mainModule = __non_webpack_require__.main;
-// const moduleFilename = mainModule && mainModule.filename || '';
-// if (moduleFilename === __filename || moduleFilename.includes('iisnode')) {
-//   run();
-// }
-
-// export * from './src/main.server';

+ 9 - 3
angular-client/src/app/app.server.module.ts

@@ -1,6 +1,7 @@
 import { NgModule } from '@angular/core';
-import { ServerModule } from '@angular/platform-server';
+import { ServerModule, ServerTransferStateModule } from '@angular/platform-server';
 import { FlexLayoutServerModule } from '@angular/flex-layout/server';
+import { PrebootModule } from 'preboot';
 
 import { AppModule } from './app.module';
 import { AppComponent } from './app.component';
@@ -9,8 +10,13 @@ import { AppComponent } from './app.component';
   imports: [
     AppModule,
     ServerModule,
-    FlexLayoutServerModule
+    FlexLayoutServerModule,
+    ServerTransferStateModule, // transfère les styles et données pour éviter le flash
+    PrebootModule.withConfig({
+      appRoot: 'app-root',
+      replay: true
+    })
   ],
   bootstrap: [AppComponent]
 })
-export class AppServerModule {}
+export class AppServerModule {}