在传统的 web 架构中,浏览器发送 http 请求,web 服务器返回网页
而在前后端分离架构中,好像多了个前端服务器,我想问下这个前端服务器有什么用?
是接收后端返回的 json 数据,然后渲染成网页,再返回给浏览器吗?
就是说后端服务器只负责处理数据,前端服务器负责渲染成网页是吗?
那 ajax 是不是可以理解为一个微型的前端服务器,接收后端服务器返回的 json 数据,然后渲染到网页上

第二个问题:在下面 web 架构图中,各个软件负责啥工作?

nginx 负责接收前端请求,使用 IO 多路复用监听多个 socket
请求静态资源直接响应
请求动态资源,转发给 uwsgi 服务器
uwsgi 服务器把 http 协议中各个字段拆分出来,交给 web 应用
web 应用依照字段到数据库取值,处理,按原路返回给浏览器
nginx 中某个 socket 一有 IO 事件,就交给 web 应用,web 应用使用线程池分配一个线程来处理这个用户请求

我上面写的流程有问题吗?
现在服务器架构是使用 IO 多路复用 + 线程池处理吗?

ajax 是一个前端发送请求请求用的技术,和渲染没关系,也不能把它称为服务器。这个技术是用来让前端有在浏览器中与服务器进行交互的能力。

前端服务器描述不太清楚。
如果是指 SSR 服务,目的只是把传统服务端渲染的逻辑独立出来, 前端利用框架技术实现同一套代码的浏览器渲染和服务端渲染的复用。
如果是指静态资源服务,一般是 CDN 服务,负责浏览器渲染所需资源的分发。
ajax 是数据传递方式,没明白怎么个服务器概念。

前端服务器可以接收后端的的数据。比如我们就是用的 RPC 获取的数据,然后处理数据。如果有 SSR 的话还需要执行 SSR 方法。在开发环境中,可能还需要集成 webpack ,负责文件的打包、编译、压缩。

嗯,像前端使用 vue 开发,好像是也会运行一个服务器,我指的前端服务器就是这个

ajax 那个比喻好像是不太恰当,不要在意

“比如我们就是用的 RPC 获取的数据,然后处理数据”
这个处理数据,是指啥?把数据渲染成网页吗?

"WEB 服务器”是可以套多层的,比如 CDN->nginx a->nginx b
“前端服务器”的架构位置也在"Web 服务器"这一层。只是开发环境懒得手动打包配 nginx ,所以就弄了个即时编译并反代的工具给你用。

#4 你说的那个东西是 express ,他只是方便你在本地模拟 http 环境罢了。不需要 ssr 的前端直接用 nginx 作为容器也是可以的

意思是那只是个测试服务器是吗?

那完全前后端分离的架构,数据渲染是由谁负责呢?

是浏览器先渲染个模板,然后使用 ajax 去后端服务器取数据,浏览器再渲染到网页上

那模板从哪取?把它当静态资源存在 nginx 上吗?

还是像你们说的,有个专门的 ssr 服务器负责把网页渲染好,再返回给浏览器

拿到最原始的数据,然后处理成需要的数据

两种都有,搞 SSR 的一般都是有一定需求,比如希望搜索引擎收录或者加快首屏速度之类的
此外还有更复杂的 react server component 等

这样看业务具体选择了。可纯浏览器渲染,也能 SSR 渲染,模板都编译到 js 文件里。
SSR 只解决在 js 执行前 html 本身有内容(比如上面同学提到的 SEO 场景),js 实际运行时还是会做前端应用基于状态与和当前已渲染的 dom 比对结合过程 ( hydration )处理的。

这个前端服务器只是开发的时候用的,开发的时候会一边改代码一边看页面效果,前端服务器就是负责把编译成浏览器能运行的 html 和 js ,同时把编辑器里修改的代码同步到浏览器而已,实现前端代码热更新。打包之后的代码是不会包含这个服务器的,文件也是 html+js ,而非源代码的.vue 。

至于什么 ajax ,就是请求数据的 api ,根本不算服务器。

至于 ssr ,这个才能算是前后端分离后的前端服务器,你可以理解为,把传统 mvc 架构中 viem 这一部分抽离出来,单独部署成一个服务,你可以用 java 可以用 php 来实现,只是前端 ssr 选择了用 nodejs 来实现,这样就可以使用 vue 或者 react 来做。

后端服务器能处理,干嘛还要多此一举交给前端服务器处理呢?又或者干脆让前端服务器直接去数据库取数据,完全用不着后端服务器啊?

你既然已经说了在前后端"分离"的架构中是这样这样做,你这里回答又认为多此一举,那你首先要就搞清楚为什么要分离,前后端分离的核心在于解耦数据逻辑与数据展示之间的关系,前端专注于做前端的视图渲染,后端只负责数据处理,最大限度的降低人员之间的合作成本,你所谓前端服务器的 12L 已经解析很清楚了。目前比较普遍的做法是前端在完成开发之后 build 成浏览器可解析的 html+css+js 文件,然后由 nginx 反代统一管理这些静态文件和后端服务器。至于楼上提到的 SSR ,是前后端分离的下一个阶段,为什么 web 开发发展了这么多年又回到了前后端合并的路子上,楼主可以自行搜索"同构渲染"

#13 分离的目的是可维护可扩展。你的业务需求简单当然可以一把梭,但是这样不利于项目的工程化。如果前后端混在一起,用后端的模板引擎,一个问题就是这些模板谁来写?前后端都越发的复杂,后端了解在模板里的数据绑定,但是不了解前端的复杂页面如何编写,反之亦然。分开的目的就是为了各司其职,同时也可以减轻后端服务器的压力。

这个架构里的所谓“web 服务器”不是必须的,直接让 django, flask 监听 80 端口一样可以提供接口给前端页面进行 ajax 请求。

弄个虚拟机,自己装一遍就全明白了。

你说的情况是使用了内置的 web 服务器,django 内置了一个符合 wsgi 标准的 web 服务器, 这个 web 服务器是用于开发调试用的,性能比 nginx 差,对静态资源处理没有 nginx 好,开发调试用的。至于 flask 我就不知道了,应该也差不多奥

前后端分离的前端页面(静态)部分当然可以走 nginx ,而 django 和 flask 提供的是接口,并不是开发调试用的。即使上线了接口从 nginx 转一道,最后提供数据的照样的 django 和 flask 提供的是接口。

所谓的前后端分离,有一层意思是文中的'web 服务器'可以分开为“前端 web 服务器”和“后端 web api 服务器”。前端的静态文件(所有 html, js, css 图片)等可以放到一个 web 服务器中,例如 nginx ,或者开发的时候用 yarn start 跑起来的,或者直接 python -m SimpleHTTPServer 、servce -s 等命令临时跑起来调试的,又或者是放到 AWS 的 cloudfront 上,只要浏览器能正常请求到就行。

而浏览器加载完页面,js 发起的 ajax 请求的是“后端 web api 服务器”。可以直接用 django/flask/go-gin/php 提供,当前现实中更常见的是用 nginx 转一道。

我的意思是说 django 内置的 web 服务器是开发调试用的,当你使用 python3 manage.py runserver 0.0.0.0:8000 的时候,启动了 django 内建的 web 服务器, 这个内建的服务器是开发调试用的~django 文档里面写着了。

所以你原来的‘‘web 服务器’’不是必须的这个说法我觉得是不对的~ 它实际上还在

另外,哪怕你用 socket 实现 http 协议,然后处理请求,把所有程序都弄在一起了,那么这个程序本身就是一个 web 服务器了啊,只是没有了上面架构的分层

内建的 web 服务器也是符合 wsgi 协议的,不然跟 django 框架进行交互的时候就要另外实现一套协议了

你所谓的 前端服务器( vue 那个)是没有必要的,他所做的事情只是帮你快速编译你的 vue 为浏览器能跑起来的 html ,javascript ,css 代码,实际部署的时候,大多数情况下都是一个服务器,至于上面的人说的,我觉得你还不到理解这个的时候
对了,vue 里面也有一个 proxy 的东东,这个出现的原因最初的原因应该是解决跨域

另外,我感觉你说的渲染有歧义:

  1. 渲染为 html 代码
  2. 渲染为实际看到的网页(浏览器干的事情)

我感觉你的意思是第二个,上面的回答都是第一个 🤔️

这么说吧,前端服务器可以理解为分发静态资源的服务器,你请求后浏览器拿到了 html 模板和 js 代码,然后执行 js,js 会从 api 服务器去拿数据,拿完数据通过操作 dom 来更新页面,你问题的谁来渲染,肯定是浏览器啊

PS:以下 “WEB 服务器”,“前端服务器”,“WEB 应用” 等词汇可能描述不太准确……

WEB 服务器用来存储静态文件,包括 js ,html ,css 等。你浏览器访问 web 服务器其实就是下载这些静态文件,然后交给浏览器执行。所以并不是 web 服务器 --> 访问转发 --> web 应用,而是 浏览器 --> 访问 --> web 应用。

  • 浏览器:拉取 url 对应的资源,然后渲染 html 和执行 js
  • web 服务器:存放静态资源
  • web 应用:处理和响应请求

把你的图片改成下面这样,你估计就比较好理解了:

web 服务器 <——> 浏览器 <——> web 应用

另外,你说的 Vue 前端服务器,实际上就是运行在你本地的一个 web 服务器。

已经习惯了把提供接口的后端程序称为“API 服务器”了。。。

多谢你们的解释,对于 django 和 wsgi 服务器的关系我有点理不清,能帮我参考下吗?

django 本身是分为内置开发服务器和 web 应用

内置开发服务器就是一个实现 wsgi 协议的 socket 模块,其作用就是接收 socket IO 事件,然后按 http 协议字段拆分成 environment 参数列表,交给 web 应用

而 web 应用是一个纯粹的处理 env 的数据处理模块,我们编写代码都是在其预留的编程接口上处理数据而已

对于内置的开发服务器,我们可以把它替换成任意一个实现 wsgi 协议的服务器,如 uwsgi

我这样理解对吗?

刚搜了下,发现我之前也有个概念没搞懂,@Donahue 老哥说的是对的。

这样理解就没问题了:

uWSGI 和 Gunicorn 是实现了 WSGI server 协议的服务器

Django ,Flask 是实现了 WSGI application 协议的 web 框架

浏览器 <-http 协议-> nginx(非必要) <-http 协议-> uWSGI/Gunicorn <-WSGI 协议-> Django/Flask

参考: blog.css8.cn/post/9378118.html

问题:“而在前后端分离架构中,好像多了个前端服务器,我想问下这个前端服务器有什么用?”
回答:我猜楼主应该是刚刚接触前端没多久吧,那就得用初学者的思维跟你讲(没有任何恶意,谁都是从初学者过来的不是吗)。你应该用过 VS code 代码编辑器以及这个编辑器自带的 live server 插件吧,没有用过的话下载一个试试,记得把插件装上去,不会就网上搜索一下,我把 vs code 和那个插件的链接放在最后了。你在桌面建一个文件夹,命名为 test (也可以随意命名,不过建议用字母),然后文件夹里面新建一个叫 index.html 文档,文档里面写以下内容然后保存:

<!DOCTYPE html>

Hello world!


然后你尝试两种方法:
1 )直接把这个 index.html 文档拖到浏览器去;
2 )用 VS Code 打开这个文件夹,然后点击右下角 Go Live ;

你会发现这两种方法都能正常显示你的 html 静态页面。第一种方法就跟用 word 打开 word 文档一样,是直接访问本地文件;第二种方法,就是 vs code 给你搭建一个简易的 http 服务器,然后通过这个服务器来运行你的 html 静态页面。好了,现在回到你的问题,你问题里面的“前端服务器”跟这个编辑器自带的 http 服务器本质上是一样的,他只是 host 你的静态页面而已。

上面是最简单的演示,你也可以在 test 文件夹里面加入 css 和 js 文件,当然 js 文件里面也可以用上 ajax 或者 fetch ,然后在 html 里面引入他们,然后在用以上两种方法操作一遍,你会发现也是一样的。就是说,你可以不用“前端服务器”直接用浏览器打开本地文件,也能搭建一个完整的网页程序。是不是很激动,有没有!有没有!

如果你想在其他电脑访问你这个前端静态页面,你总不能让人家开个 teamviewer 远程访问你这个电脑来查看 web 页面吧。所以“前端服务器”你可以简单理解为,他只是为了方便在任何一台电脑访问你这个前端静态页面。至于把后端数据渲染到 html 页面啥的,那都是浏览器干的事情。我去,一步小心码了这么多字。。。。

VS CODE: code.visualstudio.com
Live Server: marketplace.visualstudio.com/items?itemName=ritwickdey.LiveServer

前端服务器主要是用来处理静态资源的,比如图片,css ,scripts ,他们一般性能更好,而且静态资源一般不需要状态信息,比如 cookie ,浏览器就不需要发送不需要的信息,简化 http 请求,提高速度

web 服务器(应用服务器)主要是处理动态逻辑的,比如数据库访问

对的,可以看看 django 源码,
django 框架本身是一个 wsgi 协议的 application, django 的作用就是为我们解决了 url 路由等后端开发中常用的组件,以及将 environment 参数列表封装成 request 对象,返回值封装为 response 对象,简化后端开发流程。

启动一个 wsgi 服务器,会实例化一个 WSGIHandler 对象, 这个对象的__call__(self, environ, start_response)就是请求处理的入口,每一个请求到 web 服务器,如果是动态请求就会被转发到 wsgi 服务器,wsgi 服务器将请求相关参数封装成 environ ,调用 wsgiHandler 对象的__call__()函数处理请求,django 框架从这里开始为我们封装请求对象、进行路由转发等操作

其中 web 服务器到 wsgi 服务器是怎么转发的,多线程 /多进程下会实例化多少个 WSGIHandler 对象这两个问题我还不懂,也想知道

前后端分离指的主要是渲染视图和数据分离, 不一定前端非得有专门的服务器。

比如完全可以直接生成静态的 html ,用户请求之前这个页面就已经生成完成, 然后由浏览器直接渲染这个完整的 html ,再执行 js ,通过 ajax 读取到数据 A ,再把数据也渲染出来。当用户访问其他页面的时候就是访问另外一个 html ,从页头到页脚都重新读取重新渲染不管一样不一样,然后再去读取另一个数据 B 。

但这样就出现了一个最主要的问题,当是搜索引擎爬虫访问或者用户网络不好的时候,他们只能看到一个空框架没有任何数据内容。
为了解决这个问题,所以才又专门准备一个渲染服务器,也就是你说的前端服务器,html 页不再是直接生成好的空框架页面了, 而是可以先读取空框架模版再由渲染服务器去访问后端服务器获取数据,然后生成包含数据的 html 页,这时候再返回给用户。

我之前也是这么理解的,最近接触到 Next.js ,有点颠覆我的认知了

F12 ,然后重新刷新下浏览器。标签选到 fetch/xhr,这个一般就是 ajax 请求到后端服务器的。那个 ajax 请求也是 js 运行的一个 http 请求,可以重载刷新整个页面的情况下对部分对象进行修改。一般也通过 nginx 的 location 规则转发,看 V2EX 应该也是 /api 这个前缀来匹配到转发到后端服务器的。js 这些静态资源则是放在另一个前端应用服务器上(放 vue 之类打包好的前端项目,这个也可以直接放到转发的这台 nginx 上)。同一域名基本上是 nginx 接收所有请求(或者一组).
楼上大佬说的服务端渲染不熟。感觉楼主说的渲染是不是说的 django 的模板渲染这个部分和浏览器渲染搞叉了

我觉得主要是在于理解,上面的流程图可圈可点.....

web 服务----NGINX/iis/....
应用服务---django 、flask 、.....

1 、浏览器是解释 html/js/css 语言的,这个万变不离其宗;
2.1 、涉及转发 /代理类型的,由 web 服务进行转发请求,获取结果后响应给浏览器;
2.2 、不涉及转发,则本地查找静态文件,响应给浏览器;
3 、浏览器根据响应的结果进行渲染

--jQuery 时代[特指前后端混合的时期]
一般所有请求都会转发给应用服务,应用服务会把完整的代码返回给浏览器;
后面 ajax 开始普片,大部分是 html 画个模板,数据通过 ajax 请求返回。

--vue/react 时代
前后端完全分离,前端搭建好框架,所有数据由后端提供;
*此时引出一个问题就是后端和前端的开发并不同步,前端需要测试数据的时候后端还没写好接口,mock 就出现了,伪装返回数据(当然返回数据格式自己写,按和后台的约定即可)。
*另外浏览器访问本地文件是受限的,所以 IDE 一般内置一个小型 web 服务,以此来缩小线上和开发环境的距离,而且方便。

无论 ajax 或 vue/react ,他们共通点都是数据根据 api 接口返回,但搜索引擎无法针对纯 API 返回数据进行高效索引,也无法索引到 vue/react 的代码(因为只有骨架没内容)。所以对搜索引擎收录是不友好的,此时就有针对这个问题的解决方案 SSR ,如果方位时标识为搜索引擎,返回一段简单的 html 代码+数据,如果是正常浏览器访问,则按正常形式返回。

题外话,无论请求流程怎么转,万变不离其宗的就是发出请求,收到响应。
但是我做过一个项目由于图片比较多,且后期才加入 COS 服务的,做法上:
浏览器->CDN->COS-> NGINX-> PHP
浏览器发出请求:1.jpg
CDN:检查是否有 1.jpg 的缓存,没有就去 COS 拉取;
COS:检查是否有 1.jpg 文件,没有就去 NGINX 拉取;
NGINX:检查是否有压缩的 jpg 文件(默认只返回 w750;q.8 的),没有则转发给 PHP;
PHP:返回原图,同时把图片加入 redis_queue 里面,队列形式进行压缩。

不过后来 COS 有图片压缩功能后,直接就把 PHP 那块砍了....

*其实中间经历了什么主要是看你怎么配置,这个不是浏览器关注的,就好比谁做省长我无所谓,我只要做省长夫人即可。

渲染不要 浏览器的 事情嘛?

.wwads-cn { border-radius: 3px !important; } .wwads-text { color: var(--link-color) !important; }