https://developer.zsxyww.com/blog 中山学院网络维护科 Blog 2025-07-13T03:23:58.000Z https://github.com/jpmonette/feed 中山学院网络维护科 Blog https://developer.zsxyww.com/img/favicon.ico <![CDATA[Web的历史1️⃣-HTTP]]> https://developer.zsxyww.com/blog/Web的历史1️⃣-HTTP 2025-07-13T03:23:58.000Z 注:这些文章本来是打算作为开发组后端培训文本的开篇而写的,但是我发现文章的内容过于复杂且有点离题,所以稍微修改了一下单独作为3篇独立的文章发布。

本文章的许多图片都直接来自 MDN,在这里先感谢图片的创作者。

网络的起源

人们最初发明网络的目的很简单:在不同的电脑之间传输文件。那个时候没有U盘,也没有蓝牙,计算机领域的前辈们必须从头设计一个高效的传输文件的办法。

假如我们的电脑上有一个文件example.docx,放在我们电脑的文件夹documents里面,我们如何使用最原始的方法把它传递给另外一台电脑呢?

早期的程序员编写了一类叫做"HTTP服务器"的程序,程序的功能是选择电脑上的一个文件夹,将这个文件夹暴露在互联网上让所有人操作。这个文件夹叫做HTTP服务的“根目录”。

要使用这个程序,我们还要先给电脑分配一个IP地址,IP地址是一台电脑在互联网上的唯一标识,假如我们电脑的的IP地址是123.45.67.89。我们在这台电脑上启动一个HTTP服务程序。在程序的设置里指定根目录是我们存放文件的文件夹documents

前面说过,这个HTTP服务程序的作用是把你电脑上一个指定的文件夹(我们称为“根目录”)暴露在互联网上,所有人都可以下载这个文件夹下的文件。于是这个时候,我们打开随便另一台电脑(前提是有网),在浏览器地址栏中输入http://123.45.67.89/example.docx,这时候就会发生神奇的事情:浏览器会提示你下载example.docx,这样,文件就从一台电脑传输到了另一台电脑。

假如documents文件夹下面还有另外一个文件hi.txt,那么当你在浏览器地址栏中输入http://123.45.67.89/hi.txt时,你就会发现浏览器显示了hi.txt这份文件的内容,因为浏览器可以直接显示txt文件,而不能直接显示docx文件。

IIS根目录

IIS是Windows官方的HTTP服务器,有着图形化的配置界面,其它大部分HTTP服务器都需要通过命令行和配置文件进行配置。

HTTP协议

等等,刚才所讲的操作为什么能发生呢?当我在浏览器中输入网址并按下回车键的时候,这一切的背后到底发生了什么?

我们之前提到了,要把电脑中的一个文件夹暴露在网络上供所有人访问,就需要启动一个"HTTP服务程序",这个HTTP是什么呢?

HTTP协议,是HyperText Transfer Protocol的缩写,即"超文本传输协议"。是通过网络在电脑之间进行文件传输的默认协议。进行HTTP文件传输的双方,一个称之为”客户端“(Client),一个称之为"服务端"(Server)。

我们刚才就是在我们的电脑上部署了HTTP服务端,而浏览器就是我们通信中的客户端。浏览器首先对服务器发送信息(行话叫做"请求"),要求获取某个文件;然后服务器返回这个文件(行话叫做“响应”)。

一次完整的HTTP通信总是由客户端请求(Request)和服务端响应(Response)组成。在HTTP协议中,总是由客户端先发起一次HTTP请求,然后由服务端返回这次请求的响应,这样才是一次完整的HTTP通信。

实际上,HTTP协议中的“客户端”不仅仅是指浏览器,但我们通常用的浏览器确实就是最常见的HTTP客户端。但其实像curl这样的命令行工具,或者手机APP,或者是浏览器里的JavaScript代码,当它们向服务器请求数据时,也可以被称作客户端。

HTTP请求示意图

实际上,HTTP协议不仅可以获取一个文件(就像我们所做的),也可以向服务端上传一个文件,或是修改某个文件的内容...

一次HTTP通信要进行什么操作(获取or上传等),由HTTP请求中的“方法”指定,方法是"请求头"的一部分。

"请求头"是什么?别着急,下面我们介绍HTTP协议的具体内容:

请求

前面说了,HTTP通信总是以客户端发送请求开始,服务端是没法主动给客户端发信息的。

一个HTTP请求包含请求头(HTTP Request Header)和请求体(Request Body),请求头主要包含了:

  • 协议版本:告诉服务器,这次交流,要使用哪个版本的HTTP协议
  • 目标:要访问哪个电脑,例如刚才的123.45.67.89
  • URI:要操作哪个文件,例如example.docx,IP地址或者网址后面的内容就是URI了
  • 方法:要对这个文件做什么,比如获取这个文件的内容,还是在服务器上创建这个文件
  • 自定义参数:除了上面的信息,客户端还可以发送一些额外的内容给服务端,这些内容总是以Key=Value的形式存在。在正常的URI后有一个?,表示参数的开始,&表示不同参数之间的分隔。

例如:http://123.45.67.89/example.docx?compressed=true&method=gzip

这就是让服务器压缩发来的文件,使用gzip压缩。

参数的内容不是HTTP标准,也就是服务端如何理解参数,完全靠程序员写的代码。如果程序员没有设置,那么参数就毫无用处。

HTTP方法

HTTP方法定义了这个请求具体要对指定的文件做什么,其中:

  • GET:获取指定文件的内容。
  • POST:上传一个文件,内容放在请求体(下面会讲到)。
  • DELETE:删除某个文件。

其实这里还有一些方法的,我们以后讲。

我们日常使用浏览器,比如在地址栏中输入baidu.com,其实就是在对这个地址做GET请求,浏览器会把你的输入内容自动补全成 GET http://www.baidu.com/ 这一HTTP请求

提示

打开浏览器的F12开发者菜单,选择"Network",可以看到你在访问这个网站时与服务器所进行的所有HTTP请求

在浏览器地址栏里直接输入网址,默认就是发送GET请求。你可能会好奇,POST这些请求是怎么发出去的呢?这通常是由网页中的JavaScript代码在背后发送的。我们在之后的内容会讲到这一点。

请求头

请求体

对于某些方法,需要在请求时向服务器夹带一些东西(比如POST需要你带上新文件的内容),请求体就是装载这些东西的.

像GET请求就没有请求体,因为GET请求不需要夹带信息.

提示

正常地使用浏览器,是没法做出GET外的请求的,但是浏览器在控制台里可以自定义请求,以Firefox为例,在F12的Network一栏中点New Request(有一个加号)就可以发送自定义请求。curl等HTTP命令行工具也可以发送请求.

响应

回应也分为回应头和回应体,回应头主要包含:

  • 状态码:针对请求的回应有对应的状态,比如,如果GET请求没有问题,成功返回的话就返回200 OK状态码,如果找不到请求里对应的文件就会返回404 Not Found
  • 内容格式:向浏览器说明回应体(若有)是什么格式的,文本文件的编码设置等,比如html文件?图片?docx文档?有没有被压缩?如果有,压缩格式?这决定了浏览器如何对待这些文件
  • 内容的大小:返回所请求资源的大小(若有)

回应体就是包含了回应的主体内容了,如果是GET请求的话,那么就回应了所GET文件的内容,如果是其他请求的话,可能也会没有回应体,具体看使用的方法

回应体

例子

GET方法从服务器获得一个资源,我们在浏览器的地址栏输入一个地址时,就是在对这个URI做GET请求,前面的例子也是通过GET方法来进行的。

比如,发送GET http://developer.zsxyww.com/test/test.txt,那么就会把我们服务器根目录上test文件夹里面的test.txt这个文件发过来

POST方法向服务器上传一个资源,例如使用某个客户端发送POST http://example.org/sheet.xlsx,在request body里面带上你的这个文件,那么example.org网站的根目录下就会多出一个叫做sheet.xlsx的文件

除了上面介绍的这些方法,还有DELETE,PATCH等方法,分别对应了删除,修改一个资源,你可以在你的电脑上通过curl等程序,或者通过浏览器控制台来进行常规的GET之外的方法请求

提示

httpbin.org 这个网站可以让你试验HTTP协议的方法

HTTPS

HTTP在网络上是明文传输的,也就是说客户端和服务器之间的每个网络节点,每个人都可以看到。这显然不方便隐私,所以人们发明了HTTPS,也就是把HTTP的正文加密了,HTTPS可以说是当今互联网的基石,有了HTTPS,我们才能放心地在网上输入密码,用银行卡付款等。

网页与HTML

随着网络的发展,人们发现互联网的潜力远不止于传输文件。特别是浏览器的普及,人们希望能直接在网页上展示丰富的信息,而不只是把浏览器当作下载工具。前面我们说过了,浏览器可以直接展示txt文件,但是最大的问题是,txt文件是没有样式的。

比如:txt无法设置字体大小和颜色,无法加粗、倾斜文字,无法创建表格和列表,无法插入图片和链接。这些都是txt的局限。

为了解决这个问题,人们发明了HTML(HyperText Markup Language),就是“超文本标记语言”,HTML的核心思想是:在普通文本中加入特殊的标记,告诉浏览器如何显示内容。

比如<i>你好~</i>就是指示浏览器以斜体的方式显示你好~这段文字。你可以用系统自带的笔记本像打开txt文件一样打开HTML文件,只不过浏览器默认是加载渲染之后的界面而不是原始的HTML。

除HTML之外,人们还发明了CSS与HTML搭配使用,CSS可以对样式做更复杂高级精细的控制,这里就不细说了

回到我们的主题,不管使用的是什么方法,操作的是什么文件,HTTP协议传输的对象都是一些固定静态的文件,其内容在服务器上是固定不变的(除非手动修改),这样的网页称为静态网页。

静态网页中,所有用户看到的内容都相同,内容不会根据用户行为动态变化,服务器只负责传输文件,不进行复杂计算。

我们的wiki就是静态网页,在服务端上都对应着HTML页面,只不过加了非常多的样式显得很高级。

很多常见的网站(如企业官网、技术文档、个人博客等)都是静态网页,虽然看起来很精美,但本质上就是经过精心设计的HTML和CSS文件。

网页的构成

info

这是三篇系列文章中的第1

点击以跳转:

HTTP(你在看的文章)

动态网页

Web应用

]]>
<![CDATA[Web的历史2️⃣-动态网页]]> https://developer.zsxyww.com/blog/Web的历史2️⃣-动态网页 2025-07-13T03:23:58.000Z 上篇文章我们已经了解了静态网页是如何工作的,但是这样的网页是不能满足大家对互联网的需求的。举例子来说:你访问b站首页bilibili.com,每次刷新,首页上显示给你的视频都不一样,不同的人访问这个首页,显示的也不一样,按理说大家都是访问一个网址,背后应该都是同一个文件,为什么每个人都不一样呢?这种功能是如何实现的?

淘宝上有数不清的商品在售卖,如果淘宝为每一个商品都在服务器目录下面创建一个html文件,好让大家通过访问http://taobao.com/someproduct.html来查看商品信息,那这个工作量就非常大了。而且,这样的网页,基本上没有交互的功能:我们希望用户可以点击按钮就能购买商品,商家在网页后台上操作就能上传商品。这种功能应该如何实现呢?

暂时先不考虑这些高级的问题,让我们先从最基础的讲起:

服务器端内嵌(SSI)

如果你想向网站中插入动态内容,SSI是最简单,最直接的办法,比如我们的wiki有许多页面,但是每个页面都有一些共同的元素:页面头部的导航栏,左侧的列表,页脚等。如果为每个页面都复制一份相同的HTML的话,那就太麻烦了,有没有什么办法,可以使HTML一次编写,到处渲染呢?

SSI(Server Side Includes)就是满足这种需求的一个HTML宏语言。它有点类似于C语言的# include宏:

假设这是我们首页的HTML:

<h1>wiki</h1>
<!--#include file="navbar.html" -->
<div class="article"></div>

假如navbar.html的内容如下:


<tr>
<td>教程</td>
<td>文档</td>
<td>高级</td>
<td><a href="github.com/zscnsd/website">Github</a></td>
</tr>


那么用户访问我们首页时就会看到:


<h1>wiki</h1>
<tr>
<td>教程</td>
<td>文档</td>
<td>高级</td>
<td><a href="github.com/zscnsd/website">Github</a></td>
</tr>
<div class="article"></div>


如果导航栏的界面有变化,那么只需要修改navbar.html即可,不用修改网站中的每一个页面。

没错,SSI的功能就是简单地把指定的内容插入进HTML里。这对一些重复的元素(例如每个网页的页头,页脚,侧边栏)还有一些需要更新的内容很实用。

当然,SSI并没有解决动态网页的问题,它只是把需要手动更新的地方单独拿了出来,使维护静态网站更容易,所以程序员们又发明了CGI技术。

CGI

CGI(Common Gateway Interface) 是第一个真正实现动态网页的技术,它允许Web服务器执行外部程序来生成网页内容。

CGI的工作原理是:当用户访问特定URL时,服务器不是返回静态文件,而是执行一个程序,并将程序的输出作为HTTP响应返回给用户。

(举个天气预报的例子):

Web服务器通常会把能执行的程序(除开静态文件)放在一个叫cgi-bin的特殊目录里。假设我们服务器的这个文件夹里有一个查询天气的Python程序weather.py,当用户访问http://example.org/cgi-bin/weather.py?city=中山&date=2025-06-25时,我们的HTTP服务程序会自动执行放在路径中/cgi-bin/weather.py的这个Python脚本,并且将客户端的请求头和请求体传递给脚本;

脚本解析请求头中city=中山&date=2025-06-25这个参数,在数据库中查询这个日期的天气,然后返回一个HTML给HTTP服务程序,再把这个HTML返回给客户端。

如果没有设置CGI,那么服务程序只会返回给客户端TodayWeather.py这个脚本文件的代码本身。

其实,CGI是一个接口格式,它定义了我们编写程序与HTTP服务程序之间如何交互。通常,HTTP服务程序给CGI程序的输入就是环境变量,输出就是标准输出。

CGI的巧妙之处在于,服务器不是用什么复杂的方式和脚本沟通,而是把请求信息(比如URL参数里的城市)变成程序很轻松就能读到的环境变量。而程序也不需要复杂的操作进行IO,它只需要把生成的HTML代码打印出来,服务器就会自动收集这些打印的内容,然后发回给用户的浏览器。

下面是一个例子:

/var/www/cgi-bin/system-info.sh

#!/bin/bash

# HTTP响应头
echo "Content-type: text/html"
echo ""

# HTML内容
echo '<html>'
echo '<head><title>系统信息</title></head>'
echo '<body>'
echo '<h1>服务器系统信息</h1>'
echo '<p>当前时间:'$(date)'</p>'
echo '<p>内存使用情况:</p>'
echo '<pre>'
free -h
echo '</pre>'
echo '</body>'
echo '</html>'

每次用户访问这个页面,都会看到实时的系统信息,真正实现了动态内容。

虽然CGI现在很少见了,但它建立了一个重要概念:将URL请求映射到程序函数,而不是静态文件。这个思想成为了现代Web开发的基础。

嵌入式脚本

随着动态网页需求的增长,纯CGI编程变得复杂。程序员们希望能够在HTML中直接编程动态代码,这样既保持了HTML的可读性,又能实现动态功能。

这个就是嵌入式脚本,顾名思义就是把脚本和HTML混在一起,在HTML中嵌入脚本;

但是这种脚本和今天的前端JavaScript不同,它是由后端解释执行的,在返回HTML响应之前,HTTP服务程序会检查这个HTML里面有没有可以执行的脚本内容,有的话就执行这些脚本,并且把脚本的输出嵌入到HTML里面。任何有效的HTML也是有效的这类脚本语言。

从CGI到嵌入式脚本的另外一个关键驱动力是性能。CGI每来一个请求,服务器就得创建一个新进程去运行CGI程序,完成后再销毁,开销很大。而嵌入式脚本通常则是直接作为服务器的一部分运行,效率远高于CGI。

JSP

举个例子吧,你可以轻松使用Java来创建动态网页,只需要把Java代码嵌入到HTML里面,使用<% %>包裹住代码:


<html>
<body>
<p>当前时间:<%= new java.util.Date() %></p>
</body>
</html>


复杂一点的例子:


<html>
<body>
<h1>欢迎访问我们的网站</h1>
<p>当前服务器时间:<%= new java.util.Date() %></p>
<p>您是第 <%= session.getAttribute("visitCount") %> 位访客</p>

<%-- 这是JSP注释,不会出现在最终HTML中 --%>
<%
// 这里可以写复杂的Java逻辑
String userName = request.getParameter("user");
if (userName != null) {
out.println("<p>欢迎您," + userName + "!</p>");
}
%>
</body>
</html>

session和cookie

在这段JSP代码中有一个对象叫做session,这是什么呢?实际上,因为HTTP是无状态的协议,意味着两次请求之间是完全独立的,一次请求不应该依赖另一次请求。这显得有点不灵活,于是人们会在HTTP的请求体上夹带一些额外的参数,用于表明用户的身份信息,比如在用户登录网站之后,服务器会给客户端一个密钥,下一次客户端请求页面时带上这个密钥,服务器就知道这是某个用户的请求。在这种模式下,服务器需要为每个用户维护信息,比如最简单地需要维护密钥是对应哪个用户的,这些信息就叫做session。

类似于这样的脚本叫做JSP(JavaServer Pages),它在后端返回时被转换成Java Servlet代码来执行,本质上,JSP是Java Servlet的一种语法糖。至于JSP和Java Servlet都是什么,自行了解吧。

PHP

比JSP更灵活的就是PHP,PHP就是一门纯正的脚本语言了,它的用法与JSP类似,使用<?php ?>包裹代码


<html>
<body>
<h1>欢迎来到我的网站</h1>
<?php
$time = date('Y-m-d H:i:s');
echo "<p>当前时间:$time</p>";
?>
</body>
</html>

也可以这样写,这样就类似于CGI程序的写法了:


<?php
echo "<html><body>";
echo "<h1>欢迎来到我的网站</h1>";
$time = date('Y-m-d H:i:s');
echo "<p>当前时间:$time</p>";
echo "</body></html>";
?>

LAMP

这种动态网页的编写方法流行了很多年,形成了一个叫做"LAMP"的套路:Linux+Apache+MySQL+PHP;就是将电脑装上Linux系统,运行Apache这个HTTP服务端,使用PHP作为动态脚本语言,使用MySQL来存储和访问业务数据。

需要注意的是,这四个都是开源免费的软件,LAMP的兴起,是开源软件运动的标志之一。开源软件使得部署网站的成本极大地降低,推动了互联网的繁荣。如果你想建站,那时候互联网上到处都是"LAMP一键安装脚本"之类的东西,现在也能搜到不少。一个下午就能上线一个完备的网站。这些技术的出现,使得开网站不再局限于大企业才能办得到的事情,一时间互联网上到处都是个人或者小单位的网站,甚至后来出现了诸如Wordpress之类的方案,不会写代码也能开网站。繁荣的生态,网页上丰富的动态内容,形成了被我们称为“Web 2.0”的时代。

LAMP的一个典型反面是微软全家桶:Windows Server+IIS+SQL Server+ASP,这套技术方案需要给微软缴纳高额的授权费用,在当时基本上只限于追求稳定和售后服务的企业使用。我们的文章也没有怎么介绍这些技术。不过IIS对于个人用自己的电脑建站还是非常方便的。(当然国内没有公网IP那是另一回事了╮( ̄▽ ̄)╭)

MVC架构

随着网页的不断发展,出现了复杂的业务逻辑,并且页面也越来越复杂;这时候,把页面和程序逻辑混在一起的嵌入式脚本在庞大的复杂代码情况下变得难以维护。

而且它们都有一个特点:依赖于具体的某个HTTP服务程序,PHP依赖于Apache的mod_php或Nginx的FastCGI支持,JSP依赖于Servlet容器例如Tomcat,这增加了开发与部署的耦合度,更使得项目难以管理。嵌入式脚本难以复用已有的代码,这些代码的测试也需要模拟HTTP环境,难以测试。

此时兴起了一种新的Web后端编程思想,它就是MVC(Model-View-Controller)

简单来说,根据大量的开发经验累积,人们发现一个动态网页的后端通常需要做到这3件事情:

  • Model:使用面向对象的方法为业务建模,把数据对应到编程语言中的对象,把对数据的操作对应到对象的方法。负责对业务数据进行实际的操作。
  • View:输入数据,负责把数据变成用户可以直观看懂的HTML。
  • Controller:负责协调,调用上面两个部分。

MVC

例如,当我们在报修系统中想要查询一个片区的全部报修时,首先我们访问http://wwbx.zsxyww.com/QueryTickets.php?zone=朝晖&status=pending

然后服务器根目录下的QueryTickets.php程序就会接受到我们的请求(在MVC时期的PHP程序已经不像嵌入式脚本那时混写HTML和PHP,整个文件就是以<?php开头的一整个脚本,没有HTML)

QueryTickets.php脚本就是MVC中的Controller,脚本解析到我们想寻找朝晖片区所有待解决的报修,于是它调用一个函数Ticket.Query(),但是Controller知道朝晖片区在数据库对应的编号是10,待解决状态的编号是0,于是他把URI参数中的朝晖pending改成100传递给函数;

这个函数会去数据库(比如前面提到的MySQL)里查询数据,最终执行类似于SELECT * FROM tickets WHERE zone=10 AND status=0;这样的SQL语句,然后把查询的每一行都对应一个Ticket对象,返回一个Ticket的数组。这个函数,以及Ticket类的定义就是Model负责的部分。

info

SQL(Structured Query Language)是进行数据库操作的标准途径,你可以简单地把数据库理解成有更多功能和性能更高的Excel表格,即使这个表格有上亿行,数据库也能在不到几秒内精准执行复杂的数据读取或写入。

刚才的那个SQL语句,就是让数据库找到所有在10号片区(朝晖)未解决的工单,给出这些工单的所有信息。是不是非常直观方便?

数据库往往是后端的核心。许多后端系统,可以说就是SQL数据库的套壳,它们的业务逻辑不会超过"CRUD",这也是为什么例如Supabase之类的产品能够如此的流行。

Model是程序的核心。它不关心页面长什么样,只负责处理和业务相关的数据。在我们报修系统的例子里,它定义了一张报修单应该包含哪些信息(如ID、地点、状态),并提供了操作这些数据的方法(如从数据库查询报修单、更新报修单状态等)。这是网站业务逻辑的体现。

当Controller获得Model返回的数据时,它就把这些数据移交View函数渲染,调用View函数。View首先把Ticket对象里面的100改成朝晖待解决,然后检查客户端的UA,如果是电脑的话就在一行显示更多数据,如果是手机的话就返回紧凑的界面。根据不同的访问设备,预先在系统中存放了一些模板HTML文件,view读取这些文件,然后将数据放到里面,返回给用户;

这样用户访问QueryTickets.php时,就会看到一个根据后台数据实时更新的一个页面。这就是一个简单的MVC架构页面的例子。

提示

哈哈,这个例子其实是骗你的,我们的报修系统既不是用的PHP,也不是用的MVC架构,甚至路由都是虚拟的。这个例子只是让你比较好懂~

虚拟路由

实际上,程序员们认为基于传统服务器文件的路由架构严重阻碍了后端系统的灵活设计。也就是说,用户输入的URI,必须对应根目录里面一个实际存在的文件,比如上面的QueryTickets.php,在后端就是一个实际存在的脚本。由HTTP程序负责调用这个脚本,并且把脚本的输出发送过去。

MVC架构,包括更新的设计,都采用虚拟路由。也就是说,URI不再匹配根目录里的一个文件,什么URI匹配什么现在完全取决于程序员希望它匹配什么,比如匹配程序里的某个函数(不再一定是脚本语言,可以编译语言例如C语言的函数),把函数的输出传给用户。

通常的MVC设计下,在MVC之前程序还有一个路由层,客户端的HTTP请求首先到达这里,经过这里解析后,转交给不同的地方

举例来说明,当用户访问http://wwbx.zsxyww.com/Tickets/朝晖/pending的时候,程序员可以在路由层定义:所有以/Tickets开头的URI,全部转交Query()函数处理,Query()看到转交过来的请求头URI在/Tickets后面是/朝晖/pending,就去数据库查询朝晖的待解决工单,然后调用渲染函数返回HTML。在服务器根目录下面是没有/Tickets/朝晖/pending这个文件的。嗯...其实连根目录都不需要了,

发展到后来,连HTTP服务器也没有了:因为程序员们觉得每次都要配置一个独立的HTTP服务器(比如Apache)再来运行自己的程序有点麻烦。于是,很多现代的Web框架干脆自己内置了一个迷你的HTTP服务器功能。启动程序时,这个内置的服务器也一起启动了,让开发和部署变得更简单。

当然,这种模式下,URI也可以传统地绑定到某个文件上。例如,我们的官网www.zsxyww.com在接受到任何无效的URI时,都会将请求路由到文件404.html上,表示没有找到你请求的东西,比如,www.zsxyww.com/hahaha,和www.zsxyww.com/aaa/bbb都没有在程序路由中规定,一切超出规定的URI全部都路由到404.html文件上,返回一个静态的文件。

现代Web后端

MVC框架

MVC作为一种编程思想可以被程序员灵活地使用,不过也有一些在编程语言基础上编写的MVC框架,来约束程序员使用MVC的思想开发后端,也简化了开发;这种框架有很多,而且不少编程语言都有,我们介绍几个有代表性的:

  • Ruby on Rails 约定大于配置
  • Django
  • Spring Boot
info

这是三篇系列文章中的第2

点击以跳转:

HTTP

动态网页(你在看的文章)

Web应用

]]>
<![CDATA[Web的历史3️⃣-Web应用]]> https://developer.zsxyww.com/blog/Web的历史3️⃣-Web应用 2025-07-13T03:23:58.000Z 随着技术的不断发展,尤其是移动设备的普及和移动互联网的发展,使得动态网页对于日益复杂的需求力不从心。

具体地来说,业界开始需求Web的“应用化”,也就是要把网页变成一个应用程序:在以前,我们介绍的动态网页技术可以很好地支持博客,论坛,新闻网站,企业官网等;而现在,我们需要在浏览器上写文件(腾讯文档),聊天(Discord),直播,点外卖,玩游戏(实际上,很多手机APP都是浏览器套壳,本质上就是向你展示一个网页;包括微信小程序,也是一个只可以使用微信内置浏览器打开的网站)

那些开发APP的程序员也很乐意把自己的项目搬到浏览器里,因为用HTML+CSS写GUI非常方便,而且Web😇具有跨平台的特性:你需要付出很多努力才能确保一个传统APP能在手机,电脑,MacOS,Windows,Linux上运行,但是你写网页只需要很轻松地写,不用考虑兼容,浏览器会确保你的网页在这些平台上都能运行。

包括我们的报修系统,也算是一个Web应用。Web应用相比传统的高级动态网页有如下特征:

  • 页面少:动态网页可能要有成千上百个页面,所以必须使用模板动态生成。而应用程序可能只有几个或者几十个界面。
  • 高交互:动态网页的主要目的是供人观看,用户的交互(例如点击链接,按钮等)比较少且简单。而Web应用中,用户的交互非常频繁且复杂。前后端通信更加频繁。
  • 部分更新:动态网页在不同页面之间也有不变的内容,比如页头页脚之类的,但是这些内容只占网页的少部分;在应用程序中,页面的绝大部分元素都没有怎么变化,只有少部分数据会发生变化,这时候,每更新一次数据就请求一次新页面就十分浪费了。

Web应用程序的这些需求都是我们之前介绍的动态网页技术所不好满足的,于是Web开发进入了一个新时期:

AJAX与前后端分离

JavaScript

之前我们一直在介绍服务器的技术,而一直忽略了浏览器的发展。实际上,浏览器也慢慢变得复杂,也在慢慢进行技术演进。

在1995年,也就是差不多后端处于SSI和CGI的时代,网景公司的员工发明了一种编程语言,叫做"JavaScript",这是对当时新兴的Java Applet的回应。Java Applet可以让你在浏览器运行Java代码,和它同一生态位的一个技术大家应该很熟悉:Flash,不过它们后来都被淘汰了。

JavaScript作为Java Applet的竞争者,是专门设计出来在浏览器内运行的脚本语言。连名字都是在蹭Java的热度:)网景浏览器(也就是火狐浏览器的前身)为JavaScript提供了一个强大的能力:动态地改变当前页面HTML和CSS的内容。

另外,浏览器还为JavaScript提供了发起HTTP请求的接口,不只是GET请求。这本是非常强大的功能,不过,由于JavaScript早期语法混乱,再加上当时人们对于前端能力需求并不是太大,所以直到2010年左右,前端和JavaScript才被重视起来。也就是我们接下来的内容:

AJAX

传统的动态网页有一个不灵活的特性:每次只能返回一个完整的页面。也就是说,要么不更新网页,要么就全部更新,没办法更新现有网页的一部分。

比如我们有一个在文章下面评论的功能:用户需要在文本框里输入评论,然后点击"发布评论"的按钮。如果创建成功,那么我们应该让用户看到"评论成功"的提示并且在评论区显示用户最新评论的内容;如果失败也要提示有关的信息。

传统的动态网页只能这么做:返回一个完整的网页,这个网页和用户之前的网页的大部分内容没有区别,只是多了提示的信息和新的评论。但实际上,页面的有效信息只有那一部分。这极大地浪费了网络带宽和服务器性能(因为要重新渲染)。

AJAX(Asynchronous JavaScript and XML)就是解决这一痛点的技术,既然JavaScript可以动态地修改页面内容,发送HTTP请求,那么就直接让JavaScript程序发送带有评论内容的POST请求到服务器,然后服务器返回纯数据(通常使用JSON或XML的格式,把数据用格式包装起来有一个术语,叫做"序列化")而不是HTML,送给JavaScript程序而不是浏览器,JavaScript根据返回的操作HTML或CSS,实现网页部分更新的效果。

AJAX的核心在于"异步",浏览器发送请求后,不会等待后端返回数据,而是允许用户继续浏览。即是指在发评论时,页面不会整个刷新一下的特性。

一个AJAX报名系统的例子

下面举一个例子,来详细说明AJAX的工作原理:用户需要通过一个网页进行某项活动的报名,填写个人信息,然后点击网页上的提交按钮,服务端会返回报名的结果,成功还是失败,与传统的方法不同,服务器返回信息时不是重新返回一个HTML,而是让浏览器直接在原有的界面上(比如提交按钮的下面)显示报名的状态。

用户通过浏览器输入地址,使用GET方法发起对http://example.org/submit的请求,服务器程序获得请求后,在路由表中查得/submit这个路径匹配静态文件/static/submit.html,于是将文件发送给浏览器;

浏览器得到文件后开始解析渲染这个文件,发现这个html在头部又要求浏览器向服务器获取submit.js文件和submit.css文件,于是浏览器不需要用户处理自己又发送了两条GET的HTTP Request,在文件返回后,浏览器自动执行返回的脚本并应用CSS样式。脚本的内容是监听html文件中提交按钮的“点击”事件,当按钮被点击时,执行提交报名表函数。

返回的html文件包括了提示框,要求用户输入姓名,手机,空闲时间等信息,在这些输入栏的最下面有一个提交按钮,当点击这个按钮时,脚本中的指定函数就会被激活,这个函数将读取用户从上面输入进来的内容,做一些简单的认证(比如说手机号的格式等),如果检验失败则让用户重新填写信息。

如果检验成功,则将这些信息组织成一个JSON文件(虽然AJAX中的X就是XML,但是现在其实最常用JSON),向服务器发送一个POST请求,地址是http://example.org/api/submit,在请求体中包含刚才的JSON文件。

服务器在接收这个请求后,在路由模块中查得这个URL匹配HandleTicket()函数,于是这个请求的上下文全部转交给HandleTicket()这个函数,函数会创建一个Ticket类的实例,将数据做后端检验后赋给实例的成员,如果检验成功则调用方法Ticket.Create()录入数据库,并向客户端(在这里指JavaScript脚本,而不是用户)发送回应成功的状态码(200);

检验的内容除了冗余检查格式是否合法之外,还要执行业务逻辑方面的检查:

  • 一个人只准报名一次,所以如果数据库中存在“姓名”字段相同的记录的话,检验则不通过(这个只是个例子,所以不考虑重名的情况)
  • 如果我们活动的时间的范围没有包含在空闲时间的范围内,检验则不通过,因为用户的时间和我们活动的时间冲突
  • 如果数据库中的记录(表示报名成功的用户)大于或等于我们设定的数字的话,检验则不通过,因为人够了

如果检验没有成功,则回应失败的消息,除了依据失败的类型设置对应的错误码外,还要返回一个JSON文件来简述错误信息。

报名成功的消息的示例:

StatusCode:201 Created



{
"status":"success",
"submit":{
"name":"小明",
"freeAt":"2023-11-25_15:00~19:00",
"phone":12345678900
},
"createdAt":"2023-11-23_9:23:22"
"message":"恭喜!你已成功报名我们的活动"
}

报名失败的消息的示例:

StatusCode:400 Bad Request

{
"status":"fail",
"submit":{
"name":"小明",
"freeAt":"2023-11-25_9:00~15:00",
"phone":12345678900
},
"createdAt":"2023-11-23_9:23:22"
"message":"抱歉,你的空闲时间与我们的活动时间有冲突"
}

在JavaScript脚本接收到回应后,根据返回的内容,操作html文件的内容,在提交按钮的下面一行插入一个文本块:

报名成功时:


<div class="json_responses">
<p class="json:message">恭喜!你已成功报名我们的活动</p>
<p>请检查你的信息:</p>
<p class="json:submit.name">姓名:小明</p>
<p class="json:submit.freeAt">空闲时间:2023-11-25 15:00至19:00</p>
<p class="json:submit.phone">手机号:12345678900</p>
<p class="json:createdAt">服务端提交创建时间:2023-11-23 9:23:22</p>
</div>

报名失败时:


<div class="json_responses">
<p class="json:message">报名失败!抱歉,你的空闲时间与我们的活动时间有冲突</p>
<p>请检查你的信息:</p>
<p class="json:submit.name">姓名:小明</p>
<p class="json:submit.freeAt">空闲时间:2023-11-25 9:00至15:00</p>
<p class="json:submit.phone">手机号:12345678900</p>
<p class="json:createdAt">服务端提交创建时间:2023-11-23 9:23:22</p>
</div>

使用AJAX技术,用户会发现,在写完信息点击提交按钮后,页面没有变化,过了1秒,按钮下面出现了提示信息。这给用户的体验非常像传统的桌面APP。

如果不使用AJAX技术,那么用户会发现在按下按钮后,网页会白屏刷新一段时间,过了几秒,返回了一个新页面,提示了报名状态信息。这样的应用虽然不是不能用,但是用户体验总是不如AJAX来的无缝,自然。性能也不好。

API驱动与前后端分离

在上面那个例子里,服务器并没有返回HTML,而是返回纯数据,交给前端渲染。既然浏览器自己就能根据数据渲染HTML,那么后端也就没必要返回成品HTML了。

可以发现,这实际上就是把MVC架构中的"Views"部分推给了前端,后端只需要通过HTTP传递数据给前端JavaScript,前端JavaScript再渲染成HTML。后端返回纯数据,由前端渲染的模式称之为前后端分离

而这样返回数据的URL也不能称之为"网页"了,因为它从给人看变成了给JavaScript程序看,这样的URL实际上变成了前端程序和后端程序这两个程序之间交互的接口,所以这种URL我们称之为HTTP API,一个主要是API而不是HTML网页的网站,我们称之为"API驱动"的网站。

REST API

不知道你还记得前面说的“虚拟路由”吗?这被广泛地运用在如今的API设计下,比如说我要获取某篇文章下面的全部评论,评论获取API可能是这样的:GET http://example.org/api/getComments?article_id=123123

这种设计模式把一个URL看作一个业务窗口,窗口的作用是办理一个业务,在这个例子里getComments办理的业务就是“获取评论区”,非常的直观。

这种面向业务的API设计对于小规模项目非常方便,如果你的API接口只有几十个的话那使用这种设计也无妨,不过如果你的业务多起来的的话,这种设计是非常难以管理的。

现代的API设计采用面向资源的设计模式,把一个URL看作一个资源,例如我们重写上面的获取评论API:GET http://example.org/article/123123/comments

文章的评论是一项资源,一个URL就是获取这个资源,文章本身也是一项资源,如果我们要查看文章内容的话,我们可以这样写:GET http://example.org/article/123123,如果我们要上传评论的话,可以写POST http://example.org/article/123123/comments,在请求体里面带上评论。获取和上传评论,URL是一样的,只是方法不一样,这就利用了HTTP方法:例如,要删除评论区下的第15条评论的话,可以这样写:DELETE http://example.org/article/123123/comments/15

如果我们要获取评论区下某个用户发过的其他评论的话,我们可以这么写:GET http://example.org/user/456456/comments,核心思想是,一个URL是一个资源,方法是对资源的操作。

你看,这有点像传统的文件系统路由,但是这是完全在后端程序里实现的虚拟的“文件资源系统”,在URL后对应的并不是文件,而是和数据库打交道的Model板块函数。这种方式,就是所谓的REST,以这种方式设计的API,就叫REST API。

GitHub的API设计可以说是REST风格的典范,你可以抽空看看。

单页应用(SPA)

API驱动架构发展到极致就是所谓的"单页应用",顾名思义,服务器只会在用户打开网页时加载一段html文件和一些JavaScript脚本(也就是一个页面),后面的全部内容都是通过JavaScript动态更新的。JavaScript和服务器的后端接口交互主要就是采用AJAX。现代网站,或者说Web应用,通常流行这种单页应用架构。

单页应用的坏处就是打破了为静态网页文件设计的“收藏”功能和前进后退,也不利于SEO和无障碍,因为用户在网站上只打开一个网页,网页的内容根据用户操作由浏览器脚本动态更新,而生成的html也大多没有做语义化优化,难以被理解和解析。当然这些问题有解决方案,下面会讲到。

SPA的例子就是邮箱应用(比如Gmail),通过浏览器访问你的邮箱,通过邮箱的主页面点某个邮件,你并没有进入一个新的页面,而是在原有的页面上,内容变成了邮件查看的界面,如果在主页面上点击发件的话,同样也没有一个发件页面,而是直接呈现发件的UI。

框架

作为"应用",SPA是很复杂的,我们开发桌面应用也不是从底层一路造轮子,而是用Qt,GTK这种框架,同样Web应用也有框架,其中最主要的两个就是Vue.jsReact.js,这些框架提供了Web应用需要的种种功能,我们介绍其中主要的几个方面:

UI组件

SPA框架通常把整个页面的UI分成许多的"组件",当页面变得复杂时,手动管理DOM会变得非常繁琐且容易出错。组件则抽象了复杂的DOM,使其变成有组织,易维护,可复用的一个个单元。

更重要的是,主流框架提供了"声明式UI编程"的方法,这使得你像是在写UI的配置文件,而不是操纵这些UI的程序;你告诉框架"做什么",而不是"怎么做",框架会自动解析你的声明,随着数据的变化自动更新UI组件,你要做的就是告诉框架在某种数据状态下哪个组件需要是怎么样的。用行话讲这叫做"数据驱动视图"。

这样,就可以让开发者可以像搭积木一样构建用户界面,并且当数据变化时,框架会自动更新界面,极大地提高了开发效率。

虚拟DOM

尽管JavaScript可以动态修改DOM,但频繁地直接操作真实DOM是非常耗费性能的。每次DOM操作都需要浏览器重新渲染一遍全部内容。在复杂或数据频繁变化的界面中,这会导致页面卡顿,用户体验下降。

为了解决这个问题,许多SPA框架引入了虚拟DOM的概念。虚拟DOM是一个缓冲区:当数据发生变化时,框架不会直接修改真实DOM,而是先在内存中构建一个新的虚拟DOM树,等到积累到一定的改动,再一次性应用全部的DOM操作。

这种机制的目的是将真实的DOM操作次数降到最低。这显著优化了渲染性能,尤其是在数据频繁变化的复杂界面中,虚拟DOM能够提供更好的性能。另外虚拟DOM也有利于管理状态,增加清晰度:

状态管理

基于Web前端技术栈的单页应用和传统桌面应用程序有一个很大的不同:渲染Web应用界面所基于的数据有着异步获取,分散来源的特点。在流行的REST风格的API设计下,服务器不负责记录状态,所以整理数据,维持状态的重任就全部交由前端负责。

偏偏前端技术栈又不是为开发这种复杂应用而诞生的:HTML最初就是一个标记格式的简单机制,JavaScript最初被用来写简单的脚本,为了保持兼容性,现代框架必须在这种基础上开发,而不是重构。

作为前端开发核心的DOM对象,其本身就是一个巨大的全局状态,所以前端开发天生没有选择像传统桌面开发那样把状态放在各个对象中进行操作,而是维护一个全局的状态,任何操作都是对这个全局的对象进行操作。

这种不良的设计风格,加上RESTful API和HTTP协议的无状态特性,导致前端技术栈天生不适合开发复杂的应用程序:当应用逻辑变得复杂时,通过直接操作DOM来更新视图,就极易导致代码混乱,状态难以追踪,很容易把代码写成一坨浆糊。

正如前面提到的,我们又不能重构浏览器的底层技术栈,所以必须在这一屎山上构建我们的项目。由于在SPA下不再刷新页面,前端需要自己维护一个与UI保持同步的数据状态。这就要考虑如何解耦模块和数据一致性的问题了。

这本来可以通过OOP+设计模式解决,不过我们说过,DOM的存在使得程序员要迎合这一模式,将状态以全局的方式保存,由异步数据来异步操作DOM也容易引起数据竞争。这就极大限制了OOP的发挥,那还有什么办法呢?

于是函数式编程(FP)这个学院派的范式就进入了前端程序员的视野。FP对于前端的这一痛点提供了极佳的解决方案。现在我们不修改原状态,而是创建一个新的状态替代原状态(不可变数据);我们的函数,现在除了输出数据外不进行任何类似于console.log()的其它操作(纯函数)。等等诸如此类的编程思想基本上成为了现代前端开发的基石。使得SPA更容易规范开发和调试。

手写FP框架自然不太好,本着不重复造轮子的精神,前端提供了许多践行着FP范式的状态管理库,帮助前端程序员进行复杂程序的状态管理;Vue和React的状态管理框架分别叫Vuex/Pinia(在Vue3)和Redux。状态管理是现代Web应用前端开发的重要部分。

info

这个部分的内容可能有点难以理解,不过理解这一部分对于成为一个优秀的前端程序员是非常重要的。你也可以在这之后了解关于FP范式的主要思想,这对开发现代和优秀的程序是很重要的,因为FP是编程界的下一个技术潮流,比如Rust,其设计就大量参考了FP思想,甚至Rust早期就是用OCaml开发的😃

前端路由和首屏优化

前面提到SPA打破了浏览器UX和搜索引擎对网站的检测,这些框架提供了API来改变地址栏中的内容,对于不同的页面可以设置不同的URL,来使得逻辑更加清晰,收藏栏也可以正常地使用了。

SPA因为需要初始化大量JavaScript程序,对于网络连接或者设备性能不好的用户,他们会看到长时间的白屏,首屏优化就是在一开始的html中加入一些信息,比如给用户看一个加载中...的动画,让用户耐心等待。或者是在head栏里放一些信息,使得网站可以被搜索引擎更好地搜到。

构建系统

浏览器其实并不能直接理解前端框架(Vue,React等)写的代码。因为它们并不是标准JavaScript,它们含有大量自定义的语法。因此,前端开发必须要把使用了框架的代码编译成浏览器可以理解的HTML,CSS,JavaScript。请注意,类似JQuery这样的库是不需要编译的,因为它们并没有超出原生的JavaScript功能和语法,而只是一个类库。你只需要在你的HTML里包含它们就可以。

构建系统是现代前端开发的重要一环,除了编译高级JavaScript代码外,构建系统通常还会做一些打包,压缩等杂活;这是现代前端开发必不可少的一环。常用的构建系统有Vite和Webpack


JavaScript并不是天生开发复杂应用的语言,所以SPA框架提供了那么多的机制,来把这个为简单脚本设计的语言变成一个足够开发可靠Web应用的语言,尽管一层一层的嵌套拖慢了性能,但是Web的性能瓶颈通常不在于计算而是I/O,加上业界大环境的各种因素,导致了前端基本几天就要推出新技术,眼花缭乱层出不穷,但是这些技术底层都是差不多的。重要的是不要被各种宣传迷惑了认知,而要认清它们的本质。

JavaScript fatigue

"JavaScript Fatigue",图源Auth0博客

JavaScript全栈

Node.js

前面提到,JavaScript是专门设计运行在浏览器中的语言,也就是说,JavaScript的运行时就是浏览器,不过JavaScript也有浏览器之外的运行时,最出名的叫做Node.js

简单来说,Node.js 让JavaScript走出了浏览器,可以直接在服务器上运行了。这意味着,开发者可以用同一种语言(JavaScript)来编写前端(用户在浏览器里看到的界面)和后端(服务器上处理数据的逻辑),这就是所谓的‘JavaScript全栈’开发。常见的JavaScript后端框架有Express.js Koa.js Nest.js。

Web开发之外

Node.js能做的也不只是Web开发,传统编程语言可以做的Node.js都可以,在Web外最重要的领域就是桌面开发了,通常使用Electron框架,可以让你使用HTML,CSS来编写GUI界面,用JavaScript来操作界面。

因为依赖问题,通常每个Electron App 都打包了自己的Node.js运行时,每安装一个App,就相当于安装了一个浏览器内核。这严重浪费了电脑的内存和硬盘空间,但好处就是方便了开发。会写Web就能做桌面开发,降低了学习成本,也更方便跨平台。

最新技术

WebSocket

HTTP已经很好了,但是协议本身有一个缺点:一个HTTP连接分为请求和响应,只能由客户端发起请求,服务端返回响应,如果客户端没有先发一点什么给服务端的话,服务端是没办法先给客户端发信息的,这就给一些更加复杂的要求增添了障碍:比如通过浏览器和别人聊天,需要接受来自服务器发给你的其他人的聊天的信息;或者是在浏览器上玩游戏,需要和服务器交换数据。这可以通过轮询来解决,但是这样还是不方便,效率也较低。

WebSocket是一个全新的协议,支持客户端和服务器的全双工通信,即客户端和服务器都可以主动地发消息,而且可以同时进行,而且是采用较小的数据帧格式,降低了网络开销,提高了数据传输速度。WebSocket在建立连接后保持长时间有效,不需要为每次消息发送重新建立连接,因此效率更高。

为了兼容HTTP协议,WebSocket也是监听80和443端口(HTTPS),握手采用了HTTP协议,通过升级协议来建立连接,如果可以升级,服务器会返回101状态码,升级连接.

WebAssembly

WebAssembly是最近新出现的技术,他允许开发者将C/C++ , Rust等原本的一些编译型语言编译成浏览器可以执行的字节码,使得在浏览器中也可以执行这些程序,目前也有一些使用WebAssembly的应用,可以去看看,最大的好处就是不会JavaScript也能开发前端,也可以复用以前为桌面写的代码。这还是一个非常新兴的领域,值得技术投资😄

总结与资源

Web的历史总共经历了三个阶段:首先为了传输基本文件而发明了HTTP,为了方便地预览文档而发明了HTML和CSS,这是Web的静态网页的时代;为了给静态网页增加动态内容,批量生成许多的网页,人们发明了动态网页技术,形成了后端程序的概念,这是动态网页的时代;为了让客户端浏览器更加强大,给后端减负,人们发明了JavaScript,又搭建了复杂的Web应用程序,形成了前端程序的概念,这是Web的应用程序时代。

虽然技术变得越来越复杂,但是发展的脉络是可以抓住的:Web由于其特性,被人们承载了越来越高的期望:从操作文件,到浏览信息,到成熟的应用程序平台。最重要的是知道Web的诸多名词,概念,技术并不是突然出现的,而是经历了半个世纪的历史沉淀,它们也不是为了创新而创新,每个技术能够出现并受到欢迎,一定是因为解决了某个痛点才有了价值。

编年史

按照时间顺序总结对Web意义重大的技术:

编年史(AI写的,可能有点小错误)

早期 (1966-1990)

  • 1966年

    互联网的前身“阿帕网” (ARPANET) 立项: 美国国防部高级研究计划局(ARPA)启动了阿帕网项目,旨在研究能够在部分网络遭到破坏后仍能维持通信的计算机网络。

  • 1969年

    阿帕网首次成功通信: 10月29日,阿帕网上的第一条消息成功从加州大学洛杉矶分校(UCLA)发送到斯坦福研究院(SRI)。

  • 1983年

    TCP/IP协议成为阿帕网标准: 1月1日,TCP/IP协议取代了原有的网络控制协议(NCP),成为阿帕网的标准通信协议,这一事件被认为是互联网诞生的标志。

  • 1989年

    HTTP协议被发明: Tim Berners-Lee在欧洲核子研究中心(CERN)发明了超文本传输协议(HTTP),为万维网的诞生奠定了基础。

  • 1990年

    HTML被发明: Tim Berners-Lee开发了超文本标记语言(HTML),作为创建网页的标准化语言。

    世界上第一个网页浏览器和Web服务器诞生: Tim Berners-Lee编写了第一个网页浏览器WorldWideWeb(后改名为Nexus)和第一个Web服务器CERN httpd。

萌芽与成长 (1991-2004)

  • 1991年

    互联网向公众开放: 8月6日,Tim Berners-Lee在公共新闻组上发布了万维网项目,标志着互联网开始向公众开放,并逐渐普及。

  • 1993年

    NCSA Mosaic浏览器发布: NCSA(美国国家超级计算应用中心)发布了Mosaic浏览器,这是第一个能够图文混排的浏览器,极大地推动了Web的普及。

    CGI被发明: NCSA HTTPd服务器第一个实现了通用网关接口(CGI),使得Web服务器能够调用外部程序,从而实现动态网页。

  • 1994年

    HTTPS协议被发明: Netscape公司发明了HTTPS协议(安全的HTTP),并在其浏览器中实现。

    Netscape浏览器发布: Netscape Navigator发布,迅速成为当时最流行的浏览器。

    CSS被发明: Håkon Wium Lie首次提出了层叠样式表(CSS)的构想。

  • 1995年

    MySQL首次发布: MySQL数据库的第一个内部版本发布,之后逐渐发展成为Web领域最受欢迎的开源数据库之一。

    Apache项目启动: Apache项目组接手了NCSA HTTPd的开发,并将其发展成为至今仍然非常流行的Apache HTTP Server。

    JavaScript发布: Netscape公司发布了JavaScript(最初名为LiveScript),为网页添加了动态交互能力。

    PHP发布: Rasmus Lerdorf发布了PHP(Personal Home Page Tools),一种用于创建动态网页的服务器端脚本语言。

  • 1996年

    HTTP/1.0发布: HTTP/1.0作为RFC 1945发布,对早期的HTTP协议进行了补充和规范。

    CSS 1成为W3C推荐标准:CSS Level 1正式发布,为网页样式提供了标准化方案。

    XML被发明: W3C开始制定可扩展标记语言(XML),旨在以结构化的方式传输和存储数据。

  • 1997年

    HTTP/1.1发布: HTTP/1.1作为RFC 2068发布,引入了持久连接、管道化和分块传输等重要改进,至今仍是广泛使用的协议版本。

    Java Servlet发布: Sun Microsystems发布了Java Servlet技术,提供了一种在Web服务器上运行Java程序的方式。

  • 1998年

    XMLHttpRequest对象出现: 微软在Internet Explorer 5.0中首次引入了XMLHttpRequest对象,为日后AJAX的出现奠定了基础。

  • 1999年

    JSP发布: Sun Microsystems发布了JavaServer Pages(JSP),允许开发者将Java代码嵌入到HTML页面中。

    HTML 4.01发布: HTML 4.01成为W3C推荐标准,是2000年代使用最广泛的HTML版本。

  • 2000年

    REST被提出: Roy Fielding在他的博士论文中提出了表述性状态转移(REST)的软件架构风格,为Web API的设计提供了重要的理论指导。

    JSON被发明: Douglas Crockford提出了JavaScript对象表示法(JSON),作为一种轻量级的数据交换格式。

  • 2002年

    Firefox发布: Mozilla基金会发布了Firefox浏览器,作为Netscape的继任者,以其开源、可扩展和对Web标准的良好支持而受到欢迎。

  • 2004年

    Ruby On Rails发布: David Heinemeier Hansson发布了Ruby on Rails,这是一个全栈Web应用框架,以其“约定大于配置”的理念和快速开发能力而闻名。

繁荣与变革 (2005至今)

  • 2005年

    MVC架构的流行: 模型-视图-控制器(MVC)架构模式大约在2000年代中期随着Ruby on Rails和Django等框架的兴起而在Web开发领域流行起来。

    AJAX被广泛认知: Jesse James Garrett创造了AJAX(Asynchronous JavaScript and XML)这个术语,描述了一种使用现有技术创建更具动态性和交互性的Web应用的方法。

    Django发布: Django,一个基于Python的高级Web框架,首次发布。

  • 2006年

    jQuery发布: John Resig发布了jQuery,这是一个快速、小巧且功能丰富的JavaScript库,极大地简化了HTML文档遍历、事件处理、动画和Ajax交互。

  • 2008年

    HTML5第一份公开草案发布: WHATWG发布了HTML5的第一份公开工作草案。

  • 2009年

    Node.js发布: Ryan Dahl发布了Node.js,这是一个基于Chrome V8引擎的JavaScript运行环境,使得JavaScript可以在服务器端运行。

    ES5发布: ECMAScript 5(ES5)发布,为JavaScript语言带来了重要的改进,例如严格模式和对JSON的原生支持。

    AngularJS发布: Google发布了AngularJS,这是第一个被广泛采用的现代前端框架,引入了数据绑定、依赖注入等概念。

  • 2010年

    Express.js发布: Express.js,一个基于Node.js平台的极简、灵活的web应用开发框架,首次发布。

  • 2011年

    WebSocket协议标准化: WebSocket协议被IETF标准化为RFC 6455,提供了浏览器与服务器之间全双工通信的能力。

  • 2013年

    前后端分离架构的兴起: 大约在2013年左右,随着前端框架(如AngularJS, React)的成熟和RESTful API的普及,前后端分离的架构模式开始兴起。

    React.js发布: Facebook发布了React.js,这是一个用于构建用户界面的JavaScript库,以其组件化和虚拟DOM的概念而受到欢迎。

    Electron发布: GitHub发布了Electron(最初名为Atom Shell),这是一个使用Web技术构建跨平台桌面应用的框架。

  • 2014年

    HTML5正式发布: W3C正式发布了HTML5推荐标准。

    Vue.js发布: 尤雨溪发布了Vue.js,这是一个渐进式JavaScript框架,以其易用性和灵活性而著称。

    SPA架构的流行: 单页面应用(SPA)架构随着AJAX的出现和前端框架的发展而逐渐流行,大约在2010年代中期成为构建富交互Web应用的主流方式。

  • 2015年

    ES6发布: ECMAScript 2015(ES6)发布,为JavaScript带来了大量新特性,如类、模块、箭头函数、Promise等,是JavaScript语言的一次重大更新。

    REST开始流行: 随着移动互联网和前后端分离架构的兴起,RESTful API成为Web服务的主流设计风格。

    WebAssembly首次宣布: WebAssembly,一种新的、可移植的、大小和加载时间高效的格式,旨在为Web带来近乎原生的性能,首次被宣布。

  • 2016年

    Next.js发布: Vercel(当时名为ZEIT)发布了Next.js,这是一个基于React的服务端渲染框架,简化了React应用的开发。

  • 2017年

    WebAssembly MVP发布: WebAssembly的最小可行产品(MVP)在主流浏览器中得到支持。

  • 2018年

    WebAssembly首个公开工作草案发布: W3C发布了WebAssembly核心规范、JavaScript接口和Web API的第一个公开工作草案。

  • 2019年

    WebAssembly成为W3C推荐标准: WebAssembly核心规范成为W3C的官方推荐标准。

资源

(MDN是一个非常好的网站,是开发FireFox的Mozilla公司写的,基本上是前端开发的圣经了,对于后端人,这个网站也有许多很好的内容)

  1. 关于HTTP协议的详细解释
  2. Web开发教程 HTML,CSS,JavaScript都有,需要学什么就去看什么
  3. 后端开发教程
info

这是三篇系列文章中的第3

点击以跳转:

HTTP

动态网页

Web应用(你在看的文章)

]]>
<![CDATA[女生宿舍的网络结构解释]]> https://developer.zsxyww.com/blog/2025/5/9/女生宿舍的网络结构解释 2025-05-09T00:00:00.000Z 这里有几张女生宿舍网络结构的图解

概述

请观察这张图片:

朝晖

这是朝晖的网络结构,我们以它举例来说:

首先需要知道,用户的电脑通过自己的网线接到端口上,才可以连上网络

端口的后面,是被我们称为“墙线”的网线通路,顾名思义,它们是嵌在墙里的(这样说不准确,但是你可以这么认为),我们没有办法对墙线做手脚,要是墙线坏了,就只能上报,墙线的另一端是楼层机房的配线架

配线架起到一个中继器的作用,配线架的背面有很多入口,正面有很多出口,配线架会把入口的信号原样输出给出口,入口连接的是墙线(也就是用户端口),出口连接的是交换机

端口不直接连接到交换机,而是首先经过配线架,有很多好处,这样如果配线架到交换机的那一段网线出现了问题,就不用换掉整个墙线,而是直接把出口的线直接换掉;这样也方便我们换线(比如用户用的是电信,毕业了之后新生办的是移动宽带);另外,配线架还有避免信号衰减的功能

端口首先连接到配线架,配线架再连接到楼层交换机;楼层交换机还会连接到楼栋交换机和核心交换机,不过就不是我们网维需要负责的了。

朝晖片区的配线架和交换机放在同一个机柜,但是香晖片区的就不是了,香晖片区的机房结构类似如下: 香晖

提示

本文持续更新中

]]>
<![CDATA[给纯新手的网维快速入门指南]]> https://developer.zsxyww.com/blog/2024/09/27/给纯新手的网维快速入门指南 2024-09-27T00:00:00.000Z 许多新进网维的小伙伴们都可能对我们的日常工作一脸懵:网维是干什么的,我们到底在修什么?其他人说的交换机,主线到底是什么?

甚至有很多正式成员都搞不明白这个问题,为了提升大家的业务水平和网络技术,我们特意编写了这份指南,希望能对你有些许的用处。

网络

网维,那不就是修网络的吗?但是,网络是什么?他究竟是什么样的?为什么我可以访问到遥远彼方的内容(网页,游戏,等)?

首先,我们需要知道,计算机中的一切数据,都是一串二进制数字,也就是许多的01010101101010.........,网络的本质,就是在计算机之间传输这些数字,如何进行传输呢?通常是使用物理现实的两种状态来表示0和1这两个数字。

比如,一个电路(简单点说,有一个电灯),电路闭合,电灯亮,我们定义这样的状态是1,反之,电路断开,电灯暗,我们定义这样的状态为0,我们让计算机监听这个电路的状态,通过电路断开闭合状态不断的超高速的变化,就可以传递01010101这些数字来进行信息的传递。

信息

这里有我们写的一篇科普文章,介绍了网线的一些概念:Click Me

不一定需要电路这种介质,还可以是无线电波,例如,定义电波幅度大于某个值为1,小于某个值为0,或者大于某个频率为0,小于某个频率为1,让计算机接受这样的电波并解读,也可以传递信息。

其实,简单地说,前者就是以太网(有线网)本质,网线本质上就是电线,里面是以高频率断断续续的电流,把电线接到计算机里,计算机就可以依据电路的变化情况来获取信息.而后者则是无线网(Wi-Fi,移动通信技术等)本质,他们通过调制电磁波来传递信息。

网维

网维主要修的是学生宿舍的以太网,在每个宿舍的每个人的桌子前,都有一个网线端口,用户将网线插入端口和计算机,即可连接到宿舍的楼层线路,端口后面是墙线,意思是它是埋在墙里的,我们一般情况下没法动他

依据宿舍片区的不同,墙线可能直接通去了机房,也有可能是连接到了宿舍的路由器(交换机/光猫),经过这一层转发,再前往机房。

提示

你可以在片区信息中查看各个片区的具体结构

路由器是什么?交换机和光猫又是什么?这个问题如果专业地展开来讲有点难懂,对于新手来说,你可以简单的理解为他们都是中转站,电线在这里经过出去的电线继续信息的传递,而不是直接一根线连到网络上去

无线路由器(或者有路由功能的光猫)还可以让用户通过无线方式接入线路,比如,如果你想坐高铁,从中山坐到广州南,然后再从广州南做到其他地方,但是没必要全程坐高铁,你可以打车,坐地铁到广州南,然后再坐高铁。这里一样的,不管有线还是无线的接入,在网关(我们对这些设备的统称)都是转换成有线再出去,

出去是去哪里呢?在宿舍的结构中,是去了机房(或者说,弱电井),宿舍去机房的线就叫做主线(只有通过网关中继的地方才会有主线的概念,直连机房的就直接是墙线)

线路到了机房,一般会接入一个叫做配线架的设备,这个也是一个中继器,方便我们维修(如果是线路坏了的话),配线架的端口总是成对的,有一个进口就会有出口,一般进口都是不用我们去管的,线路经过出口出来,连接到交换机上,我们网维的工作一般追踪到这里,再往后的问题就不是我们的职责了,

交换机有运营商的区别,用户需要接到他宽带开通的运营商对应的交换机上,通过一类叫做“拨号”的协议来进行认证,向运营商用自己的账号请求登录,请求运营商允许联网,这样也方便运营商计费(和学校监控!),交换机通常都是集成了光纤功能,在这里将电路的信号调制成光的信号(这也是一种传输0101010的方式!),然后接入运营商的网络,访问全国,和全球的互联网

维修

上面就是我们学校宿舍网络的结构了,网维的工作,本质就是维护这样的结构正常运行,在这些环节中,每一个都有可能出错,一般来说,我们最常见的有两种错误:硬件(物理层)错误(651)和软件错误(691).

651错误

硬件错误,通常在拨号时会给出651错误,本质上,这种错误就是物理连接出现了问题,要么是墙线老化接触不良了,要么是主线出现了问题,要么是交换机,配线架没有接好,要么是网线问题,对于这种问题,我们通常通过寻线对线器来解决,排查问题

排查问题一般都是调试可能出错的因素,例如,你用一根确定了的是好的网线直连端口对线没问题,但是用户的线连接端口就有问题,那么就是用户的线有问题

信息

关于更多651错误,请查阅wiki相关页面,也请查阅技能板块中对线寻线的条目

691错误

软件错误,通常会在拨号时给出691错误,本质上,这意味着物理连接是通的,但是运营商那边拒绝的用户的登录,有几种可能:

  1. 交换机接错了,用户是某个运营商,你接到了其他的运营商的交换机,当然报错喽~
  2. 短时间内过多的登录,被运营商拉黑了,需要稍等一会子
  3. 宽带欠费,停机
  4. 账号密码错误
  5. 运营商会绑定MAC地址来限制多设备,在更换电脑或者转接器等设备时会遇到这样的情况,可以打电话给运营商要求所谓的“解绑刷新”,也可以组长
信息

关于更多691错误,请查阅wiki的相关页面,和相应的文章

总结

这里只是一篇简要的指南,我们在这里介绍了网络的基本概念,让你对你所要维修的系统有一个大概的了解,你可以继续去看wiki中的相关条目,来进一步扩充你的知识,希望你在网维的时光过得愉快!

]]>
<![CDATA[ComIntRep的使用教程]]> https://developer.zsxyww.com/blog/2024/08/19/ComIntRep的使用教程 2024-08-19T00:00:00.000Z 介绍

如果是操作系统本身的问题导致了用户没法上网的话,传统的办法是先确定故障出现在哪里,然后去修理对应的地方,在这个过程中,难免要接触到修改注册表,协议栈,hosts这类复杂的名词..

而ComIntRep就是一款一键修复windows网络的神器,全称"Complete Internet Repair",他的功能有:

  • 重置 Internet 协议(TCP/ IP 协议)
  • 修复 Winsock
  • 更新互联网连接
  • 刷新 DNS 解析器缓存
  • 刷新 ARP 缓存
  • 修复 Internet Explorer
  • 清除 Windows 更新历史记录
  • 修复 Windows 自动更新
  • 修复的 SSL / HTTPS 加密
  • 重置代理服务器配置
  • 重置 Windows 防火墙配置
  • 恢复默认的 hosts 文件
  • 修复查看工作组计算机
    这款软件可以帮我们很大程度上节省修网的时间,非常推荐使用

使用

勾选你要修复的部分,然后点击Go!,就可以了,系统会自动重置你选择那部分的网络配置,修复完毕后,重启电脑即可
官方推荐在修复前备份操作系统,点击Maintennace->System_Restore,然后点击“配置”,“启用系统保护”,然后为C盘创建一个还原点,就可以修复了

参考

https://zhuanlan.zhihu.com/p/27196344 \

]]>
<![CDATA[你可以在哪里找到开发组?]]> https://developer.zsxyww.com/blog/2024/08/18/网维联系方式 2024-08-18T00:00:00.000Z 目前,开发组成员可以通过如下的方式联系:

  • 开发组成员可以通过github上的论坛交流(仅限内部访问)
  • 开发组有一个QQ群:961797864
  • 可以联系组长,QQ:2597760847
]]>
<![CDATA[Hello World!]]> https://developer.zsxyww.com/blog/2024/06/27/Hello_World 2024-06-27T00:00:00.000Z This is a Test post

]]>