Razzle: Wie behebe ich Server-Rendering-Fehler?

Erstellt am 5. Feb. 2019  ·  9Kommentare  ·  Quelle: jaredpalmer/razzle

Hallo,

Wir haben ein Problem festgestellt, bei dem die Website als leere Seite gerendert wird, wenn Sie sich bei der Google-Suchkonsole anmelden und als Google abrufen.

Ich habe einige Nachforschungen angestellt und einige davon haben mich zu diesem Artikel geführt:

https://medium.com/@gajus/react -application-seen-as-a-blank-page-via-fetch-as-google-afb11dff8562

Es ist sehr wahrscheinlich, dass die Suchmaschine und das Abrufen wie Google selbst sich nicht so verhalten, sonst hätten wir inzwischen Rankings verloren, trotzdem möchte ich dieses Problem lösen, da es jeden Website-Besitzer wach halten würde.

Ich habe Phantomjs heruntergeladen, unter der Annahme, dass es mit fetch identisch ist wie bei Google, und der Versuch, es zu rendern, gibt eine Reihe von Fehlern zurück:

image

Dieses Problem kann so einfach zu beheben sein wie das Ändern der Zielbrowser, die kompiliert werden, aber das einfache Installieren von babel/polyfil und das Importieren funktioniert nicht.

Kann mir hier jemand den richtigen Weg weisen?

stale

Hilfreichster Kommentar

Mein Vorschlag (und vielleicht brauchen wir dazu eine Anleitung zur Fehlerbehebung in den Dokumenten) ist, so viel wie möglich zu kommentieren von server.js , client.js , App.js und möglicherweise Aspekte Ihrer Webpack-Überschreibungen (sofern vorhanden), um Ihre Razzle-Anwendung wieder in einen "Hallo Welt"-ähnlichen Zustand zu versetzen. Ich würde auch vorschlagen, die HTML-Vorlage von Razzle zurück in Ihre server.js Datei zu kopieren, nur um sicherzustellen, dass alle Skripte ordnungsgemäß geladen werden. Sobald es ausgeführt wird, öffnen Sie die Chrome-Entwicklungstools und gehen Sie zum Tab "Leistung", aktivieren Sie das Kontrollkästchen "Screenshots" und klicken Sie dann auf das Symbol "Aktualisieren". Siehe das GIF unten.

kapture 2019-02-05 at 8 33 42

Das Ziel besteht darin, sicherzustellen, dass sich im Timeline-Bereich kein leerer weißer Rahmen befindet. Nachdem Sie ein ordnungsgemäßes Rendering ohne Flashs erhalten haben, sollten Sie immer mehr von Ihrem alten Code hinzufügen. Mache Babyschritte. Dies ist ziemlich leicht zu vermasseln, wenn Sie nicht aufpassen.

Was Ihren speziellen Fall betrifft, würde ich damit beginnen, all das Google-Tag-Manager-Zeug, Ihr gesamtes CSS sowie das gesamte Code-Splitting, das Sie durchführen, zu entfernen. Lassen Sie es einfach rendern und rückwärts arbeiten. Als nächstes bringen Sie Redux zum Laufen. Fügen Sie dann den React-Helm wieder hinzu, und wenn Sie glauben, dass Sie es richtig gemacht haben, überprüfen Sie es noch einmal, indem Sie auch einen Lighthouse-Test durchführen (Registerkarte Audits). Übergeben Sie diesen Status an einen neuen Branch, damit Sie ihn in Zukunft wieder aufrufen können. Fügen Sie als Nächstes Ihre Analyseskripts zurück und überprüfen Sie das Rendering erneut. Versuchen Sie schließlich, Code-Splitting hinzuzufügen.

Hinweis: Wenn Sie Ihr CSS behalten und die obige Analyse mit Razzle im Entwicklungsmodus durchführen, erhalten Sie möglicherweise ein FOUC, da Razzle während der Entwicklung keine .css Stile auf dem Server verarbeitet (nur auf dem Client). Um vollständig zu testen, ob Sie bei Verwendung von .css Dateien ordnungsgemäß SSR mit Style-Hydratation ausführen, sollten Sie Razzle für die Produktion erstellen und den Produktionsserver lokal ausführen. Wenn Sie jedoch lediglich überprüfen, ob Ihr HTML-Code vom Server vorhanden ist, können Sie dies im Entwicklungsmodus ausführen.

Alle 9 Kommentare

Wenn Ihre Seite leer ist, haben Sie Ihr SSR- oder Trinkschema gebrochen. Razzle leidet nicht unter dem, was Sie out-of-the-box beschrieben haben, tatsächlich wird es auf SEO-orientierten Websites wie BBC.com verwendet. kannst du deine server.js-Datei einfügen? Holen Sie Ihre Daten vom Server ab?

Hallo Jared,

Danke für die prompte Antwort, ja, wir holen Daten von einem Server.

Hier ist mein server.js

import App from './App';
import React from 'react';
import Helmet from 'react-helmet';
import { StaticRouter } from 'react-router-dom';
import { Provider } from 'react-redux';
import express from 'express';
import compression from 'compression';
import { renderToString } from 'react-dom/server';

import configureStore from './store/configureStore';
import { remoteLoader } from './api/remoteLoader';
import serialize from 'serialize-javascript';

import { Capture } from 'react-loadable';
import { getBundles } from 'react-loadable/webpack';
import stats from '../build/react-loadable.json';

import { IS_PRODUCTION } from './components/shared/constants';

const assets = require(process.env.RAZZLE_ASSETS_MANIFEST);

const server = express();
server.use(compression());

if (!IS_PRODUCTION) {
    server.set('cache', false);
}

server
    .disable('x-powered-by')
    .use(express.static(process.env.RAZZLE_PUBLIC_DIR, { Expires: '30d' }))
    .get('/*', (req, res) => {
        //Ensures clients with old css paths are served the current file
        if (req.path.indexOf('/static/css') > -1 && assets.client.css) {
            const currentCssFile = `${process.env.RAZZLE_PUBLIC_DIR}${assets.client.css}`;
            return res.sendFile(currentCssFile);
        }

        remoteLoader(apiResult => {
            const responseCode = typeof apiResult.status === 'undefined' ? 404 : apiResult.status;

            if (responseCode === 301) {
                return res.redirect(responseCode, apiResult.headers.location);
            }

            // Compile an initial state
            const initialState = {
                remote: {
                    cms: {
                        result: apiResult ? apiResult.data : false,
                        loading: false
                    },
                    myDrewberry: {
                        searchResults: null,
                        loading: false,
                        failed: false
                    }
                }
            };
            // Create a new Redux store instance
            const store = configureStore(initialState);

            const context = {};
            const modules = [];
            const markup = renderToString(
                <Capture report={moduleName => modules.push(moduleName)}>
                    <StaticRouter context={context} location={req.url}>
                        <Provider store={store}>
                            <App />
                        </Provider>
                    </StaticRouter>
                </Capture>
            );
            const helmet = Helmet.renderStatic();

            if (context.url) {
                res.redirect(context.url);
            } else {
                const bundles = getBundles(stats, modules);
                const chunks = bundles.filter(bundle => bundle.file.endsWith('.js'));

                res.status(responseCode).send(
                    `<!doctype html>
                    <html ${helmet.htmlAttributes.toString()}>
                    <head>

                        ${assets.client.css ? `<link rel="stylesheet" href="${assets.client.css}">` : ''}
                        ${helmet.title.toString()}
                        ${helmet.meta.toString()}
                        ${helmet.link.toString()}
                        <link rel="icon" type="image/png" href="/favicon16.png" sizes="16x16"/>
                        <link rel="icon" type="image/png" href="/favicon32.png" sizes="32x32"/>
                        <link rel="icon" type="image/png" href="/favicon96.png" sizes="96x96"/>
                        <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=0"/> <!--320-->
                        <meta http-equiv="expires" content="0">
                    </head>
                    <body class="drewberry-preload">

                        <!-- Google Tag Manager -->
                            <script>(function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start':
                            new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0],
                            j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src=
                            'https://www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f);
                            })(window,document,'script','dataLayer','GTM-TKPXWB');</script>
                        <!-- End Google Tag Manager -->

                        <div id="root">${markup}</div>
                        <script>
                            window.__PRELOADED_STATE__ = ${serialize(initialState)}
                        </script>
                        ${
                            IS_PRODUCTION
                                ? `<script src="${assets.client.js}"></script>`
                                : `<script src="${assets.client.js}" crossorigin></script>`
                        }
                          ${chunks
                              .map(chunk =>
                                  IS_PRODUCTION
                                      ? `<script src="/${chunk.file}"></script>`
                                      : `<script src="http://${process.env.HOST}:${parseInt(process.env.PORT, 10) + 1}/${
                                            chunk.file
                                        }"></script>`
                              )
                              .join('\n')}
                          <script>window.main();</script>
                    </body>
                </html>`
                );
            }
        }, req.path);
    });

export default server;

Ich kann bestätigen, dass Razzle nicht sofort darunter leidet, nur eine saubere Installation geladen und mit phantomjs keine der Warnungen angezeigt wurde, was bedeutet, dass es sich wahrscheinlich um eine Bibliothek handelt, die wir installiert haben.

Sehen Sie oben etwas Offensichtliches?

Im Moment fühlt es sich an wie eine wilde Gänsejagd! :-)

Mein Vorschlag (und vielleicht brauchen wir dazu eine Anleitung zur Fehlerbehebung in den Dokumenten) ist, so viel wie möglich zu kommentieren von server.js , client.js , App.js und möglicherweise Aspekte Ihrer Webpack-Überschreibungen (sofern vorhanden), um Ihre Razzle-Anwendung wieder in einen "Hallo Welt"-ähnlichen Zustand zu versetzen. Ich würde auch vorschlagen, die HTML-Vorlage von Razzle zurück in Ihre server.js Datei zu kopieren, nur um sicherzustellen, dass alle Skripte ordnungsgemäß geladen werden. Sobald es ausgeführt wird, öffnen Sie die Chrome-Entwicklungstools und gehen Sie zum Tab "Leistung", aktivieren Sie das Kontrollkästchen "Screenshots" und klicken Sie dann auf das Symbol "Aktualisieren". Siehe das GIF unten.

kapture 2019-02-05 at 8 33 42

Das Ziel besteht darin, sicherzustellen, dass sich im Timeline-Bereich kein leerer weißer Rahmen befindet. Nachdem Sie ein ordnungsgemäßes Rendering ohne Flashs erhalten haben, sollten Sie immer mehr von Ihrem alten Code hinzufügen. Mache Babyschritte. Dies ist ziemlich leicht zu vermasseln, wenn Sie nicht aufpassen.

Was Ihren speziellen Fall betrifft, würde ich damit beginnen, all das Google-Tag-Manager-Zeug, Ihr gesamtes CSS sowie das gesamte Code-Splitting, das Sie durchführen, zu entfernen. Lassen Sie es einfach rendern und rückwärts arbeiten. Als nächstes bringen Sie Redux zum Laufen. Fügen Sie dann den React-Helm wieder hinzu, und wenn Sie glauben, dass Sie es richtig gemacht haben, überprüfen Sie es noch einmal, indem Sie auch einen Lighthouse-Test durchführen (Registerkarte Audits). Übergeben Sie diesen Status an einen neuen Branch, damit Sie ihn in Zukunft wieder aufrufen können. Fügen Sie als Nächstes Ihre Analyseskripts zurück und überprüfen Sie das Rendering erneut. Versuchen Sie schließlich, Code-Splitting hinzuzufügen.

Hinweis: Wenn Sie Ihr CSS behalten und die obige Analyse mit Razzle im Entwicklungsmodus durchführen, erhalten Sie möglicherweise ein FOUC, da Razzle während der Entwicklung keine .css Stile auf dem Server verarbeitet (nur auf dem Client). Um vollständig zu testen, ob Sie bei Verwendung von .css Dateien ordnungsgemäß SSR mit Style-Hydratation ausführen, sollten Sie Razzle für die Produktion erstellen und den Produktionsserver lokal ausführen. Wenn Sie jedoch lediglich überprüfen, ob Ihr HTML-Code vom Server vorhanden ist, können Sie dies im Entwicklungsmodus ausführen.

Ok, danke für die tolle Arbeit mit Razzle und jetzt dafür. Ich werde es gut prüfen und berichten.

Natürlich ist das erste, was Sie auf jeder "leeren" (nicht leeren, das ist ein Fehler) SEO-Seite überprüfen müssen: Ist Ihre Seite von componentDidMount abhängig, um die anzuzeigenden Daten zu laden. Sie können curl , um schnell zu überprüfen. Wenn ja, benötigen Sie etwas wie After.js zum Vorladen.

Könnten Sie Ratschläge zum Debuggen mit der Registerkarte "Leistung" geben, wenn eine leere Seite um die 1000-ms-Marke herum vorhanden ist? Ich weiß, dass mein Problem darin besteht, dass eine Symbolschriftbibliothek nicht auf dem Server geladen wird und daher diese leeren leeren Quadrate beim ersten Laden angezeigt werden ...

screen shot 2019-02-11 at 9 57 30 am

Ah, alle meine benutzerdefinierten Schriftarten werden erst viel später geladen. Ich lade meine Schriftarten über CSS' @font-face {}.

@font-face {
  font-family: 'SFProText';
  font-display: auto;
  src: url('fonts/SFPro/SF-Pro-Display-Regular.otf') format('truetype');
  font-weight: normal;
  font-style: normal;
}

@jaredpalmer Ich habe auch das gleiche Problem. Schriftarten und Assets werden nicht auf meiner Kundenseite geladen.

Ich habe keine webpack.config-Datei in meinem Anwendungs-Setup hinzugefügt, die ich aus Ihrem Repository geklont habe. Ich habe gerade die Kopie aus Ihrem Repo genommen und npm-Module installiert, dann habe ich mit der Verwendung der Anwendung begonnen, indem ich einen npm-Startbefehl ausgeführt habe.
Habe ich etwas vergessen zu konfigurieren oder muss meine neue Webpack-Datei verwenden, um dieses Problem zu lösen?

Bitte helfen Sie mir, dieses Problem zu lösen.

Meine App.css-Datei sieht so aus..
@Schriftart {
Schriftfamilie: 'Roboto-Medium';
src: url("./static/fonts/Roboto-Medium-webfont.eot");
src: url("./static/fonts/Roboto-Medium-webfont.eot?#iefix") format("embedded-opentype"), url(./static/fonts/Roboto-Medium-webfont.woff") format("woff"), url("./static/fonts/Roboto-Medium-webfont.ttf") format("truetype"); }

@Schriftart {
Schriftfamilie: 'Roboto-regulär';
src: url("./static/fonts/roboto-regular-webfont.eot");
src: url("./static/fonts/roboto-regular-webfont.eot?#iefix") format("embedded-opentype"), url(./static/fonts/roboto-regular-webfont.woff") format("woff"), url("./static/fonts/roboto-regular-webfont.ttf") format("truetype"); }

danke im voraus :)

War diese Seite hilfreich?
0 / 5 - 0 Bewertungen

Verwandte Themen

knipferrc picture knipferrc  ·  5Kommentare

charlie632 picture charlie632  ·  4Kommentare

dizzyn picture dizzyn  ·  3Kommentare

corydeppen picture corydeppen  ·  3Kommentare

alexjoyner picture alexjoyner  ·  3Kommentare