不久前,我们使用服务器渲染页面,然后React用于客户端渲染和单页应用程序。它引入了虚拟DOM,并改变了我们编写代码的方式。
在编写代码之前,我们需要所有这些react库,并将它们作为依赖项进行安装。现在我们可以分解成许多组件,有许多css和scss文件,包括图像。但最后我们将构建文件,制作紧凑的捆绑包,并从构建文件夹中提供服务。
快速获取路线
app.get('*', (req,res) =>{
res.sendFile(path.join(__dirname+'/client/build/index.html'));
});
我的理解是:
生成文件夹是webpack组合所有文件并创建可供部署的缩小捆绑包的地方。该文件基本上是每个浏览器都能理解的简单HTML和JS文件。由于所有的浏览器都不懂ES6等等,我们必须将所有这些文件转换成每个浏览器都能理解的普通语言。
此外,webpack-dev服务器仅用于开发目的,我们不会将其运行到生产中。
- 虚拟DOM/Real DOM只是用于开发目的吗?或在构建缩小的文件时,这些react库是否也被转移?如果是以后的情况,react是否在客户端浏览器的后台模式下运行?我想知道react在构建应用程序后如何处理客户端路由
- 如何管理Node React应用程序的github存储库?您是否保留了两个不同的存储库,一个用于前端,另一个用于后端?行业标准是什么
- 如果保留两个存储库,如何部署前端代码?因为您无法将webpack开发服务器运行到生产环境中。您也不能在后端(express服务器)中指定公共静态(构建文件夹),因为它们被分隔在两个repo中。这两个存储库的集成是如何进行的(假设我们有两个AWS EC2实例,每个实例一个),还是前端从前端回购中获得服务?)。你真的能在生产中使用类似npm服务的东西吗
我想做什么?
我想在AWS上部署我的node-react应用程序。我在github上只有一个存储库。我在我的repo中有一个文件夹"client",所有的react代码都放在它的package.json文件中。服务器的所有其他文件都在根文件夹中(服务器并没有自己的文件夹,文件分散在根文件夹内)。所以有两个package.json文件,一个在服务器的根文件夹中,另一个在客户端文件夹中。我计划在docker容器上运行我的节点应用程序。
请帮助我理解代码托管和部署的核心概念和标准实践,以保持大型企业应用程序的完整性。
我不会在这里解释你问题中的所有要点,因为@Arnav Yagnik和@PrivateMega都在解释大部分要点方面做得很出色。我绝对建议你在阅读这个答案之前,正确阅读他们的答案,并阅读提供的链接以获取更多信息。
我想解决您关于部署Node React应用程序的问题。在生产中,通常,我们对前端(React)和后端(Node)都有不同的部署(或您在问题中提到的"存储库")。这允许您的后端位于EC2实例中,例如,通过自动扩展来确保它能够处理所有传入的请求
正如前面的回答中所提到的,以及在您的问题中,webpack将React文件编译并缩小为简单的HTML和JS文件,大多数浏览器都可以运行这些文件(我不打算在这里解释VirtualDOM,因为它在其他回答中已经得到了完美的解释)。例如,您可以将这些缩小的文件从S3存储桶中提供,因为它是一个单页应用程序(也在其他答案中讨论过),业务逻辑已经在缩小的JS文件中,它只是将所有请求发送到后端服务器。
现在,对于前端,您可以使用TravisCI将构建文件夹(您在问题中谈到的文件夹)部署到EC2实例,并使用NGINX提供文件,或者如果您可以正确配置CDN部署,则可以从S3存储桶提供文件,以获得最佳性能。
您可以将服务于React应用程序,就像向用户的浏览器发送一个神秘的代码块一样。现在,您可以将这个神秘的代码块部署到公共可用的S3存储桶中,并从那里提供它。同样,由于webpack和缩小/放大,no-on无法正确理解您的原始代码是什么,请记住,例如,您仍然可以访问Chrome的"源代码"选项卡中的所有代码。
我想用不同的方法来解决这个问题。
服务器呈现页面:概念没有改变,服务器在遇到DOC请求时必须用html进行响应。现在HTML可能包含也可能不包含脚本(可以是内联的,也可以是外部服务器地址)。在问题的上下文中,您仍然可以在HTML中下载您编写的脚本(可能包括react或not)。在大多数情况下,您可以运送带有脚本标签的空html,这些标签将通过网络下载脚本并执行它们,其中包含所有渲染逻辑。
要回答您的问题:第一:单线程JS中没有后台模式(除非我们想讨论工人,但我们可以将他们排除在讨论之外)。通过编写代码,您不会与任何DOM交互。您正在指示组件(由React扩展)何时更改其状态以及何时重新渲染(setState)。React在内部计算虚拟DOM,并与真实DOM进行比较,以计算真实DOM上要进行的实际更改(这是一个非常抽象的答案,为了获得更多的理解,请阅读React文档,这里的基线是你没有与任何DOM交互,只是指示React核心库何时更新以及更新的状态是什么)
第二:如果你想支持SSR(服务器渲染页面)。我建议用不同的包.json制作两个文件夹,客户端(这将包括所有客户端组件和逻辑)和服务器(将包括所有服务器端逻辑),因为这两个应用程序的包不同。这里没有这样的行业标准,漂浮的东西应该可以工作,但通常基于逻辑实体制作目录应该满足分离和可维护性,如果将来你认为你想在单独的repo中分配服务器和客户端,这肯定会让这个过程变得容易。
第三:您避免在生产中运行webpack开发服务器。文件通常不会混淆,因此负载很重(不要忘记您编写的代码就在那里)。即使您想制作不同的repo,服务器也可以吐出html,html也可以向您的客户端服务器请求脚本。
如何部署:部署您的代码并运行:
节点服务器/app.js
并且在app.js中,您可以编写您提到的位置块。
附言:如果你只需要一台带有该位置块的服务器。你真的需要快递服务吗?您可以将客户端构建上传到CDN,并将您的域路由到CDN中提供index.html(这里也可以使用s3 bucket)
我想从尽可能多地清理术语开始。
就像你所说的,服务器渲染页面在过去是一个更突出的标准,但随着React的推出,它根本没有改变,因为即使是React也支持服务器渲染或SSR,这意味着HTML页面是在服务器端生成的,然后使用浏览器提供给客户端。
客户端呈现意味着,将HTML页面加载到浏览器中,然后javascript代码在这些HTML页面上呈现内容,并使其具有交互性。
单页应用程序的概念是,我们只有一个HTML文件或基本HTML页面,基于用户交互和来自服务器的数据,它会不断重写。
虚拟Dom是React引入的一个令人惊叹的概念。React库代码以树的形式在内存中重新创建HTML页面的所有元素(称为DOM元素)的结构。这使得称为Fiber的React算法能够根据路由更新或任何其他更改来协调适当的更改,然后将其转换为HTML页面中的真实元素。
Babel是一个将浏览器引擎尚未开始支持的最新功能转换为他们能够理解的代码的转换器,通常是ES6+代码,因为所有浏览器都支持ES6之前的代码。在React应用程序中,如果您已经使用JSX语法编写了应用程序,babel也支持将JSX转换为普通的javascript。
是的,由于React组件的组成性质,可以将页面分解为多个组件,这意味着我们可以通过组合更小、更集中的东西来构建复杂的东西。
在将其提供给最终用户之前,我们不能因为代码太大而导致web应用程序滞后,因此在构建过程中,会进行缩小(删除空白等)和其他优化,如将多个javascript文件合并为一个文件等,然后像您所说的那样从构建文件夹中提供紧凑的捆绑包。
是的,构建文件夹是webpack进行缩小和组合以创建尽可能小的捆绑包的地方。每个浏览器都能理解基本的HTML和JS文件,如果代码中包含特定浏览器不支持的内容,那么相应的支持代码或polyfill也会与之捆绑在一起。从技术上讲,你不能说浏览器只理解ES6之前的代码,因为很多浏览器引擎已经实现了大量的ES6功能。
Webpack dev-server只用于通过node.js服务器等端口为Webpack应用程序提供服务,并为我们提供实时重载等功能,当您不断更改应用程序代码库时,需要实时重载,而在生产时则不需要,因为正如我们之前所说,在生产时,它只是HTML和js,没有人对这些文件进行任何更改。
-
虚拟DOM是React Code使用的内存表示或概念,就像我们有堆栈和队列一样,它不仅仅在开发时使用。是和否。因为我认为运行应用程序所需的react源代码的适当部分也将在生成生产捆绑包之前进行捆绑。
-
我想说的是,不要有预设的事情方式,因为这完全取决于开发人员和团队,因为我看到人们使用了两个独立的repo,因为前端人员处理前端的事情,而后端人员处理后端的事情。但也有一种情况是,每个人都是全栈开发人员,从技术上讲,你可以将其放在一个带有单个package.json的单个repo中,并使用后端为前端文件提供服务,你必须手动安装每个react依赖项,不能直接使用CRA或创建类似react应用程序的生成器。
-
2个存储库与生产中的前端部署有什么关系?您不需要在生产中运行webpack-dev-server-to-server文件。您可以创建一个生产捆绑包,然后设置任何http服务器来为生成的捆绑包提供服务。
关于您当前的场景,我想说的是,您可以使用一个单独的package.json并将所有依赖项一起安装,而不是使用2个package.jsson,或者使用类似lerna或yarn工作区的monoreo方法。
但对于一个完全初学者,我建议使用两个独立的存储库来减少遇到的问题。
如果你不知道的话,还有一点值得奖励,你可以用ES6之前的代码编写React,也可以不用JSX。
1)虚拟DOM基本上是说你调用的是react函数,而不是在真实DOM 上进行操作的实际函数
像这个
document.getElementById("demo").innerHTML ="Helloworld"
修改实际的dom
但是这个
ReactDOM.render(
<HelloMessage name="Taylor" />,
document.getElementById('demo')
);
如果你正确地看到了这一点——你没有直接在dom上做任何事情——你只是给了react函数控制权来做事情,只要react想根据自己的逻辑重新渲染它,内部react就会负责修改dom元素演示,他们声称这是优化的,这就是人们首先使用它的原因。是的,当你用webpack构建代码时,它确实包含了react,这是缩小代码的一部分,所以如果你在开发中看到任何错误堆栈,你会看到react是它的起点
2) 我认为这是一个选择,因为这个没有限制
3) 在部署方面,一般来说,如果你想使用nodejs,你可以选择expressjs服务器类型的部署,但除此之外,通常最好使用Nginx或Apache等高性能服务器,或者,如果你只是不想陷入人们通常使用基于heroku的部署,或者人们使用netlify等特殊平台,surge.sh(在这些平台上部署起来超级容易)。
我相信其他人在解释React Virtual DOM方面做得很好。以一种简单实用的方式,我将尝试解释如何使用NodeJS和React管理动态网站(包括中型企业系统)的部署。我也尽量不让你厌烦。
想象一下,React从未存在过,您正在构建一个传统的服务器端渲染应用程序。每次用户点击一条路线时,控制器都会与模型一起执行一些业务逻辑并返回一个视图。在NodeJS中,此视图通常使用模板引擎(如handlerbs)进行编译。如果你反思一下,很明显,视图可以是任何html内容,然后作为响应发送回浏览器。
这是一个可以发回的典型响应:
<html>
<head>
<title>Our Website</title>
<style></style>
<script src="/link/to/any/JS/script"></script>
</head>
<body>
<h1>Hello World </h1>
</body>
</html>
如果这个响应进入浏览器,屏幕上显然会显示"Hello World"。现在,通过这种简单的方法,我们可以做强大的事情!
选项1:我们可以指定一个控制器来处理所有传入路由app.get("*", controllerFunc)
,并为整个服务器呈现一个视图。
选项2:我们可以要求多个控制器处理不同的路由,并从服务器呈现特定于路由的视图。
选项3:我们可以要求多个控制器处理不同的路由,并从服务器动态生成页面。
如果我们正在构建一个传统的web应用程序,选项3将是唯一合理的标准。在这里,页面是为不同的路由动态生成的。然而,使用选项1,我们可以生成一个高质量的单页应用程序,其中发送到服务器的响应是一个空的html页面,但使用构建的JS脚本,该脚本能够操作DOM–是的,React!以下是这样的回应:
<html>
<head>
<title>Our Website</title>
<style></style>
<script src="/link/to/any/JS/script"></script>
</head>
<body>
<h1>Hello World </h1>
<div id="root"> </div>
<script async type=”text/javascript” src="/link/to/our/transpiled/ReactSPA.js"></script>
<!--async attribute is important to ensure that the script has access to the DOM whenever it loads. This also makes the script non-blocking -->
</body>
</html>
很明显,我们将所有责任交给生成的SPA,所有路由逻辑都在客户端处理(请参阅react router dom)。在服务器端,我们可以在选项2中引入这个概念,并调整NodeJS路由处理程序,以监听任何RESTneneneba API通信的另一个特定路由。如果您熟悉NodeJS,那么app.get()
或app.post()
注册路由的顺序很重要。
但是,使用选项1,我们很快就会受到限制,并且只能从该服务器提供一个单页应用程序。为什么?因为我们已经要求一个控制器处理所有非API传入路由并呈现一个视图。我们还冒着提供不必要的膨胀JS文件的风险。当用户想要的只是登录页时,他们会得到完整的网站。
不过,如果我们考虑选项2,我们可以进行更多的调整,并为不同的路由提供多个单页应用程序,所有这些都来自我们的服务器。这种方法有助于减少发送到浏览器的JS构建的大小。一个典型的例子是一个网站,它有一个欢迎页面(或介绍目录)、一个登录页面和一个仪表板。
通过为不同的路由分配控制器,我们可以为这些路由建立唯一的SPAs。介绍页面的SPA,登录页面的SPA和仪表板的SPA。是的,浏览器在三者之间转换时必须加载,但至少我们大大增加了网站的初始渲染时间。我们还可以使用更安全的cookie选项进行授权,而不是在localStorage
上存储会话令牌的不太安全的选项。
在更高级的设置中,我们可以将具有不同React组件的动态网站呈现为动态生成页面中的小部件。事实上,Facebook就是这么做的。
构建此类SPAs或组件的方法非常简单。启动一个react项目并配置webpack,将生产就绪的JS文件呈现到服务器端repo中您首选的公共静态目录中。视图中指定的<script>
可以很容易地加载这些构建的react组件,因为它们存在于服务器端的公共目录范围内。
本质上,这意味着一个具有多个客户端目录和一个服务器目录的repo,其中将由webpack为每个客户端项目生成的生产构建文件的目标设置为服务器的公共静态目录。因此,每个客户端的目录都是一个项目(可以是完整的SPA,也可以是作为小部件的简单React Component),它有自己的webpack.config和package.json文件。事实上,您可以有两个独立的配置文件——生产和开发。然后,要进行构建,可以使用npm ~relevant command~
进行生产构建或开发构建。
然后,您可以像托管任何NodeJS应用程序一样继续托管它。因为,主要的应用程序是NodeJS,这就是服务器所在的位置。用PHP和Apache/NGINX替换NodeJS,概念仍然不变。