MVC/MVVM架构
MVC和MVVM是前端架构的核心模式,理解它们的原理有助于更好地使用现代前端框架。
MVC架构模式
核心概念
MVC将应用分为三个核心组件:
- Model:数据层,管理业务数据和逻辑
- View:视图层,负责UI展示
- Controller:控制器,处理用户交互,协调Model和View
MVC流程
JavaScript
用户操作 → Controller → Model更新 → View刷新
基础实现
JavaScript
// Model
class UserModel {
constructor() {
this.users = [];
this.listeners = [];
}
addUser(user) {
this.users.push(user);
this.notify();
}
getUsers() {
return this.users;
}
subscribe(listener) {
this.listeners.push(listener);
}
notify() {
this.listeners.forEach(listener => listener(this.users));
}
}
// View
class UserView {
constructor(container) {
this.container = container;
}
render(users) {
this.container.innerHTML = users.map(user =>
`<div class="user">${user.name}</div>`
).join('');
}
bindAddUser(handler) {
document.getElementById('addBtn').addEventListener('click', handler);
}
}
// Controller
class UserController {
constructor(model, view) {
this.model = model;
this.view = view;
this.model.subscribe(users => this.view.render(users));
this.view.bindAddUser(() => this.handleAddUser());
}
handleAddUser() {
const name = document.getElementById('nameInput').value;
this.model.addUser({ name });
}
}
// 初始化
const model = new UserModel();
const view = new UserView(document.getElementById('userList'));
const controller = new UserController(model, view);
MVC变体
被动MVC
JavaScript
// View被动等待Controller更新
class UserView {
render(users) {
this.container.innerHTML = users.map(...);
}
}
// Controller主动更新View
class UserController {
handleAddUser() {
this.model.addUser({ name });
this.view.render(this.model.getUsers()); // Controller主动更新View
}
}
主动MVC
JavaScript
// Model主动通知View更新(观察者模式)
class UserModel {
notify() {
this.listeners.forEach(listener => listener(this.users));
}
}
class UserView {
constructor(model) {
this.model = model;
model.subscribe(users => this.render(users)); // View订阅Model
}
}
MVVM架构模式
核心概念
MVVM引入ViewModel层:
- Model:数据层
- View:视图层
- ViewModel:视图模型,连接Model和View,实现数据绑定
MVVM流程
JavaScript
View ↔ ViewModel ↔ Model
(双向数据绑定)
数据绑定实现
JavaScript
// 简易MVVM实现
class ViewModel {
constructor(data) {
this.data = {};
this.listeners = {};
// 数据劫持
Object.keys(data).forEach(key => {
this.defineReactive(key, data[key]);
});
}
defineReactive(key, value) {
this.data[key] = value;
this.listeners[key] = [];
Object.defineProperty(this, key, {
get() {
return this.data[key];
},
set(newValue) {
if (newValue === this.data[key]) return;
this.data[key] = newValue;
this.listeners[key].forEach(listener => listener(newValue));
}
});
}
watch(key, callback) {
this.listeners[key].push(callback);
}
}
// View绑定
class Binder {
constructor(viewModel, view) {
this.viewModel = viewModel;
this.view = view;
this.bindData();
this.bindEvents();
}
bindData() {
const inputs = this.view.querySelectorAll('[data-bind]');
inputs.forEach(input => {
const key = input.dataset.bind;
// View → ViewModel
input.addEventListener('input', () => {
this.viewModel[key] = input.value;
});
// ViewModel → View
this.viewModel.watch(key, (value) => {
input.value = value;
});
});
}
bindEvents() {
const buttons = this.view.querySelectorAll('[data-event]');
buttons.forEach(button => {
const [eventType, method] = button.dataset.event.split(':');
button.addEventListener(eventType, () => {
this.viewModel[method]();
});
});
}
}
// 使用
const vm = new ViewModel({ name: '', age: '' });
const binder = new Binder(vm, document.getElementById('form'));
响应式实现(Proxy)
JavaScript
// 使用Proxy实现响应式
function reactive(data) {
const callbacks = new Map();
return new Proxy(data, {
get(target, key) {
return target[key];
},
set(target, key, value) {
target[key] = value;
if (callbacks.has(key)) {
callbacks.get(key).forEach(cb => cb(value));
}
return true;
}
});
}
function watch(proxy, key, callback) {
if (!proxy.callbacks) proxy.callbacks = new Map();
proxy.callbacks.set(key, [callback]);
}
// 使用
const state = reactive({ count: 0, name: 'Alice' });
watch(state, 'count', (value) => {
document.getElementById('count').textContent = value;
});
state.count = 10; // 自动更新UI
现代框架中的MVVM
Vue实现原理
JavaScript
// Vue响应式系统示意
function defineReactive(obj, key, value) {
const dep = new Dep();
Object.defineProperty(obj, key, {
get() {
if (Dep.target) {
dep.addSub(Dep.target);
}
return value;
},
set(newValue) {
if (newValue === value) return;
value = newValue;
dep.notify();
}
});
}
class Dep {
constructor() {
this.subs = [];
}
addSub(watcher) {
this.subs.push(watcher);
}
notify() {
this.subs.forEach(watcher => watcher.update());
}
}
class Watcher {
constructor(vm, key, callback) {
this.vm = vm;
this.key = key;
this.callback = callback;
Dep.target = this;
this.value = vm[key]; // 触发getter,收集依赖
Dep.target = null;
}
update() {
const newValue = this.vm[this.key];
this.callback(newValue);
}
}
Vue组合式API
JavaScript
// Vue 3 Composition API
import { ref, reactive, computed, watch } from 'vue';
export default {
setup() {
// ref:基础类型响应式
const count = ref(0);
// reactive:对象响应式
const state = reactive({
name: 'Alice',
age: 25
});
// computed:计算属性
const doubleCount = computed(() => count.value * 2);
// watch:侦听器
watch(count, (newValue, oldValue) => {
console.log(`count: ${oldValue} → ${newValue}`);
});
return {
count,
state,
doubleCount
};
}
};
React的状态管理
text
// React单向数据流
function Counter() {
const [count, setCount] = useState(0);
return (
<div>
<p>Count: {count}</p>
<button onClick={() => setCount(count + 1)}>
Increment
</button>
</div>
);
}
// 自定义Hook(类似ViewModel)
function useUserViewModel(initialUser) {
const [user, setUser] = useState(initialUser);
const [loading, setLoading] = useState(false);
const updateUser = async (data) => {
setLoading(true);
const updated = await api.updateUser(data);
setUser(updated);
setLoading(false);
};
return { user, loading, updateUser };
}
MVC vs MVVM对比
| 特性 | MVC | MVVM |
|---|---|---|
| 数据流 | 单向 | 双向绑定 |
| View更新 | Controller主动/被动 | 自动响应 |
| 测试性 | 较易 | ViewModel易测试 |
| 适用场景 | 服务端渲染 | 客户端渲染 |
| 代表框架 | Backbone | Vue、Angular |
选择建议
text
简单页面/服务端渲染 → MVC
复杂交互/客户端渲染 → MVVM
MVVM的双向绑定简化了View更新逻辑,但也增加了调试难度。
架构实践示例
用户管理模块(MVVM)
text
class UserViewModel {
constructor(api) {
this.api = api;
this.users = reactive([]);
this.loading = reactive(false);
this.error = reactive(null);
}
async fetchUsers() {
this.loading = true;
this.error = null;
try {
const users = await this.api.getUsers();
this.users = users;
} catch (err) {
this.error = err.message;
} finally {
this.loading = false;
}
}
async addUser(user) {
try {
const created = await this.api.createUser(user);
this.users.push(created);
} catch (err) {
this.error = err.message;
}
}
}
// View绑定
class UserView {
constructor(viewModel) {
this.viewModel = viewModel;
this.bind();
}
bind() {
// 数据绑定
watch(this.viewModel, 'users', users => this.renderUsers(users));
watch(this.viewModel, 'loading', loading => this.toggleLoader(loading));
watch(this.viewModel, 'error', error => this.showError(error));
// 事件绑定
document.getElementById('refreshBtn').addEventListener('click',
() => this.viewModel.fetchUsers());
}
renderUsers(users) {
const list = document.getElementById('userList');
list.innerHTML = users.map(u => `<li>${u.name}</li>`).join('');
}
toggleLoader(loading) {
document.getElementById('loader').style.display = loading ? 'block' : 'none';
}
}
要点总结
- MVC三层:Model数据、View展示、Controller协调
- MVVM核心:ViewModel实现数据绑定,解耦View和Model
- 数据绑定:Object.defineProperty或Proxy实现响应式
- Vue实现:依赖收集 + 响应式劫持 + Watcher更新
- React差异:单向数据流,使用setState触发更新
存放路径:articles/JS/专家/设计模式与架构思想/MVC/MVVM架构.md
📝 发现内容有误?点击此处直接编辑