真正可维护的mvp需补全model层、view接口、presenter依赖注入三块:model为纯js对象,view提供render/clear等抽象方法,presenter仅接收view和model实例,彻底解耦dom操作。

纯 HTML + 原生 JS 或 jQuery 实现 MVP,本质是人为划分职责,不是靠框架自动绑定;没有 Model 层的“空壳 MVP”很常见,但真要落地,Presenter 必须能解耦 View 和 Model 的直接依赖,否则只是披着 MVP 外衣的事件脚本。
为什么 jQuery 项目里常写成“假 MVP”
多数示例(比如你看到的 Page 构造函数)只实现了 View → Presenter 的调用链,Presenter 直接操作 DOM、拼接字符串、清空输入框——这其实是把 View 的逻辑硬塞进了 Presenter,Model 彻底缺席。
-
Presenter里出现$('#inputValue').val()或ul.append(...):说明它在干View的活,违反 MVP 的“Presenter 不持有 DOM 引用”原则 - 所有数据都来自表单实时读取、无缓存、无校验、无异步加载:意味着根本没有独立
Model实体,data就是临时变量 - 测试困难:因为
Presenter方法强依赖 jQuery 对象,无法脱离浏览器环境单元测试
真正可维护的 MVP 要补哪三块
补全 MVP 不需要重写整个项目,从现有代码里拆出三个明确边界即可:
-
Model 层:定义为纯 JS 对象或类,只暴露
getData()、saveItem(item)、onDataChange(callback)这类方法,内部不碰document或$ -
View 接口:不是 DOM 元素本身,而是抽象方法集合,如
renderList(items)、clearInput()、showError(msg);Presenter只调这些方法,不关心它们怎么实现 -
Presenter 初始化时只接收 View 实例和 Model 实例:例如
new TodoPresenter(new TodoView(), new TodoModel()),彻底切断对选择器的硬编码依赖
jQuery 环境下避免 DOM 泄露的关键写法
在仍用 jQuery 的老项目中,让 Presenter 不直接操作 DOM,核心是把 DOM 操作收口到 View 类里:
class TodoView {
constructor() {
this.$input = $('#inputValue');
this.$list = $('#ul');
this.$btn = $('#btn');
}
getInputValue() { return this.$input.val(); }
clearInput() { this.$input.val(''); }
renderList(items) {
this.$list.empty();
items.forEach(item => this.$list.append(`
这样改完后,Presenter 里再不会出现 $ 符号;所有 jQuery 调用被限制在 TodoView 内部,替换为原生 JS 或 Vue 只需重写这个类,Presenter 和 Model 完全不动。
容易被忽略的边界问题
MVP 在 HTML 场景中最容易模糊的是错误处理与副作用时机:
-
Model抛错时,Presenter应捕获并转成view.showError(),而不是在Model里直接alert()或操作 DOM -
View的renderList()方法应完全同步,不包含动画、延迟或请求;异步加载逻辑属于Presenter协调,但具体 fetch 调用应封装进Model - 如果页面有多个
View(比如列表页 + 详情弹窗),Presenter需持有多个View实例,但每个View仍只负责自己那块 DOM —— 切忌让一个View方法去更新另一块区域
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











