八個例子講解現代前端框架前置知識(講義)
這是《JS 深入淺出》(收費課程)第七課的講義,主要講「為什麼會有雙向綁定和單向綁定」,也就是歷史和前端的心路歷程。
JS 深入淺出 - 寫代碼啦!起源
說到前端框架,就總會談論到什麼「雙向綁定」和「單向綁定」這些概念了。
但是要理解這些概覽,最好還是從其最原始的形態入手,也就是自己搞出雙向綁定和單向綁定。
這就要說到 MVC 了。
2010 年,Backbone.js 第一版發布,三年後在騰訊工作的我才開始用這麼個東西。由此可以看出英文世界的前端知識一直都是領先於其他語言的。
有人說 Backbone.js 是基於 MVC 思想的,也有人說 Backbone.js 是基於 MVP 思想的,我不打算給大家明確的答案,因為不管是 MVC 還是 MVP,都是類似的。今天我們從需求的角度來理解 MVX(X 可以是任何東西)思路。
義大利麵條式的代碼
使用框架的人總會說不使用框架的人寫的是「義大利麵條」
因為這些代碼長長短短,還互相交織,你中有我,我中有你。
雖然乍看起來這種代碼沒有問題,但是時間久了之後,這種代碼極難維護。
例子1:https://jsbin.com/noraye/8/edit?html,js,output
一些程序員想出了解決辦法
一些程序員通過自己的總結,發現這些代碼總是可以分成三類:
- 專門操作遠程數據的代碼(fetchDb 和 saveDb 等等)
- 專門呈現頁面元素的代碼(innerHTML 等等)
- 其他控制邏輯的代碼(點擊某按鈕之後做啥的代碼)
為什麼分成這三類呢?因為我們前端抄襲了後端的分類思想,後端代碼也經常分為三類:
- 專門操作 MySQL 資料庫的代碼
- 專門渲染 HTML 的代碼
- 其他控制邏輯的代碼(用戶請求首頁之後去讀資料庫,然後渲染 HTML 作為響應等等)
這些思路經過慢慢的演化,最終被廣大程序員完善為 MVC 思想。
- M 專門負責數據
- V 專門負責表現
- C 負責其他邏輯
如果我們來反思一下,會發現這個分類是無懈可擊的:
- 每個網頁都有數據
- 每個網頁都有表現(具體為 HTML)
- 每個網頁都有其他邏輯
於是乎,MVC 成了經久不衰的設計模式(設計模式就是「套路」的意思)
現在我們來改寫一下例子1。
例子2:https://jsbin.com/yuwopuf/3/edit?js,output
改進了以下幾點:
- 把義大利麵條變成三塊有結構有組織的對象:model、view 和 controller
- model 只負責存儲數據、請求數據、更新數據
- view 只負責渲染 HTML(可接受一個 data 來定製數據)
- controller 負責調度 model 和 view
模板代碼(也就是類)
一個頁面或模塊只需要 model view controller 三個對象
第二個頁面就需要再來 model2 view2 controller2 三個對象第三個頁面就需要再來 model3 view3 controller3 三個對象……第N個頁面就需要再來 modelN viewN controllerN 三個對象你每次寫一個 model 都要寫很類似的代碼
你每次寫一個 view 都要寫很類似的代碼
你每次寫一個 controller 都要寫很類似的代碼為什麼不利用模板代碼(俗稱面向對象)把重複的代碼寫到一個類呢(JS裡面就是把「共有屬性」放到原型里)
代碼如下
例子3:https://jsbin.com/sodojac/5/edit?js,output
煩人的地方
一般來說,如果代碼有重複或類似,就能優化(也可以不優化)。
例3的代碼有這樣的重複代碼,我們一個一個來解決。
第一個煩人的地方:每次用 model 獲取數據之後,還要「手動」調用 this.view.render(this.model.data),你看代碼中有四處手動調用了 updateModel。
怎麼解決呢?一個方案是給 Model 加上事件機制。
優化後的代碼如下
例子4:https://jsbin.com/sodojac/10/edit?js,output
第二個煩人的地方有個 BUG:
每次 render 都會更新 #app 的 innerHTML,這可能會丟失用戶的寫在頁面某個 input 裡面的數據。(上課示例)
這有兩個解決辦法:
2.1. 用戶只要輸入了什麼,就記錄在 JS 的 data 里。(數據綁定的初步思想出現了)
2.2. 不要粗暴的操作 innerHTML,而是只更新需要更新的部位(虛擬 DOM 的初步思想出現了)Angular 就是基於第一個思想而發明的,而 React 則是基於第二個思想。
有些人還覺得有第三個煩人的地方:
events 能不能直接寫到 HTML 上面,而不是寫到 JS 里。(上課示例)
確實這樣寫代碼更直觀,所以 Angular 和 React 都採納了這一想法。但是又一波人不喜歡這種寫法,他們認為 HTML 和 JS 應該是分離的:
我們花了好幾年才普及內容與行為分離的觀點,沒想到 Angular 和 React 一下子就回到解放前了。
這種爭論到 2017 年才漸漸休止,大家逐漸都接受了直接在 HTML 上綁定 JS 事件的寫法。
接下來我們介紹 Angular 對於 2.1 問題的解法。
Angular 與 Vue 的雙向綁定
由於 Angular 太複雜(概念),很多人表示學不會,這時出現了一個簡化版的 Angular —— Vue 0.8。
當時的 Vue 主要借鑒了 Angular 的雙向綁定思想,所以我們用 Vue 來舉例更好理解,這樣我就不用花一個小時來向你介紹 Angular 了。
例子5:https://jsbin.com/vixoku/4/edit?js,output
Vue 代替了 View,這就是 Vue 的名字和其讀音的來歷。
Vue 的雙向綁定(也是 Angular 的雙向綁定)有這些功能:
- 只要 JS 改變了 view.number 或 view.name 或 view.n (注意 Vue 把 data 裡面的 number、name 和 n 放到了 view 上面,沒有 view.data 這個東西), HTML 就會局部更新
- 只要用戶在 input 里輸入了值,JS 里的 view.n 就會更新。
這就像雙向綁定:JS 數據與頁面元素互相綁定。
同時 Vue 也實現了局部更新(2.2)。
Vue 還有很多其他功能,使得 Controller 顯得多餘:
例子6:https://jsbin.com/ruzikax/2/edit?js,output
然後 model.data 也顯得多餘:
例子7:https://jsbin.com/cuhurit/5/edit?js,output
可以看到,事情變得「容易」了很多。
React —— 單向綁定
雙向綁定看起來很 magic(魔法),但是有些人覺得單向更好:
例子8:https://jsbin.com/fodasut/3/edit?html,js,output
我們來想想為什麼雙向綁定不好呢?
因為「雙向綁定」有一點點反「組件化」:跨組件的雙向綁定很奇怪。
但是局部使用雙向綁定是非常爽的。
單向綁定的要點
- 單向
- VirtualDOM
雙向綁定的要點
- 實現方式
- Dirty Checking(AngularJS 1.x)的方式
- Reactive
- 使用 getter setter,缺點是無法監聽不存在的屬性
- 使用 Proxy
推薦閱讀: