打開任何一間求職網站,搜尋前端相關職缺,幾乎每間公司的職缺都會在求職條件或加分項目中提到目前的三大框架:React、Vue、Angular。

到底為什麼好像全世界的前端都在用「框架(Framework)」?

沒有框架的日子#

先回想一下在沒有框架的時候,開發者的日常會長什麼樣子。

那時最流行的「套件」非 jQuery 莫屬,語法簡潔、直覺好用的元素選取器,便捷的事件監聽註冊,豐富的開發者生態系產出大量的開源套件,以及最重要的,弭平瀏覽器 XHR 差異的 ajax 函式,將複雜的瀏覽器差異藏在套件中,讓開發者只需要專注在想實作的邏輯即可。

透過元素選取器與事件監聽,開發者可依使用者行為靈活操作 DOM 結構與樣式。但隨著專案規模擴大、複雜度上升,直接操作 DOM 的缺點也浮上檯面。

難以維護#

HTML、CSS、JavaScript 無法維持原先的各司其職,因為需要透過 JavaScript 處理互動內容,勢必要將結構、樣式寫到 JavaScript 的部份中,造成架構耦合度提高,程式碼管理困難。

效能低落#

當 DOM 被改變,勢必要觸發整個 Reflow 與 Repaint 的流程,頻繁的改動觸發重複渲染,便會讓頁面效能被消耗殆盡。

框架的功能#

現今的「框架」其實就是一種提升開發效率、降低維護難度的開發架構。主流的框架(Framework,例如 React Framework、Vue Framework、TanStack 等)大都擁有以下特性。

資料與 UI 分離#

在原先,要顯示一筆資料可能必須寫死在 HTML 的結構中:

<div id="root">
  <div class="card">
    <h3 class="name">Gary</h3>
    <p class="comment">something</p>
  </div>
</div>

若是動態的資料,可能要透過 JavaScript 將資料動態塞入 DOM 元素上:

let commentData = [
  { name: "Gary", comment: "try harder" },
  { name: "Alice", comment: "good" },
  { name: "Bob", comment: "excellent" },
  //...
];

commentData.forEach((c) => {
  $("#root").append(
    $(document.createElement("div"))
      .append(
        $(document.createElement("h3")).text(c.name),
        $(document.createElement("p")).text(c.comment)
      )
      .addClass("card")
  );
});

但在 Vue 中,可能就會寫成這樣:

<template>
  <div id="root">
    <div class="card" v-for="item in commentData" :key="name">
      <h3 class="name">{{item.name}}</h3>
      <p class="comment">{{item.comment}}</p>
    </div>
  </div>
</template>

<script>
  export default {
    data() {
      return {
        commentData: [
          { name: "Gary", comment: "try harder" },
          { name: "Alice", comment: "good" },
          { name: "Bob", comment: "excellent" },
          //...
        ],
      };
    },
  };
</script>

考慮到可讀性及後續的可維護性,後者相對來說會比較容易閱讀及維護。透過 Vue 的寫法,讓頁面結構的部份仍然由 HTML 決定,並由資料來決定畫面該如何呈現。

由資料決定畫面,這就是現代框架至關重要的核心概念;將資料從介面中抽取出來,每個畫面都是對資料處理過後的呈現。

模組化的 UI#

一個網站總是會有一些重複出現的元素,例如按鈕、輸入表單、表格、對話框等等。在現代框架的概念中,會把這些重複出現的元素稱為元件(Components),每個元件內包含了元件自己需要用的結構、樣式、邏輯。

例如前述的例子,可以將 .card 這個元素抽成單獨的元件:

<template>
  <div class="card">
    <h3 class="name">{{name}}</h3>
    <p class="comment">{{comment}}</p>
  </div>
</template>
<script>
  export default {
    name: "Card",
    props: {
      name: [String],
      comment: [String],
    },
  };
</script>

再從外部元件引入它:

<template>
  <div id="root">
    <Card v-for="item in commentData" v-bind:"item" :key="name" />
  </div>
</template>

<script>
  import Card from "./components/Card";

  export default {
    components: {
      Card,
    },
    data() {
      return {
        commentData: [
          { name: "Gary", comment: "try harder" },
          { name: "Alice", comment: "good" },
          { name: "Bob", comment: "excellent" },
          //...
        ],
      };
    },
  };
</script>

各元件只需要處理元件內的事,外部引用的元件來決定怎麼使用、提供什麼資料給元件。藉由簡單的切分權責,加上由資料決定畫面,就能讓各個元件的任務單一,並且能被重複使用。

現在有許多完成度很高的前端 UI 元件庫,讓開發者有如玩樂高一樣,能快速地用元件堆出想要的畫面。

提升效能#

在複雜的頁面中,如果頻繁透過操作 DOM 的方式改變畫面,可能會造成全頁面的 Reflow 及 Repaint。在使用框架時,開發者不用太擔心這個問題。

原因是在各主流框架的實作中,幾乎都包含了 Virtual Dom 的概念,也就是用 JavaScript 物件來表達當前的頁面結構。藉由與 UI 分離的資料及 Virtual Dom 之間的關係,當資料變動時,事先計算好這次畫面需要變動的地方,便能抵銷掉無意義的更動,並重複利用已存在的 DOM 元素;當真的要進行 DOM 更新時,也會一次將所有需要更新的局部元件更新,讓效能的耗損盡可能降低。

Virtual Dom 變動的計算也是需要耗費運算資源的,若是過於簡單的專案,或是批次的大量修改,很有可能原生的寫法還比框架快上不少。

豐富的開發者生態圈#

選用熱門框架還包含了能參與豐富的開發者生態圈。各大主流框架在 GitHub 上都能輕鬆找到數以千計的眾多套件,從基本元件、互動效果,到快速建立整個專案的模板,你想解決的問題也許剛好有別人早已經解決過。

結語#

現代瀏覽器逐漸符合標準規範,jQuery 的重要性漸漸式微。框架的興起為前端工程帶來巨大轉變:從原本以檔案功能切分的關注點分離,演變為元件權責單一的關注點分離;從單純的頁面呈現,到越來越複雜的頁面流轉與資料串接。

前端社群能量越強,網頁前端要處理的事情也越繁雜。框架同時也意味著限制,唯有理解框架背後的核心思想,才能在限制中找到最適合的方向。

原文出處#

  • 原書/iThome:https://ithelp.ithome.com.tw/articles/10224417