Razzle: Как мне устранить неполадки серверного рендеринга?

Созданный на 5 февр. 2019  ·  9Комментарии  ·  Источник: jaredpalmer/razzle

Привет,

Мы заметили проблему, при которой, если вы входите в консоль поиска Google и получаете данные от имени Google, веб-сайт отображается как пустая страница.

Я провел небольшое исследование, и некоторые из них привели меня к этой статье:

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

Скорее всего, поисковая система и сама поисковая система Google не ведут себя одинаково, иначе мы бы потеряли рейтинг к настоящему времени, тем не менее, я хотел бы решить эту проблему, так как это не даст уснуть любому владельцу веб-сайта.

Я загрузил phantomjs, предполагая, что он такой же, как fetch, как Google, и попытка визуализации возвращает ряд ошибок:

image

Эту проблему может быть так же просто решить, как изменить компилируемые целевые браузеры, однако простая установка babel / polyfil и импорт не работают.

Сможет ли кто-нибудь указать мне здесь правильное направление?

Самый полезный комментарий

Поэтому мое предложение (и, возможно, нам понадобится руководство по устранению неполадок в документации по этому поводу) - прокомментировать как можно больше из server.js , client.js , App.js и, возможно, некоторые аспекты вашего веб-пакета переопределяют (если они у вас есть), чтобы вернуть ваше приложение Razzle в состояние, подобное "привет мир". Я также предлагаю скопировать HTML-шаблон Razzle обратно в ваш файл server.js чтобы убедиться, что все сценарии загружаются правильно из коробки. После запуска откройте инструменты разработчика Chrome и перейдите на вкладку «Производительность», установите флажок «Снимки экрана» и нажмите значок «Обновить». Смотрите гифку ниже.

kapture 2019-02-05 at 8 33 42

Цель состоит в том, чтобы убедиться, что в области временной шкалы нет пустых белых рамок. После того, как вы получите правильный рендер без вспышек, вам следует добавлять все больше и больше вашего старого кода. Делайте маленькие шажки. Это довольно легко испортить, если не проявить осторожность.

Что касается вашего конкретного случая, я бы начал с удаления всего этого диспетчера тегов Google, всего вашего CSS, а также любого разделения кода, которое вы делаете. Просто сделайте рендеринг и работайте в обратном направлении. Затем запустите Redux. Затем добавьте обратно реактивный шлем, а затем, как только вы решите, что все правильно, дважды проверьте, выполнив также тест Lighthouse (вкладка Audits). Зафиксируйте это состояние в новой ветке, чтобы вы могли вернуться к нему в будущем. Затем добавьте обратно свои сценарии аналитики и снова проверьте рендеринг. Наконец, попробуйте добавить разделение кода обратно.

Примечание. Если вы сохраните свой CSS и выполните вышеуказанный анализ с помощью Razzle в режиме разработки, вы можете получить FOUC, потому что Razzle не обрабатывает стили .css на сервере во время разработки (только на клиенте). Таким образом, чтобы полностью проверить, правильно ли вы выполняете SSR с гидратацией стилей, если вы используете файлы .css , вам следует собрать Razzle для производства и запустить рабочий сервер локально. Однако, если вы просто проверяете наличие вашего HTML с сервера, вы можете запустить это в режиме разработки.

Все 9 Комментарий

Если ваша страница загружается пустой, значит, вы нарушили SSR или схему гидратации. Razzle не страдает от того, что вы описали из коробки, на самом деле, он используется на сайтах, ориентированных на SEO, таких как BBC.com. вы можете вставить свой файл server.js? Вы загружаете свои данные на сервер?

Привет, Джаред,

Спасибо за быстрый ответ, да, мы получаем данные с сервера.

Вот мой 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;

Я могу подтвердить, что Razzle из коробки не страдает от этого, просто загрузил чистую установку и с использованием phantomjs ни одно из предупреждений не появилось, что означает, что это, вероятно, что-то из установленной нами библиотеки.

Вы видите что-нибудь очевидное в сказанном выше?

Сейчас это похоже на погоню за дикими гусями! :-)

Поэтому мое предложение (и, возможно, нам понадобится руководство по устранению неполадок в документации по этому поводу) - прокомментировать как можно больше из server.js , client.js , App.js и, возможно, некоторые аспекты вашего веб-пакета переопределяют (если они у вас есть), чтобы вернуть ваше приложение Razzle в состояние, подобное "привет мир". Я также предлагаю скопировать HTML-шаблон Razzle обратно в ваш файл server.js чтобы убедиться, что все сценарии загружаются правильно из коробки. После запуска откройте инструменты разработчика Chrome и перейдите на вкладку «Производительность», установите флажок «Снимки экрана» и нажмите значок «Обновить». Смотрите гифку ниже.

kapture 2019-02-05 at 8 33 42

Цель состоит в том, чтобы убедиться, что в области временной шкалы нет пустых белых рамок. После того, как вы получите правильный рендер без вспышек, вам следует добавлять все больше и больше вашего старого кода. Делайте маленькие шажки. Это довольно легко испортить, если не проявить осторожность.

Что касается вашего конкретного случая, я бы начал с удаления всего этого диспетчера тегов Google, всего вашего CSS, а также любого разделения кода, которое вы делаете. Просто сделайте рендеринг и работайте в обратном направлении. Затем запустите Redux. Затем добавьте обратно реактивный шлем, а затем, как только вы решите, что все правильно, дважды проверьте, выполнив также тест Lighthouse (вкладка Audits). Зафиксируйте это состояние в новой ветке, чтобы вы могли вернуться к нему в будущем. Затем добавьте обратно свои сценарии аналитики и снова проверьте рендеринг. Наконец, попробуйте добавить разделение кода обратно.

Примечание. Если вы сохраните свой CSS и выполните вышеуказанный анализ с помощью Razzle в режиме разработки, вы можете получить FOUC, потому что Razzle не обрабатывает стили .css на сервере во время разработки (только на клиенте). Таким образом, чтобы полностью проверить, правильно ли вы выполняете SSR с гидратацией стилей, если вы используете файлы .css , вам следует собрать Razzle для производства и запустить рабочий сервер локально. Однако, если вы просто проверяете наличие вашего HTML с сервера, вы можете запустить это в режиме разработки.

Хорошо, спасибо за отличную работу с Razzle, а теперь и за это. Я собираюсь провести аудит и отчитаться.

Конечно, первое, что нужно проверить на любой "пустой" (не пустой, это ошибка) seo-странице, это: зависит ли ваша страница от componentDidMount для загрузки отображаемых данных. Вы можете использовать curl для быстрой проверки. Если да, вам нужно что-то вроде After.js для предварительной загрузки.

не могли бы вы посоветовать, как отлаживать с помощью вкладки «Производительность», если около отметки 1000 мс есть пустая страница? Я знаю, что моя проблема связана с тем, что библиотека шрифтов значков не загружается на сервер, и поэтому она показывает эти пустые пустые квадраты при начальной загрузке ...

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

Ах, все мои пользовательские шрифты загружаются гораздо позже. Я загружаю свои шрифты через 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 У меня такая же проблема. На моей клиентской странице не загружаются шрифты и ресурсы.

Я не добавил файл webpack.config в настройку своего приложения, который я клонировал из вашего репо. Я только что взял копию из вашего репо и установил модули npm, а затем начал использовать приложение, выполнив команду запуска npm.
что-нибудь, что я пропустил для настройки, или должен использовать мой новый файл webpack для решения этой проблемы?

Пожалуйста, помогите мне решить эту проблему.

Мой файл App.css выглядит так ..
@ font-face {
семейство шрифтов: 'Робото-средний';
src: url ("./ static / fonts / Roboto-Medium-webfont.eot");
src: url ("./ static / fonts / Roboto-Medium-webfont.eot? #iefix") формат ("embedded-opentype"), url ("./ static / fonts / Roboto-Medium-webfont.woff") формат ("woff"), url ("./ static / fonts / Roboto-Medium-webfont.ttf") формат ("truetype"); }

@ font-face {
семейство шрифтов: 'Робото-обычный';
src: url ("./ static / fonts / roboto-regular-webfont.eot");
src: url ("./ static / fonts / roboto-regular-webfont.eot? #iefix") формат ("embedded-opentype"), url ("./ static / fonts / roboto-regular-webfont.woff") формат ("woff"), url ("./ static / fonts / roboto-regular-webfont.ttf") формат ("истинный тип"); }

спасибо inadvane :)

Была ли эта страница полезной?
0 / 5 - 0 рейтинги