MVC、MVP、MVVM:用户界面与业务逻辑的解耦

MVC、MVP、MVVM是前端开发中经典的软件架构模式,通过关注点分离实现用户界面与业务逻辑解耦,提升代码可维护性与可扩展性。

一、MVC:分层解耦,实现初步解耦

核心思想

将代码划分为Model(数据 / 逻辑)、View(界面 / 交互)、Controller(协调 / 控制)三大组件,每个组件有特定的职责

组件责职

组件 职责说明 关键特点
Model 数据管理与业务逻辑。包括数据的访问、处理和存储。 不关心数据展示;当数据发生变化时,通知View更新
View 界面展示与用户交互。从Model获取数据进行展示,同时将用户输入传递给Controller。 一个模型可以对应多个视图,支持不同的展示方式
Controller 协调View和Model。接收用户输入,调用 Model 处理数据,指定View进行渲染 扮演"中间协调者"角色

说明:在Web早期,用户交互主要是用户访问超链接或表单提交,服务器返回新的HTML页面。随着Ajax技术的出现,前后端分离,前端异步请求数据,后端返回JSON数据,由前端进行局部渲染更新页面。

交互流程

  • 用户与View交互,View将用户输入传递给Controller。
  • Controller调用Model处理数据。
  • Model执行完业务逻辑并返回数据。
  • Controller选择View返回给用户,View从Model获取数据生成HTML或JSON

典型框架

  1. Spring MVC

Spring MVC是一个经典的Java Web框架,一个请求处理流程如下:

  1. Backbone.js

早期的MVC框架主要应用在服务器端,随着前后端分离,前端也有自己的MVC框架。Backbone.js就是其中之一。

在前后端分离的架构下,前端主要做两件事情:视图展示和前后端数据同步,对应Backbone. js中两大组件:Model 和 View。

Model负责数据管理与业务逻辑,并通过RESTful API与后端同步数据。当数据变化时,Model会触发事件通知视图更新。

View监听Model变化,从Model获取数据进行展示;监听DOM事件,接收用户输入并调用Model处理数据。

Backbone.js的View比较厚,承担了MVC中的View和Controller的职责。

javascript 复制代码
// Model定义
var User = Backbone.Model.extend({    
    urlRoot: '/api/users'
});
// View定义
var UserView = Backbone.View.extend({    
    initialize: function() {        
        // 手动监听模型变化        
        this.listenTo(this.model, 'change', this.render);    
    },
    render: function() {       
        // 手动构建HTML或使用模板引擎        
        this.$el.html(`<h1>${this.model.get('name')}</h1>            
                       <p>Email: ${this.model.get('email')}</p>            
                       <button class="edit-btn">编辑</button>`);        
        return this;    
     },
    events: {        
        'click .edit-btn': 'editUser'    
    },
    editUser: function() {  
        // 手动创建编辑视图       
        var editView = new UserEditView({model: this.model});       
        $('#modal-container').html(editView.render().el);    
    }
});
// Controller控制流程
var user = new User({id: 123});
user.fetch();  // 从服务器获取数据
var userView = new UserView({model: user, el: '#user-container'});
userView.render();  // 初始渲染

补充:Backbone.js是早期的SPA开发框架,后来被Vue、React逐步代替。

MVC的优缺点

优点 缺点
修改View/Model互不影响 View依赖Model(需监听Model变化并获取数据)
View和Model支持并行开发 Controller职责过重,需要协调View/Model
一个Model可以对应多个View,支持不同的展示方式。 需要手动同步View和Model
可独立测试 View/Model

二、MVP:Presenter代理,实现彻底解耦

核心思想

用 Presenter 代替 Controller,彻底切断 View 与 Model 的直接通信。通过Presenter作为双向代理,实现两者的间接通信,解耦更彻底。

组件职责

组件 关键变化
View 仅提供渲染接口给Presenter调用,不需要从Model获取数据进行展示
Model 数据变化通知 Presenter 而非 View
Presenter 接收用户输入 → 调用 Model接口处理数据 → 调用 View 接口更新界面

交互流程

  • 用户与View交互,View调用Presenter接口处理事件。
  • Presenter调用Model处理数据。
  • Model完成后回调Presenter。
  • Presenter调用View接口更新界面。

典型框架

MVP框架主要应用在安卓界面开发,示例代码如下:

javascript 复制代码
class Todo {
    private content: string;
    constructor(content: string) {
        this.content = content;
    }
    getContent(): string {
        return this.content;
    }
}

interface ITodoModel {
    interface TodosCallback {
        onSuccess(): void;
    }
    saveTodo(todo: Todo, callback: GetTodosCallback): void;
}

interface ITodoView {
    showSuccess(): void;
}

interface ITodoPresenter {
    saveTodo(todo: Todo):void;
}

class TodoModel implement ITodoModel {
    saveTodo(todo: Todo, callback: GetTodosCallback): void {
        saveDB(todo);// 模拟保存数据库
        callback.onSuccess();
    }
}

class BasePresenter<T> {
    private mView: T;
    attachView(view: T):void {
        mView = view;
    }
    dettachView():void {
        mView = null;
    }
    getView():T {
        return mView;
    }
}

class TodoPresenter extends BasePresenter<ITodoView> implement ITodoModel.TodosCallbackI, TodoPresenter {
    todoModel: ITodoModel;
    constructor(){
        this.todoModel = new TodoModel();
    }
    saveTodo(todo: Todo):void {
        this.todoModel.saveTodo(todo, this);
    }
    onSuccess() {
        getView().showSuccess();
    }
}

TodoActivity extends AppCompatActivity implements ITodoView {
    private TextView mResultTv;
    private EditText mTodoContentEt;
    private TodoPresenter mTodoPresenter;
    onCreate(Bundle savedInstanceState):void {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_todo);
        initPresenter();
        initView();
    }
    onDestroy(): void {
        super.onDestroy();
        this.mTodoPresenter.dettachView();
     }
    initView():void {
        this.mResultTv = findViewById(R.id.tv_result);
        this.mTodoContentEt = findViewById(R.id.et_todo_content);

        findViewById(R.id.btn_add).setOnClickListener(new View.OnClickListener() {
            onClick(View view):void {
                const todo = getTodoInput();
                this.mTodoPresenter.saveTodo(todo);
            }
        });
     }
     initPresenter() {
         this.mTodoPresenter = new TodoPresenter();
         this.mTodoPresenter.attachView(this);
     }
     getTodoInput(): Todo {
         const content = mTodoContentEt.getText().toString();
         return new Todo(content);
     }
     showSuccess(): void {
         this.mResultTv.setText("success");
     }
}

解读:

  • ITodoModel的职责是数据管理,并通过回调通知ITodoPresenter。
  • ITodoView的职责是展示数据,调用ITodoPresenter接口处理用户输入。
  • ITodoPresenter负责接收用户输入,调用ITodoModel处理数据,然后调用ITodoView展示数据。

MVP的优缺点

优点 缺点
View 与 Model 完全解耦 Presenter职责过重,需要组织View/Model完成所有交互
基于接口通信,进一步解耦,可独立测试 需定义大量接口,增加代码复杂度
View 逻辑纯粹(仅负责渲染) 仍需要手动同步View和Model

三、MVVM:数据绑定,实现自动同步

核心思想

通过数据绑定实现 View 与 Model 的自动同步,减少手动DOM操作。

组件职责

组件 职责说明
ViewModel 响应式系统:监听 Model 变化并自动同步到 View;监听 DOM 事件并自动更新 Model。
View 声明式地绑定关联的Model,无需手动操作 DOM。
Model 数据管理与业务逻辑,不关系数据展示。

交互流程

  • 用户与View交互,ViewModel自动将用户输入同步给Model。
  • Model执行业务逻辑和操作数据,更新状态。
  • Model变化后,ViewModel自动将Model同步到View。

典型框架

2010年后,Vue.js和React等主流框架采用MVVM架构,减少了DOM操作。

html 复制代码
<body>
    <div id="app">
        常见搜索关键字:<span @click="setKeyword('人工智能')">人工智能</span>
        <input
            v-model="keyword" 
            placeholder="请输入要搜索的关键字..." 
            @keyup.enter="search"
        >
        <button @click="search">
            搜索
        </button>
        <div v-if="loading">
            正在搜索中,请稍候...
        </div>
        <div v-for="result in searchResults" :key="result.id">
            <div>{{ result.content }}</div>
        </div>
    </div>
  </body>

  <script>
        const { createApp, ref, computed } = Vue;
        createApp({
            setup() {
                const keyword = ref('');
                const loading = ref(false);
                const searchResults = ref([]);
                // 模拟数据
                const allResults = ref([
                    {
                        id: 1,
                        content: 'Vue.js 入门教程',
                    },
                    {
                        id: 2,
                        content: 'JavaScript 高级编程',
                    },
                ]);
                const setKeyword = (example) => {
                    keyword.value = example;
                    search();
                };
                const search = () => {
                    const searchTerm = keyword.value.toLowerCase();
                    searchResults.value = allResults.value.filter(result => (
                            result.content.toLowerCase().includes(searchTerm));
                    );
                };
                
                return {
                    keyword,
                    setKeyword,
                    loading,
                    searchResults,
                    search,
                };
            }
        }).mount('#app');
    </script>

解读:

  • 在Vue.js中使用v-model指令声明式地实现双向绑定。当用户输入关键字时,数据会自动同步到keyword变量。反之,当用户点击"常见搜索关键字" 按钮,改变keyword变量的值,也会自动同步到输入框。
  • 在Vue.js模版中使用{{}}声明式地实现单向绑定。当执行完搜索逻辑并把结果赋值给searchResults变量,数据会被自动渲染到结果列表。

MVVM的优缺点

优点 缺点
自动同步View和Model,减少手动 DOM 操作 数据绑定可能带来性能开销
ViewModel 可独立测试 双向绑定,调试复杂度高

四、总结

架构 核心思想 优点 缺点 适用场景
MVC 分层解耦 职责分离,易于扩展 View依赖Model 简单应用、后端开发
MVP 代理隔离 彻底切断 View 与 Model 关联 需要手动同步View和Model 中大型应用、需严格测试
MVVM 数据绑定 实现View和Model自动同步 有一定的性能与调试成本 现代前端应用、SPA 开发
相关推荐
heartbeat..1 天前
Spring MVC 全面详解(Java 主流 Web 开发框架)
java·网络·spring·mvc·web
自在极意功。1 天前
简单介绍SpringMVC
java·mvc·springmvc·三层架构
威桑2 天前
深入理解 MVC 模式的优缺点
系统架构·mvc
alonewolf_992 天前
Spring MVC启动与请求处理全流程解析:从DispatcherServlet到HandlerAdapter
java·spring·mvc
廋到被风吹走2 天前
【Spring】Spring MVC核心原理与RESTful最佳实践详解
spring·mvc·restful
alonewolf_993 天前
Spring MVC重点功能底层源码深度解析
java·spring·mvc
冰茶_3 天前
WPF路由事件:隧道与冒泡机制解析
学习·c#·.net·wpf·.netcore·mvvm
撩得Android一次心动4 天前
Android 架构模式的演变(MVC、MVP、MVVM、MVI)
android·架构·mvc·mvvm·mvp
小信丶5 天前
Spring MVC 配置器:WebMvcConfigurer 详解、应用场景和示例代码
java·spring·mvc
柒.梧.6 天前
深度解析SpringMVC实战项目:从配置到请求处理全流程
java·spring·mvc