从 MVC 到 Vue:前端终于不用当后端的附庸了

41 阅读3分钟

响应式数据驱动视图:从后端套模板到 Vue 的演进之路

“让界面随数据而动”——这是现代前端开发的核心理念。

在 Web 开发的早期阶段,我们并没有今天这样清晰的前后端分工。页面渲染完全依赖后端逻辑,前端只是被动地展示 HTML。随着业务复杂度提升和用户体验要求提高,开发模式也在不断进化。本文将带你回顾这一演进过程:从后端套模板 → 手动 DOM 操作 → 响应式数据驱动(以 Vue 为例)


一、后端套模板时代:MVC 与服务端渲染

在 Web 开发的早期阶段,前后端尚未分离,典型的架构模式是 MVC(Model-View-Controller) ,整个页面由后端负责生成:

  • Model:代表业务数据,通常来自数据库(如 MySQL)。
  • View:HTML 模板,内嵌占位符(如 ${user.name} ,用于动态填充数据。
  • Controller:接收 HTTP 请求,调用 Model 获取数据,将数据注入 View,最终返回完整的 HTML 响应。

这种模式下,前端几乎不承担逻辑处理,仅作为“展示层”被动接收后端渲染好的页面。

例如,在 Node.js 中使用原生 http 模块实现一个简易的服务端渲染服务器:

// 引入 Node.js 内置模块(基于 CommonJS 规范)
const http = require('http');
const url = require('url');

// 模拟数据库中的用户数据(Model)
const users = [
  { id: 1, name: '张三', email: 'zhangsan@qq.com' },
  { id: 2, name: '李四', email: 'lisi@qq.com' },
  { id: 3, name: '王五', email: 'wangwu@qq.com' }
];

// View:通过模板字符串生成完整 HTML
function generateUsersHtml(users) {
  const userRows = users.map(user => `
    <tr>
      <td>${user.id}</td>
      <td>${user.name}</td>
      <td>${user.email}</td>
    </tr>
  `).join('');

  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>

`;
}

// Controller:处理路由与响应
const server = http.createServer((req, res) => {
  const parsedUrl = url.parse(req.url, true);
  
  if (parsedUrl.pathname === '/' || parsedUrl.pathname === '/users') {
    res.statusCode = 200;
    res.setHeader('Content-Type', 'text/html; charset=utf-8');
    res.end(generateUsersHtml(users)); // 返回完整 HTML
  } else {
    res.statusCode = 404;
    res.setHeader('Content-Type', 'text/plain');
    res.end('404 Not Found');
  }
});

server.listen(1314, () => {
  console.log('Server is running on http://localhost:1314');
});

当用户访问 http://localhost:1314/users 时,服务器直接拼接并返回完整的 HTML 页面。前端在此过程中没有任何主动权,仅作为“接收者”呈现内容

优点:首屏加载快、天然支持 SEO(搜索引擎可直接抓取完整 HTML)
缺点

  • 前后端高度耦合,前端无法独立开发或测试;
  • 每次交互(如翻页、筛选)都需要重新请求整页,导致体验卡顿;
  • 模板逻辑混杂在服务端代码中,难以维护;
  • 缺乏组件化思想,复用性差。

此外,值得注意的是:早期 JavaScript 并无模块化能力(ES Modules 直到 ES2015 才引入),而 Node.js 采用 CommonJS(require/module.exports)作为其模块系统,这也反映了当时全栈开发以服务端为中心的技术生态。

二、前后端分离:前端开始“主动拉取数据”

随着 AJAX 和 fetch 的普及,前后端分离成为主流:

  • 后端不再返回 HTML,而是提供 JSON API(如 GET /users 返回用户列表,我们这里使用json-server实现。
  • 前端通过 fetchXMLHttpRequest 主动请求数据,再用 JavaScript 操作 DOM 渲染页面。

比如在 index.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>
        
      

// 后端接口准备好了
// DOM 编程
fetch('http://localhost:3000/users')
    .then(res => res.json())
    .then(data => {
        //console.log(data);
        const tbody = document.querySelector('tbody');
        tbody.innerHTML = data.map(user => `
            <tr>
                <td>${user.id}</td>
                <td>${user.name}</td>
                <td>${user.email}</td>
            </tr>
        `).join('')
    })


✅ 优点:前后端解耦,前端可独立开发;支持 SPA(单页应用),体验更流畅
❌ 缺点:大量手动 DOM 操作,代码冗长、易错、难以维护;开发者注意力被“找节点、更新节点”分散,偏离了业务本身


三、响应式数据驱动:Vue 的出现让一切回归业务

Vue.js 等现代框架的出现,彻底改变了这一局面。其核心思想是:

“数据即视图” —— 只需关注数据变化,界面自动更新。

App.vue 中,我们看到:


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

onMounted(() => {
  fetch(&#34;http://localhost:3000/users&#34;)
    .then(res => res.json())
    .then(data => { users.value = data; });
});



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

这里的关键在于:

  • 通过ref,将users包装成为一个响应式对象。
  • users.value 被赋值新数据时,Vue 自动追踪依赖并更新 DOM
  • 开发者无需调用 document.getElementById 或操作 innerHTML

✅ 优势:

  • 聚焦业务逻辑:你只关心“数据是什么”,而不是“怎么更新界面”。
  • 声明式模板{{ }}v-for 让模板直观表达数据结构。
  • 性能优化:Vue 内部使用虚拟 DOM 和 diff 算法,高效更新真实 DOM。

四、总结:开发范式的演进本质

阶段渲染方式数据流向开发焦点
后端套模板服务端渲染 HTML后端 → 前端(整页)后端逻辑 + 模板语法
前后端分离(DOM 操作)前端 JS 操作 DOMAPI → 前端 → 手动更新 DOMDOM 节点查找与更新
响应式框架(Vue)响应式数据驱动视图API → 响应式数据 → 自动更新纯粹的业务与数据

真正的进步,不是工具更强大,而是让开发者更专注于解决问题本身。

响应式数据驱动,正是这一理念的最佳体现。


🌟 结语:从 res.end(html)ref([]),我们走过的不仅是技术路线的变迁,更是对“开发效率”与“人本体验”的持续追求。未来,或许还会有更优雅的范式,但“数据驱动视图”的思想,已然成为现代前端的基石。