React/Node 应用程序中服务器端渲染的外部路由问题

Posted

技术标签:

【中文标题】React/Node 应用程序中服务器端渲染的外部路由问题【英文标题】:External routing issue with server side rendering in React/Node app 【发布时间】:2021-06-05 15:23:31 【问题描述】:

我有一个非常奇怪的问题,如果我使用客户端渲染运行我的应用程序,那么所有路由都可以完美运行。但是,当我切换到 s-s-r 时,它最初在 http://localhost:8001 上运行良好 但是当我通过在浏览器搜索栏中按 Enter 来加载新的获取请求以加载不同的路线时,例如 http://localhost:8001/covid 然后我得到这个错误...... 这是使用 create-react-app 生成的

TypeError [ERR_INVALID_CALLBACK]:回调必须是函数。 收到未定义 在可能回调 (fs.js:160:9) 在 Object.readFile (fs.js:311:14) 在 D:\Repo\WhiteTigerCreateReactApp\server/server.js:37:8 在 Layer.handle [as handle_request] (D:\Repo\WhiteTigerCreateReactApp\node_modules\express\lib\router\layer.js:95:5) 在下一个(D:\Repo\WhiteTigerCreateReactApp\node_modules\express\lib\router\route.js:137:13) 在 Route.dispatch (D:\Repo\WhiteTigerCreateReactApp\node_modules\express\lib\router\route.js:112:3) 在 Layer.handle [as handle_request] (D:\Repo\WhiteTigerCreateReactApp\node_modules\express\lib\router\layer.js:95:5) 在 D:\Repo\WhiteTigerCreateReactApp\node_modules\express\lib\router\index.js:281:22 在参数处(D:\Repo\WhiteTigerCreateReactApp\node_modules\express\lib\router\index.js:354:14) 在参数处(D:\Repo\WhiteTigerCreateReactApp\node_modules\express\lib\router\index.js:365:14)

这是我加载 react 应用程序的 nodeJS 文件,我已经注释掉了用于客户端渲染的行

import express from 'express';
import fs from 'fs';
import path from 'path';
import React from 'react';
import ReactDomServer from 'react-dom/server'

import App from '../src/components/App'
const PORT = 8001;

const app = express();

let indexPath = path.join(__dirname, '../build/index.html');
let buildPath = path.join(__dirname, '../build');

app.use(express.static(path.resolve(__dirname, '..', 'build')));

app.get('*', function (req, res) 
    
    console.log("response",indexPath);

    //s-s-r -------------------------
    fs.readFile(indexPath), 'utf-8', (err, data) => 
        if (err) 
            console.log('error from server.js', err);
            return res.status(500).send("Some error happened")
        
        return res.send(data.replace(
            "<div id='root'></div>",
            `<div id="root">$ReactDomServer.renderToString(<App />) </div>`));
    
    //Client Render -----------------
    //res.sendFile(indexPath);   

);

app.listen(PORT, () => 
    console.log(`App launced on $PORT`);
);

这包含一个单独的服务器文件,我正在调用该文件。这会调用上面的代码。

require('ignore-styles')

require('@babel/register')(
    ignore: [/(node_module)/],
    presets: ['@babel/preset-env', '@babel/preset-react']
)

require('./server')

我的 react 应用有这个入口点

import React from 'react';
import ReactDOM from 'react-dom';
import  BrowserRouter as Router  from "react-router-dom";

/* ./ means search local file system*/
import './assets/css/styles.css';
import './assets/css/utility.css';
import './assets/css/animate.min.css';
import './assets/css/call-now.css';
import './assets/css/Contact-Form-Clean.css';
import './assets/css/betternav.min.css';
import App from './components/App';
//when importing from a module the file extension is omitted. */
import '../node_modules/font-awesome/css/font-awesome.min.css';
import '../node_modules/bootstrap/dist/css/bootstrap.min.css';
import '../node_modules/bootstrap/dist/js/bootstrap.min.js';
import reportWebVitals from './reportWebVitals';

ReactDOM.hydrate(
  <React.StrictMode>
    <Router>
    <App />
    </Router>
  </React.StrictMode>,
  document.getElementById('root')
);    

reportWebVitals();

我的 App 组件看起来像这样,其中包含所有路由.. 很明显,在 s-s-r 期间某些东西无法正常工作或加载? :/

import  React, Component  from 'react';
import  Switch, Route  from 'react-router-dom'
import '../assets/css/header.css';
import '../assets/css/footer.css';
import Header from './header';
import Footer from './footer';
import Body from './body';
import ClientPage from './clientPage'
import CovidPage from './covidPage'
import Commercial from './commercialPage'
import CarpetFloorPage from './carpetFloor'
import IndustrialConstructionPage from './industrialConstructionPage';
import SafeContractorPage from './safeContractor';
import ContactPage from './contactPage';
import IndustrialCommercialPage from './industrialCommercialPage';
import Popper from 'popper.js';
import BetterNav from '../utility/betternav'
import PageNotFound from './notFoundPage';

class App extends Component  


  componentDidMount()
  
    BetterNav();
  

  render() 
    return (
      <div>
        <Header></Header>
        <Switch>          
          <Route exact path='/' component=Body />          
          <Route path='/clients' component=ClientPage />
          <Route path='/covid' component=CovidPage />
          <Route path='/commercial' component=Commercial />       
          <Route path='/carpet-floor' component=CarpetFloorPage /> 
          <Route path='/industrial-construction' component=IndustrialConstructionPage /> 
          <Route path='/safe-contractor' component=SafeContractorPage />          
          <Route path='/contact' component=ContactPage />
          <Route path='/industrial-commercial' component=IndustrialCommercialPage />                     
          <Route component=PageNotFound />
        </Switch>
        <Footer></Footer>
      </div>
    );
  
  


export default App;

由于将 server.js 中的代码更新为

app.get('*', function (req, res) 
    
    console.log("response",indexPath);

    //s-s-r -------------------------
    fs.readFile(indexPath, 'utf-8', (err, data) => 
        if (err) 
            console.log('error from server.js', err);
            return res.status(500).send("Some error happened")
        
        return res.send(data.replace(
            "<div id='root'></div>",
            `<div id="root">$ReactDomServer.renderToString(<App />) </div>`));
    )
    //Client Render -----------------
    //res.sendFile(indexPath);   

);

应用程序首先加载 http://localhost:8001 照常没问题。但是当我去 http://localhost:8001/covid 我得到了这个错误。..

D:\Repo\WhiteTigerCreateReactApp\node_modules\react-dom\cjs\react-dom-server.node.development.js:3709 抛出错误; ^

TypeError:无法读取未定义的属性“createElement” 在 App.render (D:\Repo\WhiteTigerCreateReactApp\src\components/App.js:30:7) 在 processChild (D:\Repo\WhiteTigerCreateReactApp\node_modules\react-dom\cjs\react-dom-server.node.development.js:3450:18) 解决时(D:\Repo\WhiteTigerCreateReactApp\node_modules\react-dom\cjs\react-dom-server.node.development.js:3270:5) 在 ReactDOMServerRenderer.render (D:\Repo\WhiteTigerCreateReactApp\node_modules\react-dom\cjs\react-dom-server.node.development.js:3753:22) 在 ReactDOMServerRenderer.read (D:\Repo\WhiteTigerCreateReactApp\node_modules\react-dom\cjs\react-dom-server.node.development.js:3690:29) 在 Object.renderToString (D:\Repo\WhiteTigerCreateReactApp\node_modules\react-dom\cjs\react-dom-server.node.development.js:4298:27) 在 D:\Repo\WhiteTigerCreateReactApp\server/server.js:27:46 在 FSReqCallback.readFileAfterClose [as oncomplete] (internal/fs/read_file_context.js:63:3) npm ERR!代码生命周期 npm 呃! errno 1 npm 错误! s-s-r-example@0.1.0 s-s-r:node server/index.js npm 错误!退出状态 1 npm ERR! npm 错误!失败了 s-s-r-example@0.1.0 s-s-r 脚本。

【问题讨论】:

【参考方案1】:

我认为你的问题在这里:

fs.readFile(indexPath), 'utf-8', (err, data) => 
    // ........ (Your code was here)

改成:

fs.readFile(indexPath, 'utf-8', (err, data) => 
    // ........ (Your code was here)
)

你刚刚错过了括号。

【讨论】:

已更新,但导致了一个非常不同的错误,将更新票证。 你说得对,你需要一个服务器端的 webpack。

以上是关于React/Node 应用程序中服务器端渲染的外部路由问题的主要内容,如果未能解决你的问题,请参考以下文章

Next.js:如何将仅外部客户端的 React 组件动态导入到开发的服务器端渲染应用程序中?

在 Nuxt.js 中检测服务器端渲染

Angular5服务器端渲染,外部Api数据服务在s-s-r中不起作用

客户端和服务器端(React/Node.js)之间的 Socket.io 连接失败

如何在 React/redux 中进行服务器端渲染?

React + Node.JS 巧妙实现后台管理系统の各种小技巧(前后端)