架构师教你来学大型网站架构的技术细节:前端架构的工作原理

前端架构

前面从宏观的角度介绍了大型网站架构。

从本篇开始,我们将着眼于细节,逐一展开介绍前面章节中提到的前端、后端、云计算服务分层的技术架构。

其中,前端部分是直接影响用户体验的,因此本篇先介绍前端部分的架构。

需要注意的是,这里的前端指的是B/S架构网站中的前端网页,是静态网页。

前端的工作原理

在讨论前端架构之前,我们先搭建一个前端Web服务器,再通过构造一个简单的网页来了解其工作原理。在了解前端网页的工作原理之后,我们才能更好地理解前端架构需要注重的细节。

Web服务器搭建

网页想要被非本机的浏览器访问,便需要搭建一个Web服务器。目前比较主流的Web服务器软件有三个,即Apache、IIS和Nginx。

Apache是目前使用最多的Web服务器软件,它拥有极其稳定的性能,扩展模块全面、多样,且可以运行在Windows和Linux等多个平台上;IIS是微软提供的互联网基本服务,它除了提供Web服务外,还提供FTP、NNTP和SMTP等服务,不过IIS只能运行在Windows操作系统上;Nginx是轻量级的Web服务器软件,它支持负载均衡和反向代理等功能,扩展模块虽然没有Apache全面,但比Apache占用的内存和资源少,在高并发处理上表现更好。

Web服务器的选取需要根据具体情况而定。对于一般的大型网站而言,因为服务器操作系统一般以更为稳定的Linux为主,并且服务器需要应对大量的网页请求(高并发),所以本书选用Nginx作为Web服务器软件。

Nginx的安装以Windows系统和CentOS系统为例。选择这两个系统进行介绍是因为Windows一般是开发人员在开发时使用的操作系统,而CentOS一般是网站服务器操作系统。

1. 在Windows系统中安装Nginx

在Windows系统中安装Nginx的具体操作步骤如下:

(1)从Nginx官网(http://nginx.org/en/download.html)下载Nginx,一般选择下载稳定版本。官网上的Nginx版本划分如图3.1所示,这里我们下载Windows系统上的稳定版本。

图3.1 Nginx官网上的版本划分

(2)下载完Nginx压缩包后,将其解压到一个目录下,其目录结构如图3.2所示。其中,html文件夹是默认存放网页资源的地方,conf文件夹存放的是Nginx的相关配置文件,logs文件夹存放的是Nginx的运行日志。

图3.2 解压后的Nginx目录结构

(3)修改Nginx配置,Nginx的配置文件是conf/nginx.conf。默认的配置文件如代码3.1所示,其中,“…”是省略的意思,“#”后面是对属性的注释。如果是本地调试,保持默认配置即可。

代码3.1 默认的Nginx配置

server {

listen 80; #端口,80为HTTP的默认端口

server_name default_server; #服务名称

location / {

root html; #网页资源文件夹,这里的html指的是图3.2中的html文件夹

index index.html; #网站默认网页

}

}

(4)启动服务,双击图3.2中的nginx.exe文件即可,运行窗口会一闪而过。在不修改默认配置且正常启动的情况下,在浏览器的地址中输入http://localhost会打开Nginx的默认网页,如图3.3所示。这个默认网页是图3.2中html文件夹里的index.html。

图3.3 Nginx的默认网页

如果不能正常启动,可以查看图3.2中logs文件夹里的运行日志。不能启动一般都是由于端口冲突造成的,此时可以修改步骤(3)中的listen配置项。

如果在步骤(3)中修改了server_name和listen配置项,则需要在浏览器的地址栏中输入server_name:listen。例如,将server_name设置为192.168.3.3,listen设置为8081,那么在浏览器的地址栏中应该输入http://192.168.3.3:8081。一般情况下,将server_name设置成default_server即可。

另外,可以通过在任务管理器中结束所有nginx.exe任务来关闭Nginx服务,如图3.4所示。

图3.4 关闭Nginx服务

(5)设置防火墙。如果非本机的浏览器想要访问网页,则需要设置防火墙端口的权限。对于大型网站而言,服务器系统一般为Linux,Windows系统下的Nginx安装一般只是为了方便本地开发,非本机浏览器访问的场景比较少,因此防火墙的设置不是必需的。如果开发时有非本机访问的情况,可以暂时关闭Windows防火墙。

2. 在CentOS系统中安装Nginx

在Centos系统中安装Nginx时,虽然也可以从官网上下载安装,但是操作比较复杂。因此,这里推荐使用yum安装,这种安装方式简单方便且不易出错,具体操作步骤如下:

(1)添加Nginx源。在默认情况下,CentOS系统中不包含Nginx源,添加Nginx源的命令如代码3.2所示,其中“\”为换行符。添加成功后,会在/etc/yum.repos.d目录下多出一个nginx.repo文件。

代码3.2 添加Nginx源的命令

sudo rpm -ivh \

http://nginx.org/packages/centos/7/noarch/RPMS/nginx-release-centos-7-0.

el7.ngx.noarch.rpm

(2)安装Nginx的命令如代码3.3所示。在CentOS系统中,Nginx默认安装在/etc/nginx目录下,配置文件是/etc/nginx/nginx.conf,运行日志的路径是/var/log/nginx/,默认网页资源存放在/usr/share/nginx/html/目录下。

代码3.3 安装Nginx的命令

sudo yum -y install nginx

(3)修改配置。Nginx的配置文件是/etc/nginx/nginx.conf,默认的配置文件如代码3.4所示,其中,“…”是省略的意思,“#”后面是对属性的注释。如果是本地调试,保持默认配置即可。

代码3.4 CentOS系统中的Nginx配置

server {

listen 80; #端口,80为HTTP的默认端口

server_name default_server; #服务名称

location / {

root /usr/share/nginx/html; #网页资源文件目录

index index.html; #网站默认网页

}

}

(4)启动服务。设置Nginx开机自动启动、启动服务和停止服务的命令如代码3.5所示。其中,“#”表示注释部分,输入命令时不需要输入,后文不再赘述。

代码3.5 Nginx的启动命令

sudo systemctl enable nginx #设置开机启动

sudo systemctl start nginx #启动服务

sudo systemctl stop nginx #停止服务

(5)配置防火墙。一般经过步骤(4)的配置就能启动Nginx了,但如果防火墙是开启状态的话,非本机浏览器是不能访问网页的,因此这里需要配置防火墙。配置防火墙的命令如代码3.6所示,命令需要按顺序执行。

代码3.6 配置防火墙的命令

sudo firewall-cmd --add-service=http --permanent #开启防火墙的HTTP服务

#打开端口,根据Nginx的端口配置,替换80即可

sudo firewall-cmd --add-port=80/tcp –permanent

sudo firewall-cmd --reload #重启防火墙

防火墙配置成功后,非本机浏览器访问网页的效果如图3.5所示。

图3.5 非本机浏览器访问网页

需要注意的是,如果有明确的Nginx版本要求的话,需要从官网上下载安装。

构造一个简单的网页

安装完Nginx后,有一个默认的网页,如图3.3或图3.5所示。这个网页只有一个HTML文件,这与我们平常的网页结构不一样,这样的网页会造成我们对前端网页工作原理的理解不够全面。我们一般把一个网页分成一个HTML文件、多个CSS样式文件和多个JavaScript文件,因此在介绍前端工作原理之前,我们还需要构造一个简单的网页,该网页将包含一个HTML文件、两个CSS文件和两个JavaScript文件。

注意:本节构造的网页只是为了方便对3.1.3小节的讲解,其中的编码细节不作为现实编码的参考。

1.创建一个HTML文件

创建一个abc.html文件,如代码3.7所示。HTML文件主要用来设置网页的属性和定义页面元素,这个页面的元素主要包括标题、一行文字和按钮。

代码3.7 abd.html文件的代码

<!DOCTYPE html> <!-- 采用HTML5标准 -->

<html> <!-- 文档开始点 -->

<head> <!-- 头部元素开始点 -->

<meta charset="utf-8"> <!-- 设置网页属性 -->

<title>简单的例子</title> <!-- 网页标签的名称 -->

<link rel="stylesheet" href="css_1.css"> <!-- 引用CSS文件 -->

<link rel="stylesheet" href="css_2.css">

</head> <!-- 头部元素结束点 -->

<body> <!-- 主体开始点 -->

<div class="title">简单的翻译例子</div> <!-- 网页元素:标题 -->

<span id="id_text">Hello World.</span> <!-- 网页元素:一行文字 -->

<button οnclick="translates()">转换</button> <!-- 网页元素:按钮 -->

</body> <!-- 主体结束点 -->

<script src="js_1.js"></script> <!-- 引用JavaScript文件 -->

<script src="js_2.js"></script>

</html> <!-- 文档结束点 -->这里需要明确一点,除了用iframe嵌入网页的情况外,一个网页只有一个HTML文件。在HTML的规范中,除了<!DOCTYPE html>这样的标准声明外,其他的内容都应该包含在<html></html>中。而<html></html>里面的内容主要包含头部元素(<head></head>)和主体(<body></body>)两个部分,头部元素主要包含一些网页属性的设置,而主体才是网页元素被定义的部分。

HTML文件可以引用多个CSS样式文件,引用CSS文件的位置一般在<head></head>中,这样有利于网页的加载,在后续工作原理的讲解中会详细说明。

HTML文件可以引用多个JavaScript脚本文件,引用的位置一般在<body></body>之后,这样有利于网页的显示,也能避免一些错误,在后续的工作原理中会详细说明。

注意:网页元素(如按钮等)写在<body></body>外或<html></html>外,网页仍会正常显示,因为浏览器具有容错机制,但这样并非HTML规范的本意。

2.创建两个JavaScript文件

下面创建两个JavaScript文件,用来进行网页的交互处理。第一个文件命名为js_1.js,如代码3.8所示,其内容主要是定义两个变量。

代码3.8 js_1.js文件的代码

var EnglishText = "Hello World.";

var ChineseText = "你好,世界。";

另一个文件命名为js_2.js,如代码3.9所示,其内容是转换网页上的文字。在网页上单击按钮后会调用此函数,代码3.7中的“<buttonοnclick="translates()">转换</button>”绑定了translates()函数。

代码3.9 js_2.js文件的代码

var translates = function() {

var text = document.getElementById("id_text").innerText;

if(text == EnglishText){

document.getElementById("id_text").innerText = ChineseText;

} else {

document.getElementById("id_text").innerText = EnglishText;

}

}

JavaScript本身没有命名空间之类的限定,只要在同一个HTML文件中被引用就可以互相调用。例如上例中,页面引用了js_1.js和js_2.js两个JavaScript文件,而js_2.js中的函数则可以直接使用js_1.js中定义的变量。

注意:这里的两个JavaScript文件内容可以写在一个JavaScript文件中,分为两个JavaScript文件只是为了模拟多个JavaScript文件的情况。

3.创建两个CSS文件

下面创建两个CSS样式文件。CSS样式文件的作用是给HTML文件中的网页元素提供样式和布局设置,如提供字号大小、间距和颜色等设置。一个CSS文件命名为css_1.css,如代码3.10所示,其内容是定义标题的样式。代码3.7中的“<div class="title">简单的翻译例子</div>”绑定了title这个样式属性。

代码3.10 css_1.css文件的代码

/* 定义标题的样式 */

.title{

margin-bottom: 10px;

font-size: 32px;

}

另一个CSS文件命名为css_2.css,如代码3.11所示,其内容是定义文字的样式。代码3.7中的“<span id="id_text">Hello World.</span>”是通过id(id_text)绑定样式属性的。

代码3.11 css_2.css文件的代码

/* 定义文字的样式 */

#id_text{

font-size: 20px;

}

注意:这里的两个样式定义可以写在一个CSS文件中,分成两个CSS文件只是为了模拟多个CSS文件的情况。

4.发布网页

为了后续对网页地址说明的方便,下面在Nginx的网页资源目录下创建一个sample文件夹,把刚创建的abc.html、css_1.css、css_2.css、js_1.js和js_2.js文件放到sample文件夹内。以Windows系统中的Nginx为例,目录结构如图3.6所示。

图3.6 Nginx中的目录结构

在Nginx默认配置的情况下,在浏览器的地址栏中输入
http://localhost/sample/abc.html,显示的页面如图3.7所示。

图3.7 页面效果

单击“转换”按钮后将会转换文字,如图3.8所示。

图3.8 单击按钮后的网页效果

前端网页的工作原理

在成功构造一个简单的网页并运行后,接下来介绍前端网页的工作原理。

通过3.1.1小节和3.1.2小节的介绍我们知道,一个网页要运行起来,至少需要两步。第一步是搭建Web服务器,让浏览器能请求并得到网页资源文件;第二步是把网页资源文件放到网页服务器上。前端网页的工作原理也分为两部分介绍:一部分是浏览器加载网页资源,介绍浏览器与Web服务器的关系;另一部分是浏览器运行网页,介绍浏览器和网页的关系。

注意:本节默认以Chrome浏览器作为说明的对象。虽然不同浏览器运行网页的工作原理会有所区别,但是整体流程是基本相同的。本节在原理讲解上会省略很多细节,读者只需要大体了解即可。

1.浏览器加载网页资源

一个网站是由很多个网页组成的,每个网页都有自己的地址。以3.1.2小节中的网页为例,在浏览器的地址栏中输入网址(
http://localhost/sample/abc.html)并按Enter键后,相当于向服务器发送了一个请求。这个请求包含多个部分,如图3.9所示,其中为了对网址有一个全面的解释,此处补充了端口部分和参数部分。

说明:类似于这样的网址或某个资源的网络地址,一般被称为URL(Uniform Resource Locator,统一资源定位符),后面将以URL代替对网址或网络地址的描述。

图3.9 网页地址的结构

协议(protocol):请求网页资源的协议一般为HTTP或HTTPS。由于HTTPS具有更高的安全性,而且浏览器会对使用HTTP的网站提示“不安全”,所以现在一般使用HTTPS。如果使用HTTPS,则需要对Web服务器进行额外配置。不过,HTTPS只是在HTTP的基础上做了通信加密,这个加解密的过程是浏览器和Web服务器自动完成的,我们只需要配置好Web服务器即可,这对网站开发本身没有影响。

主机名(hostname):这里可以是域名或者服务器的公网IP。如果是域名的话,那么在真正发送请求到网页服务器之前,域名解析服务器(DNS)会自动把域名转换成服务器的公网IP。域名解析服务器是公共资源(一般不能对其进行操作),在购买域名后,在购买域名的网站绑定域名和服务器的公网IP,即可自动同步信息到DNS。

端口(port):一般使用默认端口。如果是默认端口,那么可以省略端口部分。HTTP的默认端口是80,HTTPS的默认端口是443。

协议+主机名+端口:有了这三部分,Web服务器就能收到用户发送的请求。Web服务器收到请求后,会继续对URL的后续部分进行解析。

路径(path):Web服务器根据路径寻找资源,在如图3.9所示的例子中,Web服务器会根据/sample/abc.html这个路径找到abc.html网页文件并把文件内容发送给浏览器。不过,资源不一定都是文件,也可能是后端接口。但无论请求的资源是什么,Web服务器都会以字节流形式返回内容。

参数(parameters):从“?”开始到最后都为参数部分,多个参数之间用“&”隔开。参数一般用在请求资源时,其处理不一定在服务器端进行,也可能由网页的JavaScript脚本处理。

说明:网址还可以添加信息片段部分,这部分一般是在整个网址的最末端以“#”开始,如
http://localhost/sample/abc.html?id=123&type=1#home。信息片段部分只会被网页的JavaScript脚本处理,因此,如果只更新这部分,那么网页是不会被浏览器刷新的。

以上是对URL的介绍,下面我们回到浏览器加载网页资源的过程。服务器在接收到请求后返回网页文件,浏览器对网页文件进行解析,当发现网页文件中有其他需要下载的资源时(如代码3.7中的<linkrel="stylesheet"href="css_2.css">),会向Web服务器请求新的文件。以3.1.2小节中的网页为例,浏览器加载网页资源的全过程如图3.10所示。

图3.10 浏览器加载网页资源的全过程

注意:文件资源都以字节流的形式返回,浏览器每获取一部分内容就会立即对其进行处理,而不会等全部获取文件资源后再解析。资源请求的方式一般是异步请求,即不会等上一个请求的资源全部获取后再开始请求下一个网页资源。

对于如图3.10所示的请求过程,可以按F12键打开浏览器的开发工具查看具体的请求细节。以Chrome浏览器为例,其开发者工具如图3.11所示。

图3.11 使用开发者工具查看资源请求的细节

2.浏览器运行网页

浏览器显示网页的工作流程大概可以分成4部分,即构建DOM树、构建呈现树(render tree)、布局处理和绘制页面,如图3.12所示。因为浏览器对网页进行的是流式处理,所以此流程不是一次性完成的,而是每解析一部分网页,都可能会执行一次流程。

图3.12 浏览器显示网页的流程

可以将DOM树理解为结构树或内容树。浏览器解析HTML文档,并将各元素标签逐个转换成DOM节点,即把HTML文档中的标签转换成一个结构树。这个解析和构造过程是流式的,浏览器每获取HTML的一部分,便会立刻对其进行解析。以3.1.2小节中的网页为例,其DOM树如图3.13所示,其中省略了元素的属性。

图3.13 DOM树

呈现树是网页显示部分的数据结构。在构造DOM树的同时,也会解析外部CSS文件及元素标签中的样式设置,浏览器会构造一个与CSS样式对应的CSSOM树。浏览器会利用CSSOM树中的视觉属性(如颜色和尺寸)和DOM树对应的元素构造另一个结构——呈现树。呈现树和DOM树相对应,可以简单地理解为在DOM树的节点上添加视觉属性。但是呈现树与DOM树并非一一对应,呈现树只记录在浏览器上可显示的部分,一些非可视化的元素(如<head></head>)和一些被隐藏的元素是不会被记录在呈现树上的。仍以3.1.2小节中的网页为例,其呈现树如图3.14所示。

图3.14 呈现树

呈现树构造完毕之后,进入布局处理阶段。布局处理阶段主要是根据呈现树和浏览器窗口的大小计算出每一个节点出现在屏幕上的确切坐标。最后进行绘制,即把网页描画出来。

浏览器显示网页是渐进的过程,上文提到的流程并不是一次完成的。浏览器会尽快将内容显示在屏幕上,而不是等到整个HTML文档解析完毕才开始构建呈现树和设置布局。

在不断接收和处理网络资源的同时,浏览器会不断解析并显示内容。在以上过程中并没有提及JavaScript文件的作用。浏览器有专门的JavaScript解析器处理JavaScript文件。JavaScript解析器中会有一些预编译的流程,不过这是内部行为,我们不必过于关心。

JavaScript脚本可以响应网页事件(如单击和拖动等),调用浏览器的一些功能(如全屏等),以及与远端服务器通信(请求数据)等。JavaScript脚本对于网页显示而言,其最大的作用是可以修改HTML内容(即可以改变DOM树,一般来说,JavaScript脚本修改HTML内容的操作也被称为DOM操作),HTML内容发生变化后,浏览器会自动进行网页的重排和重绘,从而显示新的网页。例如3.1.2小节中的网页,当单击按钮后,JavaScript脚本会自动改变网页的文字内容。

以上是前端网页的工作原理。但是还有一个问题没说清楚。在3.1.2小节中为什么要把JavaScript文件的引用放在<body></body>的后面,而将CSS文件的引用放在<head></head>中呢?

这是因为,浏览器虽然对HTML文件是流式处理的,但是JavaScript文件可能会修改HTML的内容,即可能会影响DOM树的结构,从而影响呈现树的构造及后续的流程,所以当浏览器处理到<script></script>标签时,会停止对HTML文件的解析,先加载并处理完JavaScript文件后再继续解析HTML文件。在一般情况下,JavaScript脚本只在响应事件(如单击)后才会操作HTML元素,因此把JavaScript的引用放在<body></body>的后面有利于DOM树的构建,从而有利于网页尽早显示出来。再者,因为JavaScript脚本可能会操作DOM树,如果放在开头,则可能会导致由于被操作的元素还没被构造出来而发生错误。

而CSS文件由于不影响DOM树的构造,因此当浏览器处理到<link></link>标签时会继续往下解析。因为CSS文件会影响呈现树的构造,所以会暂停呈现树的构造,直到CSS文件下载结束并解析完成。把CSS文件放在<head></head>中,可以让浏览器尽早加载CSS文件,这样有利于呈现树的构造,从而有利于网页尽早显示。

以3.1.2小节中的网页为例,整个网页解析的渐进过程如图3.15所示。其中,黑色粗线为构造过程,这里只体现了DOM树的构造和呈现树的构造这两部分,而省略了布局处理和绘制这两个浏览器的自发行为。

图3.15 网页构造的全过程

在不同的浏览器中,具体构造DOM树和呈现树的暂停点和开始点是有所区别的,不过大体上仍然如图3.15所示。

本文给大家讲解的内容是大型网站架构的技术细节:前端架构,前端的工作原理

  1. 下篇文章给大家讲解的内容是大型网站架构的技术细节:前端架构,前端架构需要解决的问题

  2. 觉得文章不错的朋友可以转发此文关注小编;

  3. 感谢大家的支持!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值