⌨️ 第六章 Web技术与安全
📄 本篇一句话

MVC 把界面程序切成三权分立:Model 管数据、View 管显示、Controller 管调度;MVVM 在此之上加「数据绑定」——数据一变,界面自动跟着变,连手动刷新都省了。

📝 在本章的位置

上游:5-15~5-21 的面向对象(封装、多态、组合)是 MVC 的语法基础;5-1 的「一份事实只存一处」是它的思想源头。下游:6-7/6-8 的 DOM 操作与虚拟 DOM,正是前端框架实现 MVVM 的底层机关。

一、为什么需要切分

把数据、界面、逻辑全搅在一个函数里:改个按钮颜色得通读业务代码、换个数据源得重写界面、测试无从下手(5-3 的单元测试直接没法写)。解法=按职责切三块:

二、MVC:三权分立

用户点击 → Controller(调度员)
              ↓ 调用
            Model(数据层:查库、算账、存状态)
              ↓ 通知
            View(显示层:拿到数据渲染界面)

三、MVVM:绑定自动化

MVC 有个痛点:Model 变了,Controller 得手动通知 View 刷新,代码里满是「同步」杂活。MVVM(Model-View-ViewModel)加了一层 ViewModel:

Model ⇄ ViewModel(状态+绑定逻辑) ⇄ View(自动跟随)

代价是「魔法」变多:更新在框架黑盒里飞,出 bug 得穿透绑定链排查。MVC 是手工挡把,MVVM 是自动挡。

动手 Demo:同一个计数器,MVC 手动刷新 vs MVVM 绑定

# MVC 风格:Model 变了,Controller 手动通知 View 刷新
class CounterModel:
    def __init__(self): self.count = 0

def controller_add(model):
    model.count += 1
    view_render(model.count)          # 手动调 View —— 同步是杂活

def view_render(count):
    print(f"[View] 当前计数:{count}")

m = CounterModel()
controller_add(m); controller_add(m)   # [View] 当前计数:1 / 2

# MVVM 风格:绑定后,改数据界面自动跟(观察者模式的最小版)
class ViewModel:
    def __init__(self):
        self._count = 0
        self.listeners = []            # 绑定的 View
    @property
    def count(self):
        return self._count
    @count.setter
    def count(self, v):
        self._count = v
        for fn in self.listeners: fn(v)   # 数据一变,自动推送

vm = ViewModel()
vm.listeners.append(lambda c: print(f"[View·自动] 计数变为 {c}"))
vm.count += 1                          # [View·自动] 计数变为 1 —— 没人手动调 View
vm.count += 1                          # [View·自动] 计数变为 2

看到什么算成功:两种风格输出同样的计数;第二种里没有任何一行代码去调 View——setter 一执行,界面自动跟。

读结果:@property(5-17)+观察者回调就是 MVVM 绑定的全部原理——前端框架的「响应式」就是把这套机制做到每个字段上。

30 秒实验

在浏览器控制台跑:

// Vue 无构建版最小绑定实验(任何页面 F12 即可)
// 若页面没引 Vue 可跳过;换成读这段体会绑定思想也行
let state = { count: 0 };
const binds = [];
binds.push(c => console.log("界面显示:", c));
const vm = new Proxy(state, {
  set(obj, k, v) { obj[k] = v; binds.forEach(f => f(v)); return true; }
});
vm.count = 1;   // 控制台立刻打出「界面显示: 1」——改数据即刷界面
vm.count = 42;  // 「界面显示: 42」

看到什么算成功:只对 vm.count 赋值,没有任何「更新界面」的调用,控制台却跟着打新值——Proxy(代理)拦截赋值动作,这正是 Vue 3 响应式的核心机关。

常见误区

自测三问(点击展开) 1. **MVC 三块各管什么?** 答:Model 数据与业务、View 显示、Controller 接收输入做调度。 2. **MVVM 比 MVC 多了什么?** 答:ViewModel+数据绑定——数据变界面自动变,省掉手动同步。 3. **为什么分层之后单元测试才好写?** 答:Model 摆脱界面依赖,可以脱离 UI 直接测业务逻辑(5-3)。

相关笔记