Express.js 服务器端渲染 - 请求 '/json/version/

Posted

技术标签:

【中文标题】Express.js 服务器端渲染 - 请求 \'/json/version/【英文标题】:Express.js Server Side Rendering - Request '/json/version/Express.js 服务器端渲染 - 请求 '/json/version/ 【发布时间】:2018-04-03 22:01:06 【问题描述】:

我运行了一个快速服务器来预渲染我的反应应用程序。我有一个路由文件,它将 HomeContainer 与基本路由 / 匹配,所有其他路由与未找到的页面匹配。

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;

我遇到的问题是,当我在服务器上运行应用程序时,页面未找到路由在快速更改为 HomeContainer 路由之前被渲染。

我发现这是因为我的 express 服务器在向 / 发出请求之前向 /json/version 发出请求,此路由与我的路由文件中的一个不匹配,因此找不到该页面组件被渲染。

现在我不明白为什么它会发出这个请求以及如何停止在主容器之前呈现page not found 组件。我已经尝试调试节点服务器,并且最早可以找到引用此路径的位置是在名为_http_server.js的文件中的发出调用中@

下面是调试器的屏幕截图以及我在其中找到引用的 URL。

也供参考,我在下面包含了我的快递服务器。

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');
);

有谁知道为什么会发生这种情况以及如何解决我的问题?

编辑:这是一个显示问题的视频 - https://d26dzxoao6i3hh.cloudfront.net/items/2z3y3f1x3N1D2e422W42/Screen%20Recording%202017-10-23%20at%2012.24%20pm.mov

【问题讨论】:

出于兴趣,假设您使用的是 react-router,它是什么版本? @jthawme 我和它的版本 4 如果你在没有 JavaScript 的情况下运行它会发生什么?我认为发生的情况是您的服务器返回“找不到页面”,但是一旦 JS 启动,它就会加载正确的数据并重建 DOM。我假设您在 routeComponentPromises 中遇到问题。 @MaxGram 同样的事情,我已经剥离了整个事情,只是用快速服务器提供了一些基本的 HTML(没有路由或反应),它仍然试图调用 /json/json/version 我会尝试使用app.get('/json/version', (req, res) =&gt; res.send(204);) 在服务器上禁用'/json/version,然后在浏览器中关闭JS 并在终端中重新运行整个监视控制台。如果“不喜欢”页面仍然是这种情况,我将开始调试服务器。第一步是检查 &lt;StaticRouter location=req.url context=context&gt; 中 req.url 中的所有内容 【参考方案1】:

在我使用--inspect 键运行我的节点应用程序后,我遇到了同样的问题。这些对/json/json/version 的奇怪GET 请求是由chrome 检查器发送的。

所以,解决方案(在我的情况下)是:

    转到chrome://inspect。 点击“Open dedicated DevTools for Node”链接; 打开Connection 标签。 从列表中删除您的端点。

【讨论】:

【参考方案2】:

不是 100% 确定导致问题的原因,但我现在已经解决了。

我做的两个主要改变是

    使用 EJS 作为模板引擎 在 gzip 插件中将 indexFromEmptyFile 设置为 false

2 号似乎真的很重要,如果没有它,express 会尝试为 index.html 而不是 index.ejs 提供服务,这会导致与上述相同的问题,因为 index.html 与我的路线文件中的路线不匹配。

以下是更新代码

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');
);`

【讨论】:

以上是关于Express.js 服务器端渲染 - 请求 '/json/version/的主要内容,如果未能解决你的问题,请参考以下文章

使用 react-router v4 和 express.js 进行服务器渲染

与服务器端渲染堆栈反应

带有服务器端渲染的 React 应用程序因负载而崩溃

如何在 Express.js 服务器上执行 JWT API 请求? (edX API)

在 Express.js 上使用 Axios 向 Spotify API 发出 POST 请求时出现错误 400

angular $resource delete 不会将正文发送到 express.js 服务器