从 <table> 到 ref():Web 开发的奇幻漂流——一场关于“谁该负责界面”的千年之辩

68 阅读5分钟

从 <table> 到 ref():Web 开发的奇幻漂流——一场关于“谁该负责界面”的千年之辩

“从前,有一个程序员,他每天的工作就是拼字符串。”
——《前端考古学·卷一》

如果你穿越回 2005 年,走进一家互联网公司,你可能会看到这样的场景:后端工程师一边敲着 Java 或 PHP,一边在 .jsp.php 文件里夹杂着 <% if (user != null) { %> 和 ``。他们不是在写业务逻辑,而是在用代码缝合 HTML 的补丁

而今天,一个 Vue 工程师只需写下:

const users = ref([]);

然后在模板里:

<tr>
  <td>{{ user.name }}</td>
</tr>

——用户列表就自动出现了。数据一变,界面跟着跳舞。仿佛魔法。

那么,这中间到底发生了什么?是谁把我们从“字符串拼接地狱”中拯救出来的?

让我们开启这场 Web 开发的奇幻漂流。


第一章:黑暗时代——后端即上帝(MVC 的黄金牢笼)

1.1 “全栈”其实是“全能苦力”

在 Web 1.0 时代,HTTP 是个极其朴素的协议:请求 → 响应 → 刷新。没有 AJAX,没有 WebSocket,甚至连 JSON 都还没普及(XML 才是贵族)。

于是,所有逻辑都压在服务器上。典型的 MVC 流程如下:

  1. 用户访问 /users
  2. Controller 查询数据库(Model)
  3. 拿到数据后,用模板引擎(如 EJS、Thymeleaf)把数据“塞”进 HTML
  4. 整个页面作为字符串返回给浏览器

就像这样(Node.js 简化版):

res.end(`
  
    
      <h1>欢迎, ${user.name}!</h1>
      <p>你的邮箱是: ${user.email}</p>
    
  
`);

听起来简单?但当页面复杂起来——比如要加个分页、筛选、动态高亮——你就得在字符串里嵌套 if、for、escape、encode……稍有不慎,XSS 攻击就找上门来。

开发者内心 OS
“我到底是写业务的,还是写 HTML 的?为什么我要关心 < 要转成 <?”

1.2 模板引擎:缝合怪的诞生

为了解决“硬拼字符串”的痛苦,人们发明了模板引擎:EJS、Pug、Handlebars、Jinja2……

它们允许你写:

<% users.forEach(user => { %>
  <tr>
    <td><%= user.id %></td>
    <td><%= user.name %></td>
  </tr>
<% }) %>

看起来清爽多了?但问题没消失,只是被包装了:

  • 前端想改样式?得等后端部署;
  • 想做个无刷新搜索?对不起,整页刷新走起;
  • 前端工程师?不存在的,都是“会切图的后端”。

MVC 成了“Model-View-Controller,但 View 归后端管”的缩写


第二章:觉醒年代——AJAX 与前后端分离的革命

2.1 2005 年,Gmail 改变了世界

当 Gmail 首次实现“不刷新页面就能收邮件”时,全世界程序员都惊呆了。背后的技术叫 AJAX(Asynchronous JavaScript and XML)

虽然名字带 XML,但很快大家发现:JSON 更香

于是,新的协作模式诞生了:

  • 后端只提供 API:GET /api/users → [{id:1, name:&#34;舒俊&#34;}]
  • 前端用 JavaScript 拉取数据,自己画界面
fetch('/api/users')
  .then(r => r.json())
  .then(users => {
    // 手动操作 DOM
    const ul = document.getElementById('user-list');
    users.forEach(u => {
      const li = document.createElement('li');
      li.textContent = u.name;
      ul.appendChild(li);
    });
  });

2.2 自由的代价:DOM 地狱

自由是有了,但新痛苦来了——DOM 编程太反人类

想象你要实现一个“用户列表支持增删改查 + 实时搜索 + 排序”:

  • 每次数据变化,你都要清空 tbody,重新 appendChild;
  • 如果某个用户被删除,你得找到对应的 <tr> 并 remove;
  • 搜索时,不能直接 filter 数据,还得同步更新 DOM;
  • 一不小心,内存泄漏、重复渲染、闪烁问题全来了。

前端工程师的日常
“我写了 200 行代码,其中 180 行在找节点、删节点、建节点……我的业务逻辑呢?”

这时候,大家开始怀念后端模板的“自动渲染”——但又不想回到整页刷新的石器时代。

我们需要一种既能保留 SPA 体验,又能像模板一样“声明式渲染”的东西


第三章:魔法降临——响应式编程的崛起

3.1 什么是“响应式”?一个生活比喻

想象你有一块白板,上面写着:

“今日菜单:{{ dish }}”

旁边有个厨师,手里拿着一张纸条:“dish = 红烧肉”。

当你把纸条改成“dish = 宫保鸡丁”,白板上的字自动变成“今日菜单:宫保鸡丁” ——不需要你擦掉重写,也不需要你喊“更新菜单!”。

这就是响应式数据是源头,视图是影子。源头一动,影子自动跟随

3.2 Vue/React 如何实现魔法?

以 Vue 3 为例:

const users = ref([
  { id: 1, name: '舒俊' }
]);

ref() 不只是一个数组,它是一个被“施了魔法”的对象。Vue 在内部做了两件事:

  1. 依赖收集:当模板读取 users 时,Vue 记下:“这个组件依赖 users”;
  2. 触发更新:当 users.value.push(...) 时,Vue 知道:“哦,依赖变了,重新渲染组件”。

整个过程对开发者透明。你只管改数据,框架负责更新界面。

3.3 对比:三种开发心智模型

模式开发者思考方式代码重心
后端模板“怎么把数据塞进 HTML 字符串?”字符串拼接、转义、循环嵌套
原生 DOM“怎么找到那个元素并改它的 innerHTML?”querySelector、createElement、appendChild
响应式框架“我的数据应该是什么样子?”声明数据结构,描述 UI 与数据的关系

从“命令式”走向“声明式” ,是现代前端的核心哲学。


第四章:为什么响应式是未来的答案?

4.1 解耦:让专业的人做专业的事

  • 后端:专注 API 设计、数据库优化、权限控制、高并发;
  • 前端:专注用户体验、动效、状态管理、无障碍访问;
  • 接口契约(如 OpenAPI)成为唯一沟通桥梁。

团队可以并行开发:前端用 Mock 数据先行,后端按规范出接口,最后无缝对接。

4.2 性能与体验的双赢

  • 虚拟 DOM(React)或编译时优化(Vue)确保最小化真实 DOM 操作;
  • 组件化让 UI 可复用、可测试;
  • 状态管理(Pinia、Zustand)让复杂交互不再混乱。

4.3 未来已来:不止于浏览器

响应式思想已蔓延至全栈:

  • 服务端渲染(SSR) :Nuxt、Next.js 在服务器生成 HTML,首屏快,SEO 友好;
  • 跨端开发:Taro、UniApp 用同一套响应式逻辑编译到小程序、App;
  • 低代码平台:拖拽组件 + 绑定数据源,本质仍是“数据驱动视图”。

尾声:我们终于可以只关心“业务”了

回到开头那段 Node.js 代码:

res.end(generateUserHTML(users));

它代表了一个时代——界面是后端的附属品

而今天的 Vue 组件:


  <tr>
    <td>{{ user.name }}</td>
  </tr>

它宣告了一个新时代——界面是数据的自然延伸

我们不再问:“怎么把数据画出来?”
而是问:“数据应该是什么?”

这才是真正的开发者解放

所以,下次当你写下 ref() 时,请记得:
你不是在调用一个函数,
你是在施展一场持续了二十年的魔法。

🪄✨


附:致谢那些改变历史的英雄们

  • 2005 年,Jesse James Garrett 提出 “AJAX” 一词;
  • 2010 年,Angular 开启 MVVM 时代;
  • 2014 年,React 引入虚拟 DOM;
  • 2013 年,尤雨溪发布 Vue.js;
  • 以及无数在深夜调试 DOM 的你我。

正是这些点滴,汇成了今天的河流。