用 React/Vue 不如用 JQuery,你知道嗎?
事情的起因是這樣的,有一個粉絲朋友跟我述說了她的焦慮:都 2024 年了,她的團(tuán)隊還在用 jQuery 開發(fā)項目,她覺得自己距離 React、距離 Vue 好遙遠(yuǎn)。覺得自己是被時代拋棄的棄子,她目前的狀態(tài)就是每天都活在極度的焦慮當(dāng)中,每次聽到有人說行情不好,或者哪哪家公司又在裁員,都感覺心驚肉跳。
為了從根上解決她的焦慮,于是就有了這個標(biāo)題。我知道看到這個標(biāo)題,很多人第一反應(yīng)是不理解,jQuery 不就是遠(yuǎn)古時代的產(chǎn)物嗎,不都已經(jīng)被淘汰了嗎?它能比 React/Vue 更好?這不會是一篇標(biāo)題黨的文章吧?
但,我要非常明確的是,這不是標(biāo)題黨,而是在說一個客觀事實。接下來,我來給大家分析一下,為什么 jQuery 比 React/Vue 更好。
一、技術(shù)選型的核心目的
作為一個前端開發(fā),在搞技術(shù)選型的時候,一定一定要記住,我們有兩個核心目標(biāo),一個是能夠更高效更舒適的開發(fā)項目,而另外一個,就是要在開發(fā)項目的過程中,凝聚出屬于你自己的核心競爭力。第一個目標(biāo)更多的是偏向于團(tuán)隊,第二個目標(biāo)更多的是要偏向于個人,你想要有一個持續(xù)的,健康的職業(yè)生涯,這兩者缺一不可。只是說,第二個目標(biāo)只能憋在心里,不能廣而告之。
然而事實上,許多人,不管是主動選擇,還是被動選擇,往往只關(guān)注了第一個目標(biāo),而忽略了第二個目標(biāo)。有的人是憋著不說,有的人是壓根想不到。這就導(dǎo)致了,這些想不到的很多人,用 Vue/React 用久了,會感覺自己變成了一個廢物,演變成一年經(jīng)驗用七年。
這種情況在 Vue 使用者的身上會體現(xiàn)得更加明顯。
當(dāng)然,我不是在說所有人,而是大多數(shù)人
所以有的人雖然沒有剛才那個同學(xué)那種焦慮情緒,也很熟練的在使用 React/Vue,但就是想要獲得一個 offer 還是非常困難。因為他們雖然已經(jīng)熟練使用 React/Vue 開發(fā)頁面,但這就是一個普通的頁面仔啊,工作了 five 年,沒有凝聚出核心競爭力,成了一個廢物。
因此在做技術(shù)選型的考慮上,在我的選擇序列里,React 永遠(yuǎn)都要比 Vue 更值得選擇,只因為 React 離原生 JavaScript 更近,沒有創(chuàng)造更多的語法,沒有那么多黑箱操作,自由度更高。這讓我有更多的機(jī)會在開發(fā)項目的過程中,做到提高開發(fā)效率的同時,還能兼顧自己核心競爭力的提升。
而在這兩個點(diǎn)的權(quán)衡上,jQuery 實際上可以做得更好。
二、我們陷入了 React/Vue 的騙局
React/Vue 最核心的東西,就是組件化。能借助 webpack/vite,在 JS 模塊中,把其他靜態(tài)資源當(dāng)成模塊引入,從而在大型項目中,提高文件結(jié)構(gòu)的可讀性和易用性。并利用數(shù)據(jù)驅(qū)動 UI 的方式,在一定程度上簡化了開發(fā)負(fù)擔(dān)。
但是,我們在學(xué)習(xí) React/Vue 時,都被他們騙了。React/Vue 說,我們要構(gòu)建一個大型項目,需要一個全局狀態(tài)管理器,我們應(yīng)該把所有的狀態(tài)都放到頂層的 store 里。于是 redux 成為了入門 React 必學(xué)的技術(shù)棧。然而事實上,全局狀態(tài)管理,我覺得是一種弱智的,簡單粗暴的解決方案,他能夠讓你在思考組件拆分的時候,完全不考慮數(shù)據(jù)的歸屬問題,反正隨便放在哪里都能輕松訪問。
以致于,大部分的前端開發(fā),都是被這種騙局培養(yǎng)成了高效低能的開發(fā)者,不管你是用 React,還是用 Vue,有可能都沒有逃過這個騙局。當(dāng)你還在和別人爭論 React、Vue 誰會淘汰誰的問題時,你可能還沒有發(fā)現(xiàn),這兩個家伙構(gòu)建了一個非常堅固的信息繭房,把所有的前端都圈在里面,然后合伙把你淘汰掉。
所以我認(rèn)真的思考了一下,真的有很多數(shù)據(jù)需要全局共享嗎?
所以在很多年前,當(dāng)我經(jīng)驗逐漸豐富起來的時候,我在其他客戶端開發(fā)解決方案中,見識了更多的開發(fā)模式,然后我發(fā)現(xiàn)了這個騙局。我們大多數(shù)項目,并不需要全局狀態(tài)管理。甚至也不需要邏輯那么笨重的數(shù)據(jù)驅(qū)動。
三、數(shù)據(jù)驅(qū)動的本質(zhì)
當(dāng)全局狀態(tài)管理沒那么有必要的時候,也就意味著,我們的項目數(shù)據(jù)結(jié)構(gòu)不會那么復(fù)雜,所以數(shù)據(jù)驅(qū)動 UI 這個事情,帶來的好處,就顯得非常有限了。
在 React 中,你這樣寫:
function Hello() {
const [year, setYear] = useState(2024)
function clickHandler() {
setYear(year + 1)
}
return (
<div>
<div>hello {year}</div>
<button onClick={clickHandler}>++</button>
</div>
)
}
當(dāng)我們利用 jQuery 如何寫呢,看一下代碼:
<script id="temp" type="text/x-jsrender">
<div>hello {{:year}}</div>
<button id="increament" onclick="hello()">++</button>
</script>
var data = {
year: 2014
};
function hello() {
data.year += 1
template.link("#result", data);
}
var template = $.templates("#temp");
template.link("#result", data);
注意看,我們其實也可以利用了 jQuery 生態(tài)中的模板語言來代替 DOM 操作,達(dá)到類似的目的。我們可以相對清晰的知道當(dāng)我要改變一個數(shù)據(jù)時,有兩個事情要完成,一個是改變數(shù)據(jù),一個是重新修改 UI.
我們也可以縮小修改的范圍,從而達(dá)到最極限的性能,自由度非常高。
function hello() {
data.year += 1
$('#label').text(`hello ${data.year}`)
}
在復(fù)雜度和性能之間,我們可以自由的做出取舍。我們完全沒有必要在所有場景,都去花費(fèi)那么大的代價去考慮如何將數(shù)據(jù)與 UI 綁定在一起。不管是 Vue 的依賴收集,還是 React 的 diff,在這上面都花費(fèi)了大量的心思,增加了巨大的心智負(fù)擔(dān),關(guān)鍵邏輯還變成了黑箱操作。
四、React 的 返祖現(xiàn)象
事實上,熟悉 React 新官網(wǎng)的朋友應(yīng)該知道,React 已經(jīng)開始出現(xiàn)返祖現(xiàn)象了。也就是官方文檔把 useEffect 定性為一種逃脫方案。當(dāng)我們發(fā)生點(diǎn)擊事件時,如果需要修改其他的邏輯,新官方文檔建議我們不要去修改狀態(tài),而是直接把邏輯寫在回調(diào)函數(shù)里
// 官方文檔不推薦
useEffect(() => {
loading && api().then(() => {})
}, [loading])
function clickHandler() {
setLoading(true)
}
// 官方文檔推薦
function clickHandler() {
setLoading(true)
api().then(() => {})
}
我為什么要稱這種方式為返祖現(xiàn)象呢,因為你熟悉 jQuery 的使用的話,你就會發(fā)現(xiàn)這本身是再正常不過的邏輯了,但是新的官方文檔確要花費(fèi)大量的篇幅去解釋為什么應(yīng)該這樣做。這在我看來是非常詭異的事情。
然后呢,我又要花大量的心思去解釋我為什么不認(rèn)同官方文檔的這種觀點(diǎn)。
五、當(dāng)我們在 jQuery 中能自定義組件時
我們要達(dá)成的一個共識就是,單向數(shù)據(jù)流是一個被包裝出來的高大上概念。說白了就是函數(shù)的嵌套執(zhí)行。
注意看這段代碼:
function Parent(data) {
const {pname, ...otherName} = data
return (
`<div>${pname}</div>${Children(otherName)}`
)
}
function Children(name) {
const {cname, ...otherName} = name
return (
`<div>${cname}</div>${Grandson(otherName)}`
)
}
function Grandson(name) {
return (
`<div>${name.gname}</div>`
)
}
是不是感覺很熟悉,很像 React。當(dāng)我執(zhí)行 Parent() 的時候,所有的子元素和孫子元素都會重新執(zhí)行,從而數(shù)據(jù)就有了流向。當(dāng)我要修改數(shù)據(jù)的時候只需要。
function click() {
data.gname = 'TOM'
parent(data)
}
這就是數(shù)據(jù)驅(qū)動 UI。這就是單向數(shù)據(jù)流。
但是實際上,這不是 React 組件,他只是普通的函數(shù),返回了一個字符串而已。因此,要非常注意的是,他也滿足了單向數(shù)據(jù)流的規(guī)則。但是他的寫法也只是函數(shù)里執(zhí)行函數(shù)。
如果你要改變 data 時,只需要重新執(zhí)行一下 Parent(data) 。這樣,我們的每一個子組件,都會重新執(zhí)行。所以我想說的是,構(gòu)建一個自定義組件確實太簡單了,我們當(dāng)然也可以在 jQuery 的生態(tài)里,基于模板自定義組件。
function renderBoldP(value) {
return "<p><b>" + value + "</b></p>";
}
$.views.tags("boldp", renderBoldP);
React 的語法跟我剛才的寫法非常相似。但是,React 最大的問題就是,嵌套層級太多了,以致于我們在執(zhí)行頂層組件 Parent()
時,成本偏高。在 jQuery 中,就可以完全不用擔(dān)心這個問題,我們可以自由選擇層級,而不必把嵌套層級擴(kuò)大到整個項目。有可能你只是想要修改一個小小的地方而已。
靈活,就是 jQuery 最大的優(yōu)勢。
事實上,當(dāng)你要研發(fā)大型高性能的前端項目時,React 和 jQuery 最終都會殊途同歸。我們也會想辦法在 React 中放棄自頂向下的 diff,然后把改動縮小在可控的范圍里。但是在 React 中要做到這個事情需要非常深厚的功底,而在 jQuery 中卻非常容易。因為我們并不需要去遷就龐雜的 diff 流程,只是簡單的執(zhí)行一個目標(biāo)函數(shù)而已。
六、當(dāng)你需要雙向綁定時
當(dāng)你想要在特定的場景里需要雙向綁定時,jQuery 的生態(tài)里也有非常多的方案來支撐這個場景。
$("#changeObjects").on("click", function() {
$.observable(person).setProperty({
name: "newName",
address: {street: "New Street"},
phones: [{number: "123 123 1234"}, {number: "321 321 4321"}]
});
});
所以,當(dāng) jQuery 可以自定義組件,可以支持單向數(shù)據(jù)流,可以支持雙向綁定,這不就齊活了嗎?
七、趨勢是什么
不要問未來的趨勢是什么,問就是 jQuery。什么所謂的 Vue3,Solid,svelte,都不是最終形態(tài),他們通通都在走向返祖的道路,未來的趨勢就是 jQuery。所以如果你的團(tuán)隊里,還在使用 jQuery,正說明你們團(tuán)隊在領(lǐng)先世界,這是我內(nèi)心最真實的想法。所以你不需要過于焦慮,你要做的事情只是把 jQuery 用好,用透,去利用 jQuery 的生態(tài)構(gòu)建一套開發(fā)效率很高的架構(gòu)出來,然后回過頭來,你會發(fā)現(xiàn),React/Vue 你只需要一天就能學(xué)會。
沒有一個團(tuán)隊,會拒絕得了精通 jQuery 的人。因為你 jQuery 用得好,很大程度上能代表你原生能力相對會強(qiáng)一些,基礎(chǔ)非常扎實。