普通视图

发现新文章,点击刷新页面。
今天 — 2025年12月12日首页

从“拼字符串”到“魔法响应”:一场数据驱动页面的奇幻进化之旅

作者 AAA阿giao
2025年12月12日 09:47

引言

你有没有想过,为什么今天的网页能像变魔术一样——点一下按钮,列表自动刷新;输个名字,头像立刻出现?而十几年前,想换个内容却要整个页面“唰”地重载?

这一切的背后,是一场关于数据如何驱动页面的技术革命。今天,我们就穿越三段代码时空,跟随一份用户列表的命运,看看 Web 开发是如何从“手搓 HTML”一步步进化到“声明即渲染”的魔法世界的。


第一章:远古时代 —— 服务端“手工编织”页面(server.js

源代码链接:[vue/ref/demo/server.js · Zou/lesson_zp - 码云 - 开源中国](gitee.com/giaoZou/les… "vue/ref/demo/server.js · Zou/lesson_zp - 码云 - 开源中国")

时间回到 Web 初期,那时没有 Vue,没有 React,甚至连 AJAX 都还没普及。开发者们用最原始的方式:在服务器上把数据和 HTML 混在一起,直接“烤”成完整网页,再扔给浏览器

打开 server.js,你会看到这样一段代码:

const users = [
    {id: 1, name: '张三',email:'zhangsan@qq.com'},
    {id: 2, name: '李四',email:'lisi@qq.com'},
    {id: 3, name: '王五',email:'wangwu@qq.com'},
]

这是一份硬编码的用户名单,就像藏在厨房抽屉里的老式通讯录。

接着,一个叫 generateUsersHtml 的函数登场了:

function generateUsersHtml(users){
    const userRows = users.map(user => `
| ${user.id}|${user.name}|${user.email}|
| ---|---|---|
`).join('');
    return `
Users
| ID|Name|Email|
| ---|---|---|
${userRows}
            
    `
}

注意!这里用的不是标准 HTML 表格,而是 Markdown 表格语法(可能是为了简化演示)。但原理惊人地朴素:用 JavaScript 字符串拼接,把数据“缝”进模板里

当用户访问 /users 时:

if (parsedUrl.pathname === '/' || parsedUrl.pathname === '/users') {
    res.statusCode = 200;
    res.setHeader('Content-Type', 'text/html;charset=utf-8'); 
    const html = generateUsersHtml(users);
    res.end(html);
}

服务器把拼好的“成品网页”通过 res.end() 发出去。浏览器收到后,直接显示——没有请求、没有等待、没有交互,一切都在服务端完成

这就像一位老裁缝,根据你的身材(数据)现场量体裁衣(生成 HTML),然后把做好的衣服(完整页面)递给你。但如果你胖了两斤,他得重新做一件——整个页面刷新!

优点:简单、快速、SEO 友好。
缺点:交互差、前后端耦合、改一点就要重刷。


第二章:工业革命 —— 前后端“分家”,API 登场(index.html + db.json + package.json

随着网站越来越复杂,开发者发现:“让前端和后端各干各的,效率更高!”于是,前后端分离成为新潮流。

完整项目结构及链接:[vue/ref/demo2 · Zou/lesson_zp - 码云 - 开源中国](gitee.com/giaoZou/les… "vue/ref/demo2 · Zou/lesson_zp - 码云 - 开源中国")

![](<> "点击并拖拽以移动")

项目准备

前后端分离需要进行项目初始化

第一步:对负责后端的文件夹 backend 进行初始化,在终端打开该文件夹,执行以下命令

# 初始化项目,生成 package.json(只需执行一次)
npm init -y
 
# 安装 json-server,用于基于 JSON 文件快速创建本地 REST API 服务。
npm i json-server

第二步:在前端文件夹 frontend 中添加文件 index.html ,在后端文件夹 backend 中添加 db.json 文件

第三步:修改 package.json 文件内容

将文件内的JavaScript语句修改为如下内容

  &#34;scripts&#34;: {
    &#34;dev&#34;: &#34;json-server --watch db.json&#34;
  },

这段代码定义了一个名为 dev 的 npm 脚本,作用是使用 json-server 工具监听(--watch)项目根目录下的 db.json 文件。当运行 npm run dev 时,会启动一个本地 RESTful API 服务器,自动将 db.json 中的数据暴露为 API 接口,并在文件内容变化时实时更新接口数据,常用于前端开发中的模拟后端服务。

静态骨架:index.html 的“空舞台”

看一眼 index.html

User List
Users
| ID|Name|Email|
| ---|---|---|

这简直是个“幽灵页面”——有标题、有表头,但没有一行真实数据!它就像一个空荡荡的剧院舞台,只搭好了布景,就等演员(数据)登场。

数据仓库:db.json 的“假数据库”

真正的数据藏在这里:

{
    &#34;users&#34;: [
        {
            &#34;id&#34;: 1,
            &#34;name&#34;: &#34;张三&#34;,
            &#34;email&#34;: &#34;zhangsan@qq.com&#34;
        },
        {
            &#34;id&#34;: 2,
            &#34;name&#34;: &#34;李四&#34;,
            &#34;email&#34;: &#34;lisi@qq.com&#34;
        }
        ,
        {
            &#34;id&#34;: 3,
            &#34;name&#34;: &#34;王五&#34;,
            &#34;email&#34;: &#34;wangwu@qq.com&#34;    
        }
    ]
}

这是一个纯 JSON 文件,结构清晰,但本身不会动。它需要一个“翻译官”把它变成 API。

自动化 API 工厂:json-server(来自 package.json

package.json

{
  &#34;scripts&#34;: {
    &#34;dev&#34;: &#34;json-server --watch db.json&#34;
  },
  &#34;dependencies&#34;: {
    &#34;json-server&#34;: &#34;^1.0.0-beta.3&#34;
  }
}

运行 npm run dev,神奇的事情发生了:json-server 自动监听 db.json,并启动一个本地服务器(默认 http://localhost:3000),将 users 数组暴露为 RESTful 接口:

  • GET /users → 返回全部用户
  • GET /users/1 → 返回 ID 为 1 的用户

现在,前端只需一句 fetch('/users'),就能拿到 JSON 数据!

舞台(index.html)不再自己造演员,而是打电话给经纪公司(API):“请派三位演员上台!” 演员来了,前台 JS 再手动把他们安排到座位上(操作 DOM)。

优点:前后端解耦、接口复用、便于测试。
痛点:前端仍需手动更新 DOM,代码冗长易错——“找到表格 → 清空 → 循环创建行 → 插入单元格……”

于是,人们渴望一种更智能的方式……


第三章:魔法纪元 —— Vue 的“响应式咒语”(App.vue

如果说前后端分离是“分工”,那么 Vue 就是“自动化”。它引入了一个颠覆性理念:你只管描述“页面应该长什么样”,数据变了,页面自动跟着变

项目准备

创建Vue3项目,在终端打开创建项目的文件夹,运行以下命令

npm init vite

回车后输入项目名称 ref-demo,选择 Vue + JavaScript 即可

![](<> "点击并拖拽以移动")

完整项目结构及 App.vue 文件链接:[vue/ref/demo3/ref-demo/src/App.vue · Zou/lesson_zp - 码云 - 开源中国](gitee.com/giaoZou/les… "vue/ref/demo3/ref-demo/src/App.vue · Zou/lesson_zp - 码云 - 开源中国")

![](<> "点击并拖拽以移动")

响应式数据:会“思考”的变量 —— Vue 的魔法心脏

App.vue 中,我们看到这样两行代码:

import { ref } from 'vue';
const users = ref([]);

别小看这短短两行——它们开启了一个自动同步数据与视图的魔法世界

ref([]) 不是普通数组,而是一个“活”的数据容器

乍一看,users 似乎只是个空数组。但其实,ref() 把它包装成了一个响应式引用对象(reactive reference) 。你可以把它想象成一个装着数据的“智能玻璃瓶”:

  • 瓶子里装的是你的真实数据(比如用户列表);
  • 瓶子本身会“监听”:只要有人往里放新东西(修改 .value),它就会立刻广播:“注意!内容变了!”
  • 所有“订阅”了这个瓶子的 UI 元素(比如模板中的 {{user.name}}),都会自动更新自己。

换句话说,users 不再是死气沉沉的变量,而是一个会呼吸、会通知、会联动的活体数据

技术小贴士:在 Vue 3 的 Composition API 中,ref 内部使用了 JavaScript 的 Proxygetter/setter 机制,实现对 .value 访问和赋值的拦截,从而建立“依赖追踪”系统。


生命周期钩子:组件的“出生仪式”

接下来这段代码,是组件的“成人礼”:

onMounted(() => {
  console.log('组件挂载完成,开始从后端获取数据');
  fetch('/users')
    .then(res => res.json())
    .then(data => {
      users.value = data;
      console.log('从后端获取到的数据:', users.value);
    })
})
什么是 onMounted

Vue 组件有自己的“生命周期”:创建 → 挂载 → 更新 → 卸载。
onMounted 就是那个关键的“我已上线”时刻——当组件的 DOM 已经被渲染到页面上,Vue 就会执行这个回调函数。

这就像一个新生儿睁开眼睛的第一秒,立刻说:“世界你好!我要开始干活了!”

为什么在这里发请求?

因为:

  • 页面结构已经存在(模板已解析);
  • 此时发起 fetch('/users'),既能拿到数据,又能确保有地方展示它;
  • 如果在组件还没挂载前就操作 DOM 或赋值,可能会失败或造成内存泄漏。

于是,组件一“睁眼”,就向后端(由 json-server 提供的 /users 接口)发出请求,拿到 JSON 数据后:

users.value = data;

Boom!魔法触发!


重点:这一行代码,如何让页面“活”起来?

当你写下 users.value = data,看似只是赋值,实则引爆了一连串精妙的连锁反应:

  1. Vue 的响应式系统检测到 users 的值发生了变化
    → 因为 users 是用 ref 创建的,任何对 .value 的写入都会被拦截。
  2. 系统立刻找出所有“依赖”这个数据的地方
    → 在编译阶段,Vue 已经悄悄记录下:模板中用了 users 的地方(比如 v-for=&#34;user in users&#34;)都需要被通知。
  3. 重新执行相关的渲染逻辑
    → Vue 并不会重绘整个页面,而是只重新计算“受影响的部分”——也就是用户列表区域。
  4. 生成新的虚拟 DOM(Virtual DOM)
    → Vue 先在内存中构建一个轻量级的 DOM 树副本。
  5. 与旧虚拟 DOM 对比(diff 算法)
    → 找出最小差异:比如新增了三行、删除了零行。
  6. 精准更新真实 DOM
    → 只修改浏览器中真正需要变动的节点,避免不必要的重排重绘。

 整个过程毫秒级完成,用户毫无感知,却看到了最新数据!


声明式模板:所想即所得的 UI 编程 

现在,看看 `` 部分的神奇之处:

<table>
  <tr>
    <th>id</th>
    <th>name</th>
    <th>email</th>
  </tr>
<tbody>
  <tr>
    <td>{{user.id}}</td>
    <td>{{user.name}}</td>
    <td>{{user.email}}</td>
  </tr>
</tbody>
</table>

这里藏着 Vue 两大核心法宝:

{{ }}:插值表达式 —— 数据的“透明窗口”
  • {{user.name}} 不是一段字符串,而是一个动态绑定
  • 它告诉 Vue:“请在这里显示 user.name 的当前值,并且当它变时,自动刷新。”
  • 你不需要手动拼接 HTML,也不用担心 XSS(Vue 默认会转义内容,安全可靠)。
 v-for:列表渲染指令 —— 自动化的“克隆工厂”
  • v-for=&#34;user in users&#34; 是 Vue 的循环指令。
  • 它会遍历 users 数组,为每个 user 自动生成一行 <tr>
  • :key=&#34;user.id&#34; 提供唯一标识,帮助 Vue 高效追踪每个节点的身份(比如移动、删除时保持状态)。

在传统开发中,你要写:

// 找到表格
const tbody = document.querySelector('#user-table tbody');
// 清空
tbody.innerHTML = '';
// 循环创建行
data.forEach(user => {
  const tr = document.createElement('tr');
  tr.innerHTML = `<td>${user.id}</td><td>${user.name}</td>...`;
  tbody.appendChild(tr);
});

而在 Vue 中,你只需说:

“我想显示一个用户列表,每一行包含 id、name 和 email。”

然后 Vue 就默默完成了剩下的所有工作。

这就是声明式编程的魅力:你描述“是什么”,框架负责“怎么做”。

从此,开发者从 DOM 操作的泥潭中解放出来,专注于业务逻辑与用户体验——而这,正是现代前端框架最伟大的馈赠。


终章:三次飞跃,一条主线

时代 核心思想 数据流向 开发体验 用户体验
服务端渲染 “我给你完整的饭” 数据 → 服务端 → 完整 HTML → 浏览器 简单但笨重 刷新卡顿,交互弱
前后端分离 “我给你食材,你自己做” 浏览器 → 请求 API → 获取 JSON → 手动更新 DOM 灵活但繁琐 局部更新,但依赖手动编码
响应式框架 “你告诉我菜谱,我自动做饭” 数据变化 → 自动驱动视图更新 声明式、高效、可维护 流畅、实时、无感

结语:技术的本质是“解放人”

server.js 的字符串拼接,到 App.vue 的响应式绑定,表面看是代码风格的变迁,实则是开发范式的跃迁

从“命令式”(怎么做)走向“声明式”(做什么)

今天的前端开发者,不再需要关心“如何插入一行表格”,而是专注“用户列表应该展示哪些字段”。这种抽象,让我们能更专注于业务逻辑与用户体验。

而这,正是技术进步最美的样子——让复杂消失,让创造浮现

🌟 下次当你看到一个动态刷新的列表时,不妨微笑一下:那背后,是一场跨越二十年的工程智慧结晶。

❌
❌