2017-10-23 73 views
6

Mam uruchomiony serwer ekspresowy do wstępnego renderowania mojej aplikacji reagowania. Mam plik tras pasujący do HomeContainer do podstawowej trasy /, a wszystkie inne trasy pasują do strony, która nie została znaleziona.Renderowanie po stronie serwera Express.js - Żądanie "/ json/version/

import HomeContainer from 'containers/home-container/home-container'; 
import PageNotFound from 'components/page-not-found/page-not-found'; 

const routes = [ 
    { 
    path: '/', 
    exact: true, 
    component: HomeContainer 
    }, 
    { 
    path: '*', 
    component: PageNotFound 
    } 
]; 

export default routes; 

Problem Mam to po uruchomieniu aplikacji na serwerze strona nie znaleziono trasa zostanie wydanego przed szybko zmieniających się na trasie HomeContainer.

Zidentyfikowałem, że tak się dzieje, ponieważ mój ekspresowy serwer wysyła żądanie do /json/version, zanim wyśle ​​żądanie do /, ta trasa nie jest zgodna z jedną z moich plików tras i dlatego element nie odnaleziony na stronie jest renderowany .

Teraz nie dostaję tego, dlaczego robi to żądanie i jak zatrzymać renderowanie komponentu page not found przed kontenerem domowym. Próbowałem już debugowania serwera węzła i najwcześniejszym miejscem, w którym mogę znaleźć tę ścieżkę, do której się odwołuje, jest wywołanie emisji wewnątrz pliku o nazwie _http_server.js

Poniżej zrzut ekranu debuggera i miejsce, w którym znalazłem adres URL, do którego się odwołuję.

Debugger Screenshot

również w celach informacyjnych, podaję moje wyraźne serwer poniżej.

import express from 'express'; 
import React from 'react'; 
import { renderToString } from 'react-dom/server'; 
import { Provider } from 'react-redux'; 
import { StaticRouter, matchPath } from 'react-router-dom'; 
import serialize from 'serialize-javascript'; 
import expressStaticGzip from 'express-static-gzip'; 
import sourceMapSupport from 'source-map-support'; 

import routes from 'routes'; 
import configureStore from 'store'; 
import AppContainer from 'containers/app-container/app-container'; 

if (process.env.NODE_ENV === 'development') { 
    sourceMapSupport.install(); 
} 

const app = express(); 

app.use(expressStaticGzip('./static/assets')); 

app.get('*', (req, res, next) => { 
    const store = configureStore(); 

    /** 
    * Load initial data into state 
    * match requested URL path to the component in routes 
    * check for 'fireInitialActions' method (found in the container components) 
    * and dispatch if it exists 
    */ 
    const routeComponentPromises = routes.reduce((accumulator, route) => { 
    if (matchPath(req.url, route) && route.component && route.component.fireInitialActions) { 
     accumulator.push(Promise.resolve(store.dispatch(route.component.fireInitialActions()))); 
    } 

    return accumulator; 
    }, []); 

    Promise.all(routeComponentPromises) 
    .then(() => { 
     const context = {}; 
     const markup = renderToString(
     <Provider store={store}> 
      <StaticRouter location={req.url} context={context}> 
      <AppContainer /> 
      </StaticRouter> 
     </Provider> 
    ); 

     const initialData = store.getState(); 
     res.send(` 
     <!DOCTYPE html> 
     <html> 
      <head> 
      <title>Test</title> 
      <script src='vendor.js' defer></script> 
      <script src='app.js' defer></script> 
      <script>window.__initialData__ = ${serialize(initialData)}</script> 
      </head> 
      <body> 
      <div id="root">${markup}</div> 
      </body> 
     </html> 
     `); 
    }) 
    .catch(next); 
}); 

app.listen(process.env.PORT || 3000,() => { 
    console.log('Server is listening'); 
}); 

Czy ktoś wie, dlaczego tak się dzieje i jak mogę rozwiązać mój problem?

EDIT: Oto film pokazujący problem - https://d26dzxoao6i3hh.cloudfront.net/items/2z3y3f1x3N1D2e422W42/Screen%20Recording%202017-10-23%20at%2012.24%20pm.mov

+0

z odsetek, zakładając używasz reagowania-router, jaka wersja jest? – jthawme

+0

@jthawme Jestem i jego wersja 4 – woolm110

+0

co się stanie, jeśli uruchomisz go bez JavaScript? Wydaje mi się, że serwer zwróci "Nie znaleziono strony", ale zaraz po uruchomieniu JS ładuje odpowiednie dane i odbudowuje DOM. Zakładam, że masz problem w routeComponentPromises. –

Odpowiedz

0

Nie 100% pewni, co było przyczyną problemów, ale już teraz poprawiony.

Dwie główne zmiany zrobiłem były

  1. Korzystanie EJS jak silnik szablonów
  2. Setting indexFromEmptyFile false w wtyczki gzip

Numer 2 wydaje się być bardzo ważny, bez niego express próbował obsłużyć numer index.html, a nie index.ejs, co powodowało takie same problemy jak powyżej, ponieważ index.html nie pasowało do trasy w pliku tras.

Poniżej zaktualizowany kod

import express from 'express'; 
import React from 'react'; 
import { renderToString } from 'react-dom/server'; 
import { Provider } from 'react-redux'; 
import { StaticRouter, matchPath } from 'react-router-dom'; 
import serialize from 'serialize-javascript'; 
import sourceMapSupport from 'source-map-support'; 
import expressStaticGzip from 'express-static-gzip'; 

import routes from 'routes'; 
import configureStore from 'store'; 
import AppContainer from 'containers/app-container/app-container'; 

if (process.env.NODE_ENV === 'development') { 
    sourceMapSupport.install(); 
} 

const app = express(); 

app.set('views', './static/'); 
app.set('view engine', 'ejs'); 
app.use(expressStaticGzip('static/assets', { indexFromEmptyFile: false })); 

app.get('*', (req, res, next) => { 
    const store = configureStore(); 

    /** 
    * Load initial data into state 
    * match requested URL path to the component in routes 
    * check for 'fireInitialActions' method (found in the container components) 
    * and dispatch if it exists 
    * return as promises so we can chain 
    */ 
    const routeComponentPromises = routes.reduce((accumulator, route) => { 
    if (matchPath(req.url, route) && route.component && route.component.fireInitialActions) { 
     accumulator.push(Promise.resolve(store.dispatch(route.component.fireInitialActions()))); 
    } 

    return accumulator; 
    }, []); 

    Promise.all(routeComponentPromises) 
    .then(() => { 
     const context = {}; 
     const markup = renderToString(
     <Provider store={store}> 
      <StaticRouter location={req.url} context={context}> 
      <AppContainer /> 
      </StaticRouter> 
     </Provider> 
    ); 

     const initialData = serialize(store.getState()); 

     res.render('index.ejs', {initialData, markup}); 
    }) 
    .catch(next); 
}); 

app.listen(process.env.PORT || 3000,() => { 
    console.log('Server is listening'); 
});`