寫在前面:本文談論的是主觀的個人感受,不追求立場的“客觀、公正”,因此我下麵所說的很可能是錯的,歡迎交流指正。 我學習前端時,跟大部分beginner一樣,學的第一個框架是Vue,入職後也一直寫Vue。寫了一年多,想換換口味了,於是決定最近的新項目改用React試試。用了大半個月後,我已經完全被這個 ...
寫在前面:本文談論的是主觀的個人感受,不追求立場的“客觀、公正”,因此我下麵所說的很可能是錯的,歡迎交流指正。
我學習前端時,跟大部分beginner一樣,學的第一個框架是Vue,入職後也一直寫Vue。寫了一年多,想換換口味了,於是決定最近的新項目改用React試試。用了大半個月後,我已經完全被這個迷人的框架洗腦了,特別是它背後的設計理念非常優秀,讓人耳目一新。
React的設計思想有何過人之處呢?拿它和Vue做個對比就清楚了。
Vue: 填模板游戲繼續
Vue之所以成為國內前端初學者(包括我)的首選,一方面是因為中文文檔寫得好,更重要的原因則是它符合人們對於網頁開發的傳統認知。
Vue雖然內部做了很多工作(如Virtual DOM),但暴露給開發者的體驗更像是升級版的模板引擎,類似於Jade、Handlerbars。模板引擎的作用就是在靜態的模板上動態替換JS變數,以渲染出最終的頁面。基於Vue的項目目錄下,每個組件目錄下都有html、css、js文件三足鼎立(或者集中到一個.vue文件里,但仍然是分開的三部分),html是靜態模板,js管理動態的變數,整體結構非常的直觀。
此外,Vue在模板引擎的基礎上增加了“雙向綁定”功能,即用戶輸入造成的DOM變動可以反過來同步給關聯的JS變數;並且自定義了一些v指令,大大簡化了事件綁定、條件渲染、動態列表等常用功能的實現複雜度。這些因素使得Vue寫起來既像模板引擎一樣易於理解,又比模板引擎方便很多。我個人寫Vue的時候,一般就是先寫html模板,在此過程中把需要動態填充的數據定義出來,隨手寫個mock數據先占位;等模板寫完了,再來寫獲取這些數據的js邏輯。
也就是說,Vue的開發思路差不多就是“寫模板-填模板”的套路,與jQuery時代一脈相承。
這種套路上手容易,但是沒什麼發揮空間,寫多了真的容易膩煩。而且在JS邏輯越來越複雜的情況下,狀態管理非常麻煩,心智負擔沉重。
騷年,要不要換一種玩法試試?
React: 一種新的玩法
剛接觸React的時候,是不是被嵌在js里的html(JSX)、滿天飛的箭頭函數、高階函數等弄得有點不適應?反正我是腦闊疼了好一陣。
但是進一步瞭解React後就會明白,這不是臉書的攻城獅們在耍帥或者故弄玄虛,而是這些語法真的能夠更好地表達React的思想,幫助代碼作者和讀者thinking in React。
React的核心思想是什麼呢?不妨來看看Vue和React的組件對比。這是一個非常簡單的組件——展示從父組件收到的message字元串:
Vue的實現:
// demo.vue <template> <div>{{message}}</div> </template> <script> export default = { props: ['message'] } </script>
React的實現:
// demo.jsx export default props => ( <div>{props.message}</div> );
這兩段代碼一眼就能一個非常明顯的不同點——Vue組件實現為一個有模板(template)、有數據(props)的對象, React組件則實現為一個輸入數據(props)、輸出html片段(JSX)的函數。
函數有什麼特別呢?從數學上講,函數本質上就是表示一種映射關係。React受函數式編程思想的影響,將html視為數據映射的結果。一個數據映射出一個html片段,所有的html片段拼起來,就形成了完整的頁面DOM樹。當然,React組件還可以是class形式,但只是為了更好地操作數據,最終render函數會完成映射這一步。這是一種截然不同於模板填充的思維方式,姑且稱之為“映射思維”。相應的,將變數綁定到模板上的思維方式就稱之為“模板思維”。理解這兩種思維的對立非常重要,因為這是React與Vue等其他框架的核心差異所在。
我畫瞭如下兩張圖,嘗試闡明模板思維與映射思維的不同:
這兩種思維的根本分歧在於:視圖層與數據層何者第一,何者第二?或者說,是先有html再有js,還是先有js再有html?
模板思維認為是先有html,再有js。正如上面圖的左半部分所示,模板本身已經是一個接近完整的網頁了,只不過其中幾個變數沒法確定,需要一點邏輯來動態填充,所以找了js這麼個小弟來打打下手,把這幾個變數給補上去。因此,在模板思維的世界觀里,html居於主導地位,js服從於html的需要。
映射思維卻相反,認為js里的數據才是第一性的,html只不過是數據層向視圖層的單向映射或者說投影,如上圖右半部分所示,一個個數據的投影片段拼起來,組成了完整的html。在這個世界觀里,數據是本體,視圖是現象,他們之間的關係就像一棵樹與它的影子一樣,要想改變影子,就必須改變樹,而不能影子變了樹沒變。明白了這一點,React那些乍看之下有些奇怪的設定(如阻止input標簽直接響應的用戶輸入),以及React社區對單向數據流的偏好(如Redux),就不難理解了。
孰優孰劣?
我可不想像很多文章那樣偷懶地來一句“各有優劣”,再不痛不癢地分別列舉幾個優缺點完事。我,旗幟鮮明地認為,映射思維才是前端未來正確的發展方向。
在上古時代的網頁里,js只是嵌在html里的一行或者一小段腳本,乾著彈個提示、飛個廣告之類的雜活,遠遠稱不上一個完整的程式,說它是html的小弟或補充,一點問題也沒有。但如今,頁面越來越複雜,非同步載入、動態渲染的開發模式越來越流行,在這種模式下,整個頁面都是由js繪製出來的,正是因為js對頁面有了完全的掌控權,才使得網頁真正進化為“WebApp”,成為一個真正的程式,而不再是一個簡單的、無邏輯的類XML標記語言文件,進而使得數據-視圖層的分離及其映射關係的建立成為可能。
在WebApp里,有一個原則顯然應該被始終遵守——一致性,即用戶看到的DOM元素狀態應該與對應的js變數值保持一致。Vue的解決方案是雙向綁定,React則實現了單向數據流,這都是從它們各自的世界觀出發做出的自然而然的選擇。但在我看來,視圖=表象,不應該讓表象承載實質,否則會導致形實關係的混亂——自然法則允許通過修剪樹來改變它的影子,難道也要允許通過改變影子反過來改變樹嗎?表象就應該是實質的忠實呈現,以及用戶觸達實質的媒介(事件綁定),不應越俎代庖。
在模板思維看來,html模板和js數據都是實質,二者是併列的關係;在映射思維的世界觀里,js數據是唯一的實質。
“如無必要,勿增實體”,我選擇簡單的那個方案。
事實也是如此,學會了thinking in React,可以在編寫複雜頁面的時候,仍然保持邏輯清晰和極度舒適。
真香!!!