探索 Web 开发演进:从后端模板到 Vue 响应式驱动界面

26 阅读16分钟

一、引言

Web 开发领域始终处于快速发展之中,从早期依赖纯后端渲染模板,到如今前后端分离结合响应式驱动界面,技术的变革为开发者带来了更高效、灵活的开发模式。理解这一演进过程,对掌握现代 Web 开发技术至关重要。本文将详细解析各个阶段的关键技术,帮助开发者深入理解并应用这些知识。

二、纯后端套模板 - MVC 开发模式

(一)MVC 架构剖析

  1. Model - 数据模型:在 MVC 开发模式里,Model 是对数据的抽象表示。例如,当使用 MySQL 数据库时,开发人员需要将数据库中的表结构和关系转化为程序可操作的模型。假设数据库中有一个 “用户” 表,Model 部分可能会定义一个 “User” 类,类中的属性(如用户名、密码等)对应表中的字段,并提供方法来操作这些数据,如查询、更新用户信息等。通过这种方式,将数据库复杂的操作封装起来,方便在整个应用程序中调用,实现数据的有效管理。
  2. View - 模板:View 主要基于 HTML 构建,模板中使用类似{{todos}}这样的语法作为数据占位符。在应用程序运行时,来自 Model 的数据会自动替换这些占位符,从而实现动态内容的展示。例如,如果Model中的todos代表一个待办事项列表,那么在最终渲染的 HTML 页面上,该占位符位置就会显示具体的待办事项内容,实现数据与展示的初步结合。
  3. Controller - 业务逻辑:Controller 扮演着业务逻辑处理的核心角色,它如同一个枢纽,连接着 Model 和 View。当用户请求查看特定数据(如待办事项列表)时,Controller 会向 Model 发送查询指令,获取所需的数据。然后,它将这些数据传递给相应的 View 模板进行渲染,最终将渲染后的 HTML 页面返回给用户。这种分离的设计模式使得业务逻辑、数据和展示部分相互独立,提高了代码的可维护性和可扩展性,方便团队协作开发。
  4. 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', &#34;text/plain&#34;); // 设置响应头,指定内容类型为纯文本
        res.end('Not Found'); // 返回“Not Found”文本给客户端,并结束响应
    }
});

// 启动服务器,监听1314端口,服务器启动成功后在控制台输出提示信息
server.listen(1314, () => {
    console.log('Server is running on port 1234');
});
  1. 模块引入

    • const http = require(&#34;http&#34;);:这行代码引入了 Node.js 内置的http模块,该模块提供了创建 HTTP 服务器的功能,是构建服务器的基础模块。
    • const url = require(&#34;url&#34;);:这里引入url模块,它能够帮助我们解析客户端请求的 URL 地址,提取其中的路径、参数等信息,以便服务器根据不同的请求路径做出相应的处理。
  2. 数据定义const users = [ {... } ];:定义了一个名为users的数组,其中包含了多个用户对象。每个用户对象都具有idnameemail属性,这些数据模拟了从数据库或其他数据源获取的用户信息,作为后续生成 HTML 页面的数据基础。

  3. HTML 生成函数

    • generateUserHTML函数的作用是根据传入的用户数据数组生成 HTML 表格。
    • const userRows = users.map(user => ...).join('');:使用map方法遍历users数组,为每个用户生成一个 HTML 表格行字符串。在生成的表格行中,通过模板字符串插值的方式,将用户对象的idnameemail属性值插入到相应的<td>标签中。最后,使用join('')方法将所有生成的表格行连接成一个字符串。
    • return ...;:返回一个完整的 HTML 字符串,该字符串包含了 HTML 文档的基本结构、样式定义以及生成的用户表格数据。其中,部分设置了页面的元信息和样式,部分包含了标题和表格,表格的<tbody>部分插入了生成的用户表格行数据。
  4. 服务器创建与请求处理

    • 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', &#34;text/plain&#34;);:设置响应头的Content - Type字段为text/plain,表示返回的内容是纯文本格式。
    • res.end('Not Found');:返回 “Not Found” 文本给客户端,并结束响应,告知用户请求的资源不存在。
  5. 服务器监听server.listen(1314, () => { console.log('Server is running on port 1234'); });:调用服务器实例的listen方法,使服务器开始监听指定的端口号(这里是 1314)。当服务器成功启动并开始监听后,会执行回调函数,在控制台输出提示信息 “Server is running on port 1234”,表示服务器已正常运行,可以接收客户端的请求。

三、前后端分离

(一)前后端职责转变

  1. 前端:前端主要由 HTML、CSS 和 JS 组成。HTML 负责搭建页面的基本结构,CSS 用于美化页面样式,赋予页面视觉上的吸引力,而 JS 则为页面添加交互性。ajax(Asynchronous JavaScript and XML)和fetch技术的出现,使得前端能够主动地从后端获取数据。ajax可以在不重新加载整个页面的情况下,通过异步请求从服务器获取数据,并更新页面的部分内容,从而提供更流畅的用户体验。fetch是现代浏览器提供的一种更简洁、灵活的 API,用于进行网络请求。例如,当前端页面需要展示用户列表时,可通过ajaxfetch向服务器发送请求,获取用户数据,然后利用 JS 将这些数据填充到 HTML 页面的相应位置,实现动态展示,使页面能够根据用户的操作和数据的变化实时更新。
  2. 后端:在前后端分离的架构中,后端不再负责返回完整的 HTML 页面,而是专注于提供数据接口(API)。例如,后端可能提供一个http://localhost:3000/users这样的 API 地址,前端通过向该地址发送请求,即可获取用户相关的数据。后端的主要任务转变为处理数据,包括对数据库进行增删改查操作,确保数据的准确性和一致性,同时还要优化并发性能,以应对大量的前端请求,保证系统在高负载情况下的稳定运行。

(二)前后端分离优势 - 开发人员解耦

  1. 前端开发人员:工作重心更多地聚焦在数据的展示和用户体验的优化上。在传统开发模式中,前端开发人员往往需要花费大量时间在 DOM 编程上,例如使用document.getElementById等方法查找页面节点,这部分工作与业务逻辑的关联性相对较弱。而在前后端分离的模式下,他们可以将更多精力投入到业务相关的操作中,比如处理focus事件(当用户聚焦到输入框时,显示提示信息等),通过数据驱动界面的方式,使页面的交互性更强,用户体验更流畅。同时,由于前后端职责明确,前端开发人员可以独立地进行开发和测试,提高开发效率。
  2. 后端开发人员:主要关注数据的处理和并发性能的优化。他们需要优化数据库查询语句,以提高数据获取的速度,确保在高并发情况下,服务器能够快速、准确地响应前端请求。此外,后端开发人员还需要处理系统的稳定性和安全性问题,如防止数据泄露、处理恶意请求等。通过专注于这些核心任务,后端开发人员能够更好地发挥其专业技能,提高后端服务的质量和性能,同时与前端开发人员并行工作,加快整个项目的开发进度。

(三)前端代码示例解析

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('');
        });



  1. HTML 结构与样式

    • ``:声明文档类型为 HTML5,告诉浏览器以 HTML5 标准解析页面。

    • ``:定义页面的语言为英语,有助于搜索引擎优化和浏览器语言相关的设置。

    • ``部分:

      • ``:设置文档的字符编码为 UTF - 8,确保页面能够正确显示各种字符。
      • ``:设置视口,使页面能够在不同设备上正确显示,width=device - width表示页面宽度等于设备宽度,initial - scale = 1.0表示初始缩放比例为 1.0。
      • xhr给前端独立人格:设置页面标题,显示在浏览器标签上,用于描述页面的主题。
      • ``部分:定义了表格的样式,包括表格宽度为 100%,边框合并,表头和表格单元格的边框、内边距、文本对齐方式以及表头的背景颜色等,使表格具有良好的视觉呈现效果。
  2. 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 表格行字符串。在生成的表格行中,通过模板字符串插值的方式,将用户对象的idnameemail属性值插入到相应的<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); 

  1. 导入 Vue 响应式相关功能

    • import { ref, onMounted } from 'vue';:这里从 Vue 库中导入了refonMountedref是 Vue 提供的一个函数,用于创建响应式数据。它将普通的数据包装成一个响应式对象,当这个数据发生变化时,Vue 会自动检测到并更新与之关联的模板部分。onMounted是一个生命周期钩子函数,它会在组件挂载到 DOM 后执行,我们可以在这个函数中执行一些需要在组件渲染完成后进行的操作。
  2. 创建响应式数据users

    • const users = ref([... ]);:通过ref函数创建了一个名为users的响应式数据。其初始值是一个包含多个用户对象的数组,每个用户对象都有idnameemail属性。由于users是通过ref创建的响应式数据,所以对users数组的任何修改,比如添加、删除或修改其中的用户对象,Vue 都能够检测到,并自动更新模板中依赖于users的部分,实现数据与视图的自动同步。
  3. 动态更新响应式数据

    • 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>  

  1. 构建表格结构

    • <table>标签定义了一个 HTML 表格。<thead>部分定义了表格的表头,其中包含三个<th>元素,分别显示 “ID”、“Name” 和 “Email”,用于标识表格列的含义。
  2. 使用v - for指令进行数据循环渲染

    • <tr>v - for是 Vue 的一个指令,用于在模板中循环渲染数组或对象。这里它遍历users这个响应式数组,为数组中的每个user对象生成一个<tr>(表格行)元素。:key=&#34;user.id&#34;为每个循环项提供了一个唯一的标识,这在 Vue 的虚拟 DOM 算法中非常重要。当数据发生变化时,Vue 可以通过这个唯一的key更高效地识别和更新变化的部分,避免不必要的 DOM 操作,从而提高渲染性能。
    • <td>{{ user.id }}</td><td>{{ user.name }}</td><td>{{ user.email }}</td>:这三行代码使用了 Vue 的模板插值语法。通过双大括号{{ }},将user对象的idnameemail属性值插入到相应的<td>(表格单元格)元素中。当users数组中的数据发生变化时,Vue 会自动重新渲染这些模板部分,更新表格中显示的用户信息。

(三)Vue 作用域样式

html




``表示该样式只作用于当前组件。在这个组件内部,你可以编写 CSS 样式,这些样式不会影响到其他组件,有效地避免了样式冲突。虽然这里没有具体的 CSS 代码,但在实际开发中,可以在此处定义表格的颜色、字体、边框等样式属性,使组件的样式更加独立和易于管理。

通过对上述不同阶段 Web 开发技术的详细解析,我们可以清晰地看到从纯后端套模板到前后端分离,再到 Vue 响应式驱动界面的发展脉络,这些技术的演进为我们构建现代、高效、用户体验良好的 Web 应用提供了坚实的基础。