點燈坊

失くすものさえない今が強くなるチャンスよ

Vue 的 Functional 架構

Sam Xiao's Avatar 2020-02-29

Vue 2 雖然不算是 FP 架構,但透過不一樣寫法,仍可使 Vue 2 接近 FP 讓 Project 更易維護。

Version

Vue 2.6.11
Vuex 3.1.2
Ramda 0.27.0

Challenge of Web

Backend MVC

約在 2015 年,主流的 Web 開發架構仍以後端 MVC 為主:

  1. PHP / Laravel
  2. Ruby / Ruby on Rails
  3. C# / .NET MVC
  4. Python / Django

這類都是 後端 以 MVC 為主,前端 以 jQuery / Ajax 為輔的架構。

這種架構有幾個挑戰:

  1. 有於重點在後端,時常必須換頁到後端去,使用者會明顯感受到網頁重新載入,視覺與使用感受較差
  2. 若想減少換頁到後端,就必須大量使用 jQuery / Ajax,則前端就會有大量 ECMAScript
  3. 由於 ECMAScript 缺乏 module 概念,實務上常常會看到上千行的 jQuery / ECMAScript,導致維護困難
  4. 由於 jQuery / ECMAScript 是躲在後端網頁內,因此大部分的 jQuery / ECMAScript 都是由後端工程師撰寫,但由於 ECMAScript 語言特性與後端語言明顯的差異,大部分後端工程師選擇逃避原生 ECMAScript,而改用較容易的 jQuery,此時都是 Web 工程師都是 全端工程師
  5. 也由於使用 jQuery,導致大部分 全端工程師 並沒有深入研究 ECMAScript,而是認為前端只是控制 HTML / DOM 而已,jQuery 足已,因此沒必要深入研究 ECMAScript,導致一些 ECMAScript 很重要的語言特性被忽略 (first-class function, higher order function、closure、IIFE、function composition …),其實這些 FP 特性,才是 ECMAScript 精華所在

Separation of Frontend and Backend

2015 年開始有一些前端 framework 嶄露頭角,如 AngularJS、React:

  1. 後端只寫到 API,以 JSON 與前端溝通
  2. 顯示邏輯 寫在前端,而不是以 presenter pattern 寫在後端,因此沒有網頁重新載入,視覺與使用感受佳
  3. 前端開始有 component 概念,也開始使用 ES6 的 module 概念,因此不再出現上千行 JavaScript,可重構成 component 與 module,更容易維護
  4. 由於使用 ES6 與原本 ES5 相比,幾乎是全新語言,有學習門檻
  5. 前端技術發展迅速,開始有
    • Node 生態圈 (NPM、Yarn)
    • ECMAScript 每年會更新一版 (2015 ~ 2020)
    • Babel / Webpack 編譯打包技術 + ESLint (即使 JavaScript 不編譯,亦可享有編譯的優點)
    • 層出不窮的 library
    • 層出不窮的 framework (Angular、React、Vue)
    • 由於前端技術爆炸,已經不再只有 jQuery,後端也要精通前端已經有難度,因此前後端開始分家,開始有所謂 前端工程師

Evolution of Frontend

相較於 jQuery、前端開始有了新的概念:

  1. Data Binding:傳統 jQuery 直接控制 DOM;現在前端改為控制 data,現在由 framework 處理 data 與 DOM 的改變
  2. Functional Programming:由於 ECMAScript 深具 Functional 語言特性 (以 Scheme 為原型),原本 OOP 的 MVC 架構蛻變為 FP 的 Pipeline (單向資料流) 架構 (React 的 Redux、Vue 的 Vuex)
  3. Webpack Tree-shaking:由於 OOP 大量使用 field,導致 tree-shaking 無法有效發揮,前端開始使用 FP 方式,以 function 取代 class,並使用 FP 的 function composition 取代 OOP 的 DI (Ramda)

可以發現前端的改變:

  1. 從控制 DOM 轉為控制 data,回歸程式設計的本質
  2. 由 Object Oriented Programming 轉為 Functional Programming

Vue

Vue 吸取 React 與 Angular 的優點:

  1. 從 Angular 學到學習門檻較低的 HTML Template,而非 React 的 JSX
  2. 從 Angular 學到 directive 控制 HTML,而非 React 直接以 ECMAScript 夾雜 HTML
  3. 從 React 學到 component 概念,使得 顯示邏輯 得以模組化
  4. 從 React 學到 狀態管理 概念 (Redux) ,讓跨 component 的溝通更加簡單 (Vuex)
  5. Vue 前期學 Angular 較多,後期則明顯向 React 靠攏

儘管如此,Vue 也有自己的缺點:

  1. Vue 的 datacomputedpropsmethod … 都必須靠 this 存取,這是一種具有 side effect 的寫法,且由於 this 的可變性,導致 Vue 重構困難
  2. Vue 寫法多元,最原始寫法是一種依賴 this,很像 OOP 又不是 OOP 寫法,後來又開發出 class component,這是正統 OOP,風格很像 Angular,但也由於太偏 OOP,導致大部分人寫 Vue 都使用 OOP 思維,而忘記 ECMAScript 最重要的 Functional 特性 (first-class function、higher order function、closure、IIFE、function composition …)
  3. computedmethod 寫在 Vue instance 內,導致縮排過多

Functional Vue

  • Vue 的 datacomputedpropsmethod 本質都是 function,不要將這些寫在 Vue instance 內,而將所有的 function 拉出來,也由於看到的是一條條 function,不是類似 OOP 的 method,有助於發現重複的部分以 higher order function 重構
  • 除了 HTML data binding 的資料放在 data 外,不要把 data 當成 OOP 的 field 使用,如此可避免使用 side effect 寫法,而改用 pure function 風格
  • 將資料從 data 搬到 Vuex 的 state,由於 stategettermutationaction 不使用 this,Vue 可徹底從 ES5 的 function expression 解放,全面改用 ES6 的 arrow function,不僅更簡潔,也更適合 higher order function 實現
  • 也由於 Vuex 的 computedmethod 本質上都是 function,可以很容易抽成 higher order function,也可以整合 Ramda 這類 functional library,透過 function composition 產生 computedmethod
  • 也由於 商業邏輯顯示邏輯 都集中在 Vuex 的 store,因此各 component 很容易重複使用這些邏輯,且只要 stategetter 有變動,其他 component 資料都會跟著變動
  • 由於 邏輯 都在 Vuex 的 store,unit test 可集中火力放在 store 即可
  • 也由於所有的 function 都沒有 this,都是 pure function 沒有 side effect,因此很容易重構到適當 module,不必被 Vue 的 componentmixin 所綁架

Conclusion

  • Functional Vue = Vue Component + Vuex + Ramda + ES6 Module