一、引言
Web 开发领域始终处于快速发展之中,从早期依赖纯后端渲染模板,到如今前后端分离结合响应式驱动界面,技术的变革为开发者带来了更高效、灵活的开发模式。理解这一演进过程,对掌握现代 Web 开发技术至关重要。本文将详细解析各个阶段的关键技术,帮助开发者深入理解并应用这些知识。
二、纯后端套模板 - MVC 开发模式
(一)MVC 架构剖析
- Model - 数据模型:在 MVC 开发模式里,Model 是对数据的抽象表示。例如,当使用 MySQL 数据库时,开发人员需要将数据库中的表结构和关系转化为程序可操作的模型。假设数据库中有一个 “用户” 表,Model 部分可能会定义一个 “User” 类,类中的属性(如用户名、密码等)对应表中的字段,并提供方法来操作这些数据,如查询、更新用户信息等。通过这种方式,将数据库复杂的操作封装起来,方便在整个应用程序中调用,实现数据的有效管理。
- View - 模板:View 主要基于 HTML 构建,模板中使用类似
{{todos}}这样的语法作为数据占位符。在应用程序运行时,来自 Model 的数据会自动替换这些占位符,从而实现动态内容的展示。例如,如果Model中的todos代表一个待办事项列表,那么在最终渲染的 HTML 页面上,该占位符位置就会显示具体的待办事项内容,实现数据与展示的初步结合。 - Controller - 业务逻辑:Controller 扮演着业务逻辑处理的核心角色,它如同一个枢纽,连接着 Model 和 View。当用户请求查看特定数据(如待办事项列表)时,Controller 会向 Model 发送查询指令,获取所需的数据。然后,它将这些数据传递给相应的 View 模板进行渲染,最终将渲染后的 HTML 页面返回给用户。这种分离的设计模式使得业务逻辑、数据和展示部分相互独立,提高了代码的可维护性和可扩展性,方便团队协作开发。
- HTTP 伺服:在 HTTP 伺服层面,
req.url用于获取客户端发送请求的 URL 地址,服务器通过解析这个地址来判断用户想要访问的资源。res.end则用于结束响应,并将数据返回给客户端。返回的内容既包括 HTML 的静态部分(如页面的基本结构、标题、导航栏等),也包括由数据驱动的动态部分(如从 Model 获取并填充到 View 模板中的用户具体信息)。通过这种方式,服务器能够根据用户的请求,动态生成并返回包含所需信息的 HTML 页面。
(二)Node 服务器端代码详解
javascript
// node 服务器端代码
// 引入Node内置的http模块,用于创建HTTP服务器
const http = require("http");
// 引入url模块,用于解析URL,方便处理不同路径的请求
const url = require("url");
// 定义用户数据数组,模拟数据来源
const users = [ { id: 1, name: '舒俊', email: '123@qq.com' }, { id: 2, name: '陈俊璋', email: '1232@qq.com' }, { id: 3, name: '徐行', email: '121@qq.com' }];
// 生成用户HTML的函数,接受用户数据数组作为参数
function generateUserHTML(users) {
// 使用map方法遍历users数组,为每个用户生成HTML表格行
const userRows = users.map(user => `
<tr>
<td>${user.id}</td>
<td>${user.name}</td>
<td>${user.email}</td>
</tr>
`).join('');
// 返回完整的HTML字符串,包含用户表格数据
return `
User List
table { width: 100%; border - collapse: collapse; margin - top: 20px; }
th, td { border: 1px solid #ccc; padding: 8px; text - align: left; }
th { background - color: #f4f4f4; }
<h1>Users</h1>
<table>
<thead>
<tr>
<th>ID</th>
<th>Name</th>
<th>Email</th>
</tr>
</thead>
<tbody>
${userRows}
</tbody>
</table>
`;
}
// 创建HTTP服务器,传入请求处理函数
const server = http.createServer((req, res) => {
// 解析请求的URL,第二个参数true表示将查询字符串解析为对象
const parsedUrl = url.parse(req.url, true);
// 如果请求路径是根路径或/users路径
if (parsedUrl.pathname === '/' || parsedUrl.pathname === '/users') {
res.statusCode = 200; // 设置响应状态码为200,表示成功
res.setHeader('Content - Type', 'text/html;charset=utf - 8'); // 设置响应头,指定内容类型为HTML,字符集为utf - 8
const html = generateUserHTML(users); // 调用函数生成包含用户数据的HTML字符串
res.end(html); // 将生成的HTML字符串作为响应内容发送给客户端,并结束响应
} else {
res.statusCode = 404; // 如果请求路径不匹配,设置状态码为404,表示未找到
res.setHeader('Content - Type', "text/plain"); // 设置响应头,指定内容类型为纯文本
res.end('Not Found'); // 返回“Not Found”文本给客户端,并结束响应
}
});
// 启动服务器,监听1314端口,服务器启动成功后在控制台输出提示信息
server.listen(1314, () => {
console.log('Server is running on port 1234');
});
-
模块引入:
const http = require("http");:这行代码引入了 Node.js 内置的http模块,该模块提供了创建 HTTP 服务器的功能,是构建服务器的基础模块。const url = require("url");:这里引入url模块,它能够帮助我们解析客户端请求的 URL 地址,提取其中的路径、参数等信息,以便服务器根据不同的请求路径做出相应的处理。
-
数据定义:
const users = [ {... } ];:定义了一个名为users的数组,其中包含了多个用户对象。每个用户对象都具有id、name和email属性,这些数据模拟了从数据库或其他数据源获取的用户信息,作为后续生成 HTML 页面的数据基础。 -
HTML 生成函数:
generateUserHTML函数的作用是根据传入的用户数据数组生成 HTML 表格。const userRows = users.map(user =>...).join('');:使用map方法遍历users数组,为每个用户生成一个 HTML 表格行字符串。在生成的表格行中,通过模板字符串插值的方式,将用户对象的id、name和email属性值插入到相应的<td>标签中。最后,使用join('')方法将所有生成的表格行连接成一个字符串。return...;:返回一个完整的 HTML 字符串,该字符串包含了 HTML 文档的基本结构、样式定义以及生成的用户表格数据。其中,部分设置了页面的元信息和样式,部分包含了标题和表格,表格的<tbody>部分插入了生成的用户表格行数据。
-
服务器创建与请求处理:
const server = http.createServer((req, res) => { });:使用http.createServer方法创建一个 HTTP 服务器实例,并传入一个请求处理函数。该函数接收两个参数,req表示客户端的请求对象,包含了请求的各种信息;res表示服务器的响应对象,用于向客户端发送响应数据。const parsedUrl = url.parse(req.url, true);:在请求处理函数内部,使用url.parse方法解析请求的 URL 地址。第二个参数true表示将查询字符串解析为一个对象,方便后续获取 URL 中的参数信息。if (parsedUrl.pathname === '/' || parsedUrl.pathname === '/users') { }:通过判断解析后的 URL 路径名是否为根路径/或/users,来决定如何处理请求。res.statusCode = 200;:如果请求路径匹配,设置响应状态码为 200,表示请求成功。res.setHeader('Content - Type', 'text/html;charset=utf - 8');:设置响应头的Content - Type字段为text/html;charset=utf - 8,表示返回的内容是 HTML 格式,并且字符集为 UTF - 8,这样浏览器就能正确地解析和显示返回的页面。const html = generateUserHTML(users);:调用generateUserHTML函数,传入users数组,生成包含用户数据的 HTML 字符串。res.end(html);:将生成的 HTML 字符串作为响应内容发送给客户端,并结束响应过程。else { }:如果请求路径不匹配上述条件,执行以下代码。res.statusCode = 404;:设置响应状态码为 404,表示请求的资源未找到。res.setHeader('Content - Type', "text/plain");:设置响应头的Content - Type字段为text/plain,表示返回的内容是纯文本格式。res.end('Not Found');:返回 “Not Found” 文本给客户端,并结束响应,告知用户请求的资源不存在。
-
服务器监听:
server.listen(1314, () => { console.log('Server is running on port 1234'); });:调用服务器实例的listen方法,使服务器开始监听指定的端口号(这里是 1314)。当服务器成功启动并开始监听后,会执行回调函数,在控制台输出提示信息 “Server is running on port 1234”,表示服务器已正常运行,可以接收客户端的请求。
三、前后端分离
(一)前后端职责转变
- 前端:前端主要由 HTML、CSS 和 JS 组成。HTML 负责搭建页面的基本结构,CSS 用于美化页面样式,赋予页面视觉上的吸引力,而 JS 则为页面添加交互性。
ajax(Asynchronous JavaScript and XML)和fetch技术的出现,使得前端能够主动地从后端获取数据。ajax可以在不重新加载整个页面的情况下,通过异步请求从服务器获取数据,并更新页面的部分内容,从而提供更流畅的用户体验。fetch是现代浏览器提供的一种更简洁、灵活的 API,用于进行网络请求。例如,当前端页面需要展示用户列表时,可通过ajax或fetch向服务器发送请求,获取用户数据,然后利用 JS 将这些数据填充到 HTML 页面的相应位置,实现动态展示,使页面能够根据用户的操作和数据的变化实时更新。 - 后端:在前后端分离的架构中,后端不再负责返回完整的 HTML 页面,而是专注于提供数据接口(API)。例如,后端可能提供一个
http://localhost:3000/users这样的 API 地址,前端通过向该地址发送请求,即可获取用户相关的数据。后端的主要任务转变为处理数据,包括对数据库进行增删改查操作,确保数据的准确性和一致性,同时还要优化并发性能,以应对大量的前端请求,保证系统在高负载情况下的稳定运行。
(二)前后端分离优势 - 开发人员解耦
- 前端开发人员:工作重心更多地聚焦在数据的展示和用户体验的优化上。在传统开发模式中,前端开发人员往往需要花费大量时间在 DOM 编程上,例如使用
document.getElementById等方法查找页面节点,这部分工作与业务逻辑的关联性相对较弱。而在前后端分离的模式下,他们可以将更多精力投入到业务相关的操作中,比如处理focus事件(当用户聚焦到输入框时,显示提示信息等),通过数据驱动界面的方式,使页面的交互性更强,用户体验更流畅。同时,由于前后端职责明确,前端开发人员可以独立地进行开发和测试,提高开发效率。 - 后端开发人员:主要关注数据的处理和并发性能的优化。他们需要优化数据库查询语句,以提高数据获取的速度,确保在高并发情况下,服务器能够快速、准确地响应前端请求。此外,后端开发人员还需要处理系统的稳定性和安全性问题,如防止数据泄露、处理恶意请求等。通过专注于这些核心任务,后端开发人员能够更好地发挥其专业技能,提高后端服务的质量和性能,同时与前端开发人员并行工作,加快整个项目的开发进度。
(三)前端代码示例解析
html
xhr给前端独立人格
table {
width: 100%;
border - collapse: collapse;
margin - top: 20px;
}
th,
td {
border: 1px solid #ccc;
padding: 8px;
text - align: left;
}
th {
background - color: #f4f4f4;
}
<h1>Users</h1>
<table>
<thead>
<tr>
<th>ID</th>
<th>Name</th>
<th>Email</th>
</tr>
</thead>
<tbody>
</tbody>
</table>
// 使用fetch发起对后端API的请求,获取用户数据
fetch('http://localhost:3000/users')
.then(res => res.json()) // 将响应数据转换为JSON格式
.then(data => {
// 获取页面中的tbody元素
const tbody = document.querySelector('tbody');
// 使用map方法遍历获取到的用户数据,为每个用户生成HTML表格行
tbody.innerHTML = data.map(user => `
<tr>
<td>${user.id}</td>
<td>${user.name}</td>
<td>${user.email}</td>
</tr>
`).join('');
});
-
HTML 结构与样式:
-
``:声明文档类型为 HTML5,告诉浏览器以 HTML5 标准解析页面。
-
``:定义页面的语言为英语,有助于搜索引擎优化和浏览器语言相关的设置。
-
``部分:
- ``:设置文档的字符编码为 UTF - 8,确保页面能够正确显示各种字符。
- ``:设置视口,使页面能够在不同设备上正确显示,
width=device - width表示页面宽度等于设备宽度,initial - scale = 1.0表示初始缩放比例为 1.0。 xhr给前端独立人格:设置页面标题,显示在浏览器标签上,用于描述页面的主题。- ``部分:定义了表格的样式,包括表格宽度为 100%,边框合并,表头和表格单元格的边框、内边距、文本对齐方式以及表头的背景颜色等,使表格具有良好的视觉呈现效果。
-
-
JavaScript 数据获取与渲染:
fetch('http://localhost:3000/users'):使用fetch函数发起一个 GET 请求,请求的 URL 为http://localhost:3000/users,即向后端 API 请求用户数据。fetch返回一个 Promise 对象,代表请求的最终完成或失败。.then(res => res.json()):当fetch请求成功时,.then方法会被调用,其回调函数接收一个响应对象res。在这个回调函数中,使用res.json()方法将响应数据解析为 JSON 格式,返回一个新的 Promise 对象,该对象在解析完成后会包含解析后的 JSON 数据。.then(data => { }):当 JSON 数据解析成功后,这个.then方法的回调函数会被调用,其参数data就是解析后的用户数据。const tbody = document.querySelector('tbody');:使用document.querySelector方法获取页面中<table>元素的<tbody>子元素,该元素将用于显示用户数据表格的主体部分。tbody.innerHTML = data.map(user =>...).join('');:使用map方法遍历data数组(即获取到的用户数据),为每个用户生成一个 HTML 表格行字符串。在生成的表格行中,通过模板字符串插值的方式,将用户对象的id、name和email属性值插入到相应的<td>标签中。最后,使用join('')方法将所有生成的表格行连接成一个字符串,并将其赋值给tbody.innerHTML,从而将用户数据动态地渲染到页面的表格中。
四、Vue 响应式驱动界面
(一)Vue 响应式原理与数据定义
javascript
// 从Vue库中导入ref和onMounted
import { ref, onMounted } from 'vue';
// 使用ref创建响应式数据users,初始值为包含多个用户对象的数组
const users = ref([
{ id: 1, name: '郑志鹏', email: '123@example.com' },
{ id: 2, name: '刘翔平', email: '1234@example.com' },
{ id: 3, name: '傅晨', email: '12345@example.com' }
]);
// 2秒后向users数组中添加一个新用户
setTimeout(() => {
users.value.push({ id: 4, name: '王鑫', email: '123456@example.com' });
}, 2000);
-
导入 Vue 响应式相关功能:
import { ref, onMounted } from 'vue';:这里从 Vue 库中导入了ref和onMounted。ref是 Vue 提供的一个函数,用于创建响应式数据。它将普通的数据包装成一个响应式对象,当这个数据发生变化时,Vue 会自动检测到并更新与之关联的模板部分。onMounted是一个生命周期钩子函数,它会在组件挂载到 DOM 后执行,我们可以在这个函数中执行一些需要在组件渲染完成后进行的操作。
-
创建响应式数据
users:const users = ref([... ]);:通过ref函数创建了一个名为users的响应式数据。其初始值是一个包含多个用户对象的数组,每个用户对象都有id、name和email属性。由于users是通过ref创建的响应式数据,所以对users数组的任何修改,比如添加、删除或修改其中的用户对象,Vue 都能够检测到,并自动更新模板中依赖于users的部分,实现数据与视图的自动同步。
-
动态更新响应式数据:
setTimeout(() => { }, 2000);:这是 JavaScript 的setTimeout函数,它会在延迟 2000 毫秒(即 2 秒)后执行传入的回调函数。users.value.push({ id: 4, name: '王鑫', email: '123456@example.com' });:在回调函数中,通过users.value来访问ref包装的响应式数据的实际值(因为使用ref创建的数据需要通过.value来访问其内部的值)。然后使用数组的push方法向users数组中添加一个新的用户对象。由于users是响应式数据,Vue 会检测到数组的变化,并自动更新模板中显示用户列表的部分,无需手动操作 DOM 来更新视图。
(二)Vue 模板渲染与数据绑定
html
<table>
<thead>
<tr>
<th>ID</th>
<th>Name</th>
<th>Email</th>
</tr>
</thead>
<tbody>
<tr>
<td>{{ user.id }}</td>
<td>{{ user.name }}</td>
<td>{{ user.email }}</td>
</tr>
</tbody>
</table>
-
构建表格结构:
<table>标签定义了一个 HTML 表格。<thead>部分定义了表格的表头,其中包含三个<th>元素,分别显示 “ID”、“Name” 和 “Email”,用于标识表格列的含义。
-
使用
v - for指令进行数据循环渲染:<tr>:v - for是 Vue 的一个指令,用于在模板中循环渲染数组或对象。这里它遍历users这个响应式数组,为数组中的每个user对象生成一个<tr>(表格行)元素。:key="user.id"为每个循环项提供了一个唯一的标识,这在 Vue 的虚拟 DOM 算法中非常重要。当数据发生变化时,Vue 可以通过这个唯一的key更高效地识别和更新变化的部分,避免不必要的 DOM 操作,从而提高渲染性能。<td>{{ user.id }}</td>、<td>{{ user.name }}</td>和<td>{{ user.email }}</td>:这三行代码使用了 Vue 的模板插值语法。通过双大括号{{ }},将user对象的id、name和email属性值插入到相应的<td>(表格单元格)元素中。当users数组中的数据发生变化时,Vue 会自动重新渲染这些模板部分,更新表格中显示的用户信息。
(三)Vue 作用域样式
html
``表示该样式只作用于当前组件。在这个组件内部,你可以编写 CSS 样式,这些样式不会影响到其他组件,有效地避免了样式冲突。虽然这里没有具体的 CSS 代码,但在实际开发中,可以在此处定义表格的颜色、字体、边框等样式属性,使组件的样式更加独立和易于管理。
通过对上述不同阶段 Web 开发技术的详细解析,我们可以清晰地看到从纯后端套模板到前后端分离,再到 Vue 响应式驱动界面的发展脉络,这些技术的演进为我们构建现代、高效、用户体验良好的 Web 应用提供了坚实的基础。