React 中 MVC 已死——資深開發(fā)者用這個(gè)取而代之
先把丑話說在前面:
MVC 在 React 里,不是“老了”,也不是“用久了就不太合適”, 而是——從一開始就和 React 的底層機(jī)制水火不容。
如果你現(xiàn)在還在用 Controller、View 層、偽 Model 來組織 React 應(yīng)用, 那你不是在“做架構(gòu)”, 而是在和 React 的設(shè)計(jì)對(duì)著干。
一旦你的代碼庫超過十來個(gè)組件, 或者你的團(tuán)隊(duì)從兩個(gè)人變成一個(gè)小組, 這種對(duì)著干的成本,就會(huì)開始成倍放大, 貴到你心態(tài)炸裂。
到了 2025 年,現(xiàn)代 React 架構(gòu)已經(jīng)有了非常明確的共識(shí):
- 組合優(yōu)先,而不是“強(qiáng)行分層”
- 狀態(tài)本地化,而不是“全局控盤”
- 有意設(shè)置邊界,而不是隨手建一個(gè)叫 controllers 的文件夾
高級(jí)工程師們?cè)缇颓那膿Q了一套心智模型, MVC 在這次“重構(gòu)思維”的過程中,沒有活下來。
不是因?yàn)樗疤珷€”。
而是因?yàn)椋?/span>React 本身就讓它失去了意義。
為什么 MVC 在現(xiàn)代 React 大型項(xiàng)目里,會(huì)變成一場災(zāi)難?
先把核心差異攤開講清楚——
MVC 假定的是“控制流”。React 假定的是“數(shù)據(jù)流”。
這兩個(gè)前提,從根上就是反的。
- 在 MVC 里,動(dòng)作是從 Controller 進(jìn)來的;
- 在 React 里,數(shù)據(jù)是從組件樹里流動(dòng)的。
- 在 MVC 里,UI 是一塊“被動(dòng)展示層”;
- 在 React 里,UI 本身就是系統(tǒng),它跟狀態(tài)、邏輯綁得死死的。
當(dāng)工程師硬要在 React 里塞一個(gè) MVC:
他們會(huì)重新造一遍 React 已經(jīng)提供的那套抽象—— hooks、組合、單向數(shù)據(jù)流、聲明式渲染。
然后再反過來問: “怎么感覺應(yīng)用又慢又脆,一改就全崩?”
你最后看到的,往往是這樣的怪物:
- 名叫 Controller,卻什么都管不住;
- 叫 Model,卻只是幾個(gè)散在文件里的類型定義;
- 被稱作 View 的組件,還要自己去調(diào)各種 service,因?yàn)榧軜?gòu)圖對(duì)你撒過謊。
圖畫得很專業(yè)。
代碼寫得很割裂。
性能悄無聲息地掉。
調(diào)試現(xiàn)場變成了考古現(xiàn)場。
你追著調(diào)用棧看:
Developer → Controller → State → View → Service → Controller
↑ ↓
Re-render 地獄 競態(tài)條件MVC 在 React 里不是“優(yōu)雅退場”, 而是越用越放大復(fù)雜度,卻一副“我在幫你降復(fù)雜度”的樣子。
MVC 正在拖垮 React 團(tuán)隊(duì),是在那次上線事故之后
壓垮我們的,不是什么“架構(gòu)理論之爭”。 而是一次真刀真槍的部署事故。
在那套 MVC 思維下:
- 每加一個(gè)功能,要碰五個(gè)文件、跨三個(gè)文件夾;
- 每修一個(gè) bug,要改所謂的“Controller 邏輯”, 但那個(gè)邏輯其實(shí)和渲染沒有半毛錢關(guān)系;
- 各種“Model 文件”只剩類型容器,業(yè)務(wù)邏輯則像一層抹不開的黃油, 被抹在各個(gè)角落——到處都是一點(diǎn),但哪兒都不完整。
直到那次生產(chǎn)事故。
只是一個(gè)看似無害的“價(jià)格策略更新”, 結(jié)果觸發(fā)了六個(gè) container 一起重渲染。
UI 卡死。 Redux 的 action 像陷入死循環(huán)一樣狂刷。 Controller 邏輯以一種我們完全沒預(yù)料到的方式, 在各處短路了狀態(tài)流轉(zhuǎn)。
不是 React 的錯(cuò)。
而是我們用 MVC, 硬生生把 React 的渲染模型逼到了反方向。
那一刻我們很痛快地做了兩件事:
- 把那套“看起來很高級(jí)”的架構(gòu)刪了;
- 從零開始,按 React 的真實(shí)行為,而不是課本的圖,重建整個(gè)結(jié)構(gòu)。
取代 MVC 的,不是新縮寫,而是更誠實(shí)的“責(zé)任邊界”
高級(jí)工程師們沒有用另一個(gè)“三個(gè)字母”的詞,來替換 MVC。
他們只是把系統(tǒng)切成了更接近現(xiàn)實(shí)的三塊:
領(lǐng)域邏輯 → 放在 hooks 里
UI → 放在組件里
數(shù)據(jù)操作 → 放在 service 函數(shù)里
不是放在某個(gè)“高大上的層”, 也不是按文件夾名字劃邊界, 而是按“誰真正負(fù)責(zé)什么”來切。
- Controller? 被 custom hooks 接管,變成真正的“編排引擎”。
- Model? 不再是某個(gè)孤零零的文件,而是被拆散到“就近的狀態(tài)”里, 狀態(tài)本地化、上下文化。
- View? 不再是假裝“傻瓜模板”,而是直接承載行為與交互。
用一條更貼近 2025 年現(xiàn)實(shí)的 React 架構(gòu)路線來描述,大概是這樣的:
UI Components(組件)
↓
Custom Hooks(業(yè)務(wù)邏輯)
↓
Services(IO + API 調(diào)用)變化看起來很小, 似乎只是把“Controller 邏輯”搬進(jìn)了 hook, 把“Model 層”收縮成狀態(tài)和 service 函數(shù)。
但它帶來的影響,卻是斷層級(jí)別的。
真正“干凈的 React 架構(gòu)”,長什么樣?
例子:業(yè)務(wù)邏輯進(jìn) hook,而不是去 Controller 輪回
// usePricing.js
import { useState, useEffect } from "react";
import { fetchPricing } from "../services/pricingService";
export function usePricing(productId) {
const [price, setPrice] = useState(null);
useEffect(() => {
fetchPricing(productId).then(setPrice);
}, [productId]);
return price;
}這里發(fā)生了幾件重要的事:
- 數(shù)據(jù)獲取邏輯,被封裝在一個(gè)明確的 hook 里;
- 狀態(tài)跟隨組件生命周期,自然更新,不需要額外 Controller 來指揮;
- 業(yè)務(wù)含義很清晰:“給我一個(gè) productId,我給你一個(gè) price”。
組件變得可預(yù)期、可測(cè)試、也變得小而清爽
// ProductCard.jsx
import { usePricing } from "../hooks/usePricing";
export function ProductCard({ id }) {
const price = usePricing(id);
return <p>Price: {price ?? "Loading…"}></p>;
}你會(huì)發(fā)現(xiàn)少了很多東西:
- 沒有 Controller;
- 沒有那種“繞一圈再回來”的分層跳轉(zhuǎn);
- 沒有跨文件亂改狀態(tài)的副作用。
邏輯就待在 React 期望它待的地方。
狀態(tài),就在組件樹里忠實(shí)地流動(dòng)。
React 性能的提升,不靠玄學(xué),只靠“不再和它對(duì)著干”
當(dāng)我們把 MVC 相關(guān)的那堆抽象層拆掉之后, 發(fā)生了幾組非常現(xiàn)實(shí)的數(shù)據(jù)變化:
前:████████████████████(4.3s 首屏加載)
后:█████(1.7s 首屏加載)
前:███████████(每個(gè)頁面大約 78KB 的 JS)
后:████(拆分后每屏約 31KB)
前:狀態(tài)分布在到處亂飛,誰都能改一點(diǎn)
后:狀態(tài)被鎖在清晰的邊界里,誰應(yīng)該改它一目了然
性能的提升,并不是因?yàn)槲覀兺蝗挥昧耸裁措x譜的緩存技巧, 也不是因?yàn)檎{(diào)了一個(gè)神奇的第三方庫。
而是一個(gè)更樸素的真相:
架構(gòu)不再和 React 的渲染機(jī)制造反。
當(dāng)邏輯靠近狀態(tài),當(dāng)職責(zé)對(duì)齊組件樹, React 自己就會(huì)幫你干掉一大半不必要的重渲染。
那些從“親手殺死 React 里的 MVC”里學(xué)到的教訓(xùn)
有些話,只有被生產(chǎn)環(huán)境暴打過的工程師才會(huì)說得這么直白:
- MVC 在白板上很好看,但在以 UI 為中心的系統(tǒng)里,很容易塌。
- React 的架構(gòu),是被“渲染行為”塑造出來的,而不是被理論圖譜塑造的。
- Hooks 不是“工具函數(shù)”,它們就是你的領(lǐng)域?qū)印?/span>
- 組件不是模板,它們是行為契約,是“這塊 UI 到底要干什么”的真相呈現(xiàn)。
邏輯離狀態(tài)越近, BUG 就越難“逃出現(xiàn)場”。
當(dāng)你不用再翻三個(gè)文件找到一個(gè)狀態(tài)更新邏輯, 當(dāng)你看到組件名字就能猜到它內(nèi)部的行為, 這時(shí)候的 React 項(xiàng)目,才算真正進(jìn)入“可維護(hù)”區(qū)間。
2025 年的 React 架構(gòu):要的是“邊界”,不是“疊疊樂式的層”
MVC 想做的,是按文件去“分離關(guān)注點(diǎn)”。
React 真正做的,是按行為去分離關(guān)注點(diǎn)。
這中間的心態(tài)差異,決定了一整個(gè)項(xiàng)目的命運(yùn)。
很多人糾結(jié)在:
“項(xiàng)目結(jié)構(gòu)到底要怎么擺才是最優(yōu)解?”
但走到一定規(guī)模你會(huì)發(fā)現(xiàn):
真正能撐住大型 React 項(xiàng)目的, 不是你有沒有一個(gè)“完美的目錄結(jié)構(gòu)”, 而是你的代碼,到底有沒有誠實(shí)地表達(dá)自己在做什么。
當(dāng)邏輯回到它本來該在的地方, 很多復(fù)雜度, 會(huì)在你不經(jīng)意間自己消失。
最后的現(xiàn)實(shí):React 架構(gòu),已經(jīng)不再是“可選項(xiàng)”
Press enter or click to view image in full size
MVC 死掉,不是因?yàn)?React 某個(gè)版本突然改了什么。
它死掉,是因?yàn)?/span>業(yè)務(wù)現(xiàn)實(shí)先變了:
- 頁面不再是“后端渲染完就扔給你看”的靜態(tài)結(jié)果;
- 前端成了主戰(zhàn)場,狀態(tài)和交互塞滿了整個(gè) UI;
- 架構(gòu)如果還停留在“Controller 調(diào) View、View 調(diào) Service”的年代, 就等于用 Nokia 的時(shí)代思維去寫 iPhone 的 App。
在這樣的現(xiàn)實(shí)里, 現(xiàn)代 React 架構(gòu)所獎(jiǎng)勵(lì)的,已經(jīng)是另一套價(jià)值觀:
- 代碼之間的距離要近——邏輯與狀態(tài)、行為與 UI;
- 模塊之間的邊界要清楚——誰負(fù)責(zé)業(yè)務(wù),誰負(fù)責(zé) IO;
- 團(tuán)隊(duì)之間的責(zé)任要明確——出了問題,一眼看出該找誰。
而那些只剩層、文件夾、和信仰的東西, 撐不了多久。
如果你的代碼,一碰就痛, 那很可能不是你“技術(shù)不夠好”, 而是你堅(jiān)持的那個(gè)模式,在對(duì)你撒謊。
聽一聽它在痛的地方, 很多時(shí)候,答案已經(jīng)寫在 React 的行為里了。


























