React ssr:仅从节点渲染/发送第一页,而不共享任何文件/文件夹

问题描述

我正在开发一个项目,其中React前端位于http://frontend.com,后端位于http://backend.com

访问http://backend.com会给出“您好,一切都好”,而http://frontend.com显示带有“欢迎来宾”文字的简单页面

我被要求从后端发送第一页。我学习了SSR,并做了一个简单的项目来了解csr与ssr的区别。

SSR要求服务器有权访问前端文件,例如构建文件夹,路由(共享)等。有没有一种方法可以完成文件共享而又不将后端文件放在后端?

我在网上找到了一些参考,建议使用节点模板引擎(ejs,jade,handlebars)来发送第一个文件,然后由React接管。这是一个合适的解决方案吗?

对我来说,挑战是React和node分别托管。我不能像下面那样引用前端文件

app.use(express.static(path.resolve(__dirname,"../frontend/","build"))); 

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 '../App';

const app = express();

app.use('/*',(req,res,next) => {

    fs.readFile(path.resolve('./build/index.html'),'utf-8',(err,data) => {
        if (err) {
            console.log(err);
            return res.status(500).send("Some error occurred")
        }
        return res.send(data.replace('<div id="root"></div>',`<div id="root">${ReactDOMServer.renderToString(<App />)}</div>`))
    })
});

实现路由会使ssr变得更加复杂,共享文件, 然后同构提取等。让Redux添加更多 初始存储的复杂性。我也必须在节点端进行OAuth。 uffffff!

我尝试发送一个简单的html文件,并尝试为src属性注入捆绑文件值。

app.get('/',res)=>{
    res.writeHead(200,{'Content-Type': 'text/html'})
    const firstStream = fs.createReadStream(__dirname + '/index.html','utf-8')
    firstStream.pipe(res)
})

在前端

const [page,setPage] = useState("");

axios.get('http://localhost:3000/').then(res=>{
   console.log(res.data)
   setPage(res.data); // data is the html file - 
                      // - document.querySelector(script).src = "bundle.js"
}).catch(e=>{
    console.log(e,"error occurred")
})

return(
    <div dangerouslySetInnerHTML={{__html:`${page}`}}>
    </div>
) 

,然后查找脚本标记,并将src属性值注入在构建时生成的捆绑文件中。我相信这是不可能的:(。

所以我进入了一个心态,我认为SSR希望几乎所有前端文件都可以在服务器端使用,这意味着将所有frontend.com文件上传到backendend.com位置,然后在那里进行构建以在节点上渲染React,然后发送。这将使http://frontend.com变得不必要。我可以在http://backend.com本身查看所有内容

是否有更好的方法/解决方案?我错过了什么吗?我只想要Node和React在不同位置的服务器的第一页。请让我知道您的建议。

解决方法

我找到了满足上述要求的行业最佳实践。是的,我们需要使用节点模板引擎。我最喜欢的是 EJS,因为它的语法对从 Java ex 演变而来的开发人员很友好:JSP 文字、表达式等。但我不知道如何在 EJS 中实现 while 循环或 switch case 等效。在 Pug/Jade 中发现。

第 1 步:将 React 或 Angular 项目的 index.html 转换为 Pug/Jade 语法,并将其放在 Node 项目中最好名为 Views 的文件夹中。 index.pug 下面

<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="Cache-Control" content="no-store,no-cache,must-revalidate"> 
<meta http-equiv="Pragma" content="no-cache"> 
<meta name="viewport" content="width=device-width,initial-scale=1,shrink-to-fit=no">
<link rel="shortcut icon" href="#{DOMAIN_URL || CDN}/favicon.png" />
<title>Deepak's Page</title>


case ENVIRONMENT

    when 'development'
        <link rel="stylesheet" class="my-app-stylesheet" href="#{DOMAIN_URL|| CDN}/#{VERSION_NUMBER}/static/css/styles.css" />
        

    when 'production'
        <link rel="stylesheet" class="my-app-stylesheet" href="#{DOMAIN_URL or CDN}/#{VERSION_NUMBER}/static/css/styles.css">
        

    default
        p 

</head>

<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>

case ENVIRONMENT

    when 'development'
        <script src="#{DOMAIN_URL || CDN}/#{VERSION_NUMBER}/static/js/main.js"></script>

    when 'production'
        <script src="#{DOMAIN_URL || CDN}/#{VERSION_NUMBER}/static/js/main.js"></script>
    default
        p 

</body>
</html>

第 2 步:安装 pug 模板引擎

npm install pug

第 3 步:将视图引擎设置为 Pug

app.set('view engine','pug');

第 4 步:设置您的视图文件夹的路径,以便您的节点应用程序可以识别它。

app.set('views',path.join(__dirname,'views'));

Step-5:设置默认路由“/”以响应此视图模板

app.get('/',(req,res) => {
  res.render('index');
}

第 6 步:要根据环境渲染内容,请使用我们在渲染时可以传递给 index.pug 的配置。

const myConfig = {
  DOMAIN_URL: "http://frontend.com",VERSION_NUMBER: "0.1.1" // Use variable for different versions for multi environments,CONFIG: // config variable takes value from process.env.DEVELOPMENT || PRODUCTION
}

第 7 步:将配置与渲染一起发送到默认路由

 app.get('/',res) => {
      res.render('index',myConfig);
 }

id 为 root 的 div 标签是关键元素。我们通过配置确定环境并有条件地呈现内容。域 URL/CDN 可能不同,例如 frontend.dev.com、frontend.prod.com。

相关问答

Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其...
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。...
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbc...