八個例子講解現代前端框架前置知識(講義)

這是《JS 深入淺出》(收費課程)第七課的講義,主要講「為什麼會有雙向綁定和單向綁定」,也就是歷史和前端的心路歷程。

JS 深入淺出 - 寫代碼啦!xiedaimala.com


起源

說到前端框架,就總會談論到什麼「雙向綁定」和「單向綁定」這些概念了。

但是要理解這些概覽,最好還是從其最原始的形態入手,也就是自己搞出雙向綁定和單向綁定。

這就要說到 MVC 了。

2010 年,Backbone.js 第一版發布,三年後在騰訊工作的我才開始用這麼個東西。由此可以看出英文世界的前端知識一直都是領先於其他語言的。

有人說 Backbone.js 是基於 MVC 思想的,也有人說 Backbone.js 是基於 MVP 思想的,我不打算給大家明確的答案,因為不管是 MVC 還是 MVP,都是類似的。今天我們從需求的角度來理解 MVX(X 可以是任何東西)思路。

義大利麵條式的代碼

使用框架的人總會說不使用框架的人寫的是「義大利麵條」

因為這些代碼長長短短,還互相交織,你中有我,我中有你。

雖然乍看起來這種代碼沒有問題,但是時間久了之後,這種代碼極難維護。

例子1:jsbin.com/noraye/8/edit?

一些程序員想出了解決辦法

一些程序員通過自己的總結,發現這些代碼總是可以分成三類:

  1. 專門操作遠程數據的代碼(fetchDb 和 saveDb 等等)
  2. 專門呈現頁面元素的代碼(innerHTML 等等)
  3. 其他控制邏輯的代碼(點擊某按鈕之後做啥的代碼)

為什麼分成這三類呢?因為我們前端抄襲了後端的分類思想,後端代碼也經常分為三類:

  1. 專門操作 MySQL 資料庫的代碼
  2. 專門渲染 HTML 的代碼
  3. 其他控制邏輯的代碼(用戶請求首頁之後去讀資料庫,然後渲染 HTML 作為響應等等)

這些思路經過慢慢的演化,最終被廣大程序員完善為 MVC 思想。

  1. M 專門負責數據
  2. V 專門負責表現
  3. C 負責其他邏輯

如果我們來反思一下,會發現這個分類是無懈可擊的:

  1. 每個網頁都有數據
  2. 每個網頁都有表現(具體為 HTML)
  3. 每個網頁都有其他邏輯

於是乎,MVC 成了經久不衰的設計模式(設計模式就是「套路」的意思)

現在我們來改寫一下例子1。

例子2:jsbin.com/yuwopuf/3/edi

改進了以下幾點:

  1. 把義大利麵條變成三塊有結構有組織的對象:model、view 和 controller
  2. model 只負責存儲數據、請求數據、更新數據
  3. view 只負責渲染 HTML(可接受一個 data 來定製數據)
  4. controller 負責調度 model 和 view

模板代碼(也就是類)

一個頁面或模塊只需要 model view controller 三個對象

第二個頁面就需要再來 model2 view2 controller2 三個對象

第三個頁面就需要再來 model3 view3 controller3 三個對象

……

第N個頁面就需要再來 modelN viewN controllerN 三個對象

你每次寫一個 model 都要寫很類似的代碼

你每次寫一個 view 都要寫很類似的代碼

你每次寫一個 controller 都要寫很類似的代碼

為什麼不利用模板代碼(俗稱面向對象)把重複的代碼寫到一個類呢(JS裡面就是把「共有屬性」放到原型里)

代碼如下

例子3:jsbin.com/sodojac/5/edi

煩人的地方

一般來說,如果代碼有重複或類似,就能優化(也可以不優化)。

例3的代碼有這樣的重複代碼,我們一個一個來解決。

第一個煩人的地方:每次用 model 獲取數據之後,還要「手動」調用 this.view.render(this.model.data),你看代碼中有四處手動調用了 updateModel。

怎麼解決呢?一個方案是給 Model 加上事件機制。

優化後的代碼如下

例子4:jsbin.com/sodojac/10/ed

第二個煩人的地方有個 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:jsbin.com/vixoku/4/edit?

Vue 代替了 View,這就是 Vue 的名字和其讀音的來歷。

Vue 的雙向綁定(也是 Angular 的雙向綁定)有這些功能:

  1. 只要 JS 改變了 view.number 或 view.name 或 view.n (注意 Vue 把 data 裡面的 number、name 和 n 放到了 view 上面,沒有 view.data 這個東西), HTML 就會局部更新
  2. 只要用戶在 input 里輸入了值,JS 里的 view.n 就會更新。

這就像雙向綁定:JS 數據與頁面元素互相綁定。

同時 Vue 也實現了局部更新(2.2)。

Vue 還有很多其他功能,使得 Controller 顯得多餘:

例子6:jsbin.com/ruzikax/2/edi

然後 model.data 也顯得多餘:

例子7:jsbin.com/cuhurit/5/edi

可以看到,事情變得「容易」了很多。

React —— 單向綁定

雙向綁定看起來很 magic(魔法),但是有些人覺得單向更好:

例子8:jsbin.com/fodasut/3/edi

我們來想想為什麼雙向綁定不好呢?

因為「雙向綁定」有一點點反「組件化」:跨組件的雙向綁定很奇怪。

但是局部使用雙向綁定是非常爽的。

單向綁定的要點

  1. 單向
  2. VirtualDOM

雙向綁定的要點

  1. 實現方式
    1. Dirty Checking(AngularJS 1.x)的方式
    2. Reactive
      1. 使用 getter setter,缺點是無法監聽不存在的屬性
      2. 使用 Proxy

推薦閱讀:

前端數據流哲學

TAG:前端工程師 | Vuejs | React |