前端框架簡介

我們從整體概述來探討框架、提供 JavaScript 與框架的簡要歷史、框架存在的理由、他們提供什麼東西、如何決定選擇哪個框架、以及前端框架的的替代方案。

先決條件: 熟悉 HTMLCSSJavaScript 這些核心技術。
目標: 理解 JavaScript 前端框架存在的理由、他們解決的問題、可用的替代方案、還有決定選擇的方法。

一段簡短的歷史

在 JavaScript 誕生的 1996,它的作用就只有針對網頁,提供些許的互動和興奮。但之後網路漸漸從拿來看,變成拿來用了。JavaScript 慢慢地紅了起來,JavaScript 開發者也開始針對自己碰上的問題,寫出了能解決問題的工具、接著包成能複用的工具包。他們就能把這個被稱為函式庫(library)的東西,拿去與他人共享。共享的函式庫生態,也有助於塑造網路的增長趨勢。

目前 JavaScript 已經是網路的必需品了,大約 95% 的網站都又在使用 JavaScript,網路也成了當今生活的必須。使用者可以透過文字與影像,來寫論文、聽音樂、看電影、與人遠距離交流。曾經只能透過裝在電腦內的原生軟體所完成的事情,現在也能網路上做到。這種現代化、複雜度高、還有各種互動的網站,被稱為網路應用程式(web applications)。

當代 JavaScript 框架的問世,讓構建高度動態的互動式應用,變得簡單許多。框架是個針對軟體構建,提供完整解決方案的函式庫。這些選項能讓應用程式,開始能預測和同質化。可預測性讓軟體能擴展到巨大的規模時依舊能維護;可預測性和可維護性則對軟體的健康和長壽至關重要。

JavaScript 框架能構建常用的網站裡,許多令人印象深刻的軟體。目前你正在看的 MDN Web Docs 網站,也是用 React/ReactDOM 作為前端支援的。

那有什麼框架?

有很多框架,不過主要有以下「四大框架」。

Ember

Ember 在 2011 年 12 月發行。這個框架始於 SproutCore 的內部專案。這是個比較老的框架:與 React 或 Vue 之類的替代方案相比,其用戶數比較少,不過在穩定性、社區支持、和一些巧妙的編碼原則方面,仍然享譽無數。

Angular

Angular 是個由 Google 內部的 Angular Team 與其他社群所開發的開源專案。這個專案是同一群人由 AngularJS 所重寫的專案。該專案於2016年9月14日發行。

Angular 是基於組件、並使用指令式 HTML 樣板的框架。在構建時,框架的編譯器會將模板,轉換為優化的 JavaScript 程式。Angular 使用了 JavaScript 超集(superset)的 TypeScript。我們將在下一章中詳細介紹它。

Vue

尤雨溪在維護並理解前述的 AngularJS 專案後,於 2014 年發表了 Vue。Vue 是四大框架裡面最年輕的,但成了近年來的當紅炸子雞。

Vue 除了與 Angular 一樣,使用了一些自定義的 HTML 以外,大部分還是使用現代化的標準 JavaScript。

React

Facebook 於 2013 年發表了 React。在發表當時 Facebook 內部早已使用 React 解決許多內部問題。技術上來說 React 並不是框架,而是一個用來渲染 UI 組件的函式庫。React 通常會配合其他函式庫來建立應用程式:例如 React 搭配 React Native 建立手機程式、React 與 ReactDOM 建立網路程式...等等。

由於 React 與 ReactDOM 通常會搭在一起用,React 在通俗上會被理解為 JavaScript 框架。在閱讀本模塊時,我們將以這種通俗理解為基礎。

React 使用一種很像是 HTML 的 JavaScript 語法:JSX

為什麼有框架?

我們已經討論了啟發框架建立的環境,但那不是開發人員為什麼要製造它們的理由。要探索原因,首先需要首先檢查開發軟體所碰上的挑戰。

來看看一個常見的例子吧:一個能建立待辦事項程式,我們將會用待辦事項程式為例子,來介紹不同的框架。這個應用程序要能讓使用者執行諸如渲染事項列表,添加新任務和刪除任務之類的操作;還要能可靠地跟踪並更新程式所依賴的資料在軟體開發中,此這些資料稱為狀態(state)。

每個目標分開來看,理論上都很簡單:我們能遍歷需要渲染的資料、建立新的工作物件、還能用標識符來查詢、編輯或刪除工作。然而,當我們要求程式,讓用戶在瀏覽器完成這一切的話,麻煩就來了。問題在於:更動狀態時,也同時需要更動 UI 的顯示。

讓我們藉由待辦事項程式的一個功能來看看這個問題有多難搞:把工作清單渲染出來。

DOM 的冗長變化

建立 HTML 元素並在瀏覽器上渲染,會需要驚人數量的程式碼。假設我們的狀態,是一個由多個物件組成的陣列:

const state = [
  {
    id: 'todo-0',
    name: 'Learn some frameworks!'
  }
]

我們如何對用戶顯示工作?我們想將每個工作,都表示為一個列表項目:結構為無序列表元素 <ul> 內,含有一定數量的 <li> 元素。怎麼做呢?看起來就像這樣:

function buildTodoItemEl(id, name) {
  const item = document.createElement('li');
  const span = document.createElement('span');
  const textContent = document.createTextNode(name);
 
  span.appendChild(textContent)
 
  item.id = id;
  item.appendChild(span);
  item.appendChild(buildDeleteButtonEl(id));

  return item;
}

我們在這裡用上了 document.createElement() 方法建立了 <li>、還有一些程式碼來建立需要的屬性與子元素。

程式的第十行引用了另一個構建函式:buildDeleteButtonEl()。它與用於構建列表元素的模式很像:

function buildDeleteButtonEl(id) {
  const button = document.createElement('button');
  const textContent = document.createTextNode('Delete');

  button.setAttribute('type', 'button');
  button.appendChild(textContent);

  return button;
}

這個按鈕還派不上用場,但稍後我們會用它來實做刪除功能。這渲染程式,會讓頁面看起來像這樣:

function renderTodoList() {
  const frag = document.createDocumentFragment();
  state.tasks.forEach(task => {
    const item = buildTodoItemEl(task.id, task.name);
    frag.appendChild(item);
  });

  while (todoListEl.firstChild) {
    todoListEl.removeChild(todoListEl.firstChild);
  }
  todoListEl.appendChild(frag);
}

光是為了弄 UI 我們就寫了大約三十行左右的程式:這樣就只是為了能讓清單在 DOM 渲染而已喔。更別提之後還需要添加方便樣式化的 class。

像範例這樣直接操作 DOM 的話,會需要理解很多 DOM 的東西:像是 DOM 的原理、如何建立元素、更改屬性、巢狀排列……同時還要把他們都呈現出來。這些程式甚至還沒有處理與用戶的互動或工作。在增加功能的時候,我們需要在正確的時間、用正確的方法,來更新我們的 UI。

JavaScript 框架旨在使這類工作更加輕鬆:他們會提供更好的開發體驗。框架本身並沒有給 JavaScript 提供新功能;而是用更容易的方案,建立當代的網站。

如果想查看本節中的程式碼範例,請參閱 CodePen 的程式:這網站同樣允許用戶添加和刪除新任務。

閱讀本節中使用的 JavaScript 資訊:

建立 UI 的另一種方法

JavaScript 框架都會提供一種能更加宣告性撰寫介面的方法。也就是說,框架能讓你描述 UI 看起來要怎麼樣、然後在 DOM 的背後完成這一切。

原生 JavaScript 試圖重複建立新 DOM 元素的方法,很難一眼理解。相反地,來看看 Vue 的程式碼會怎麼完成吧:

<ul>
  <li v-for="task in tasks" v-bind:key="task.id">
    <span>{{task.name}}</span>
    <button type="button">Delete</button>
  </li>
</ul>

就這樣啦。原本大約三十多行的程式,現在只要六行。如果不太熟悉大括號和 v- 屬性的話,沒關係;那些語法會在 Vue 模塊學到。這裡只是要講說與原生 JavaScript 對比,框架的程式碼看起來更像是實際呈現的 UI。

也因為有 Vue,我們再也不用自己寫針對 UI 呈現的函式;整個框架會幫我們用最優化、最效率的方法完成這件事。我們只要告訴 Vue 整個排版要怎麼排就好。熟悉 Vue 的開發者,也可以盡快參與我們的專案、搞清楚整個專案到底怎麼做的。不過並不是只有 Vue 這樣:使用框架本身,就可以提高團隊與個人的工作效率。

要在原生 JavaScript 做類似的事是可以的。樣板文字就能在編寫 HTML 字串的同時,也表示最終元素的外觀。就算是待辦事項列表應用,這樣簡單的事情,這可能是一個有用的想法,但是對於管理成千上萬條數據記錄、並且要在用戶界面中呈現盡可能多唯一元素的大型應用程序而言,這樣子是很難維護的。

框架給我們的其他東西

讓我們看看框架賦予的其他優勢。正如之前提,你可以透過原生 JavaScript 實現框架,但是使用框架可以消除自行解決這些問題所需要的認知負擔。

工具

本模塊提到的幾個框架,背後都有著龐大而活躍的社群;這些社群形成了各種生態圈、並提供能增進開發體驗的工具:像是確保功能正常的測試、或著維持程式一致性的 linting。

:如果對這方面的概念有興趣,請看看 Client-side tooling overview

切分

大多數框架鼓勵開發者把介面的各部份,封裝成各種組件(components):也就是一個個可維護、可互通、還可重用的程式碼塊(chunks of code)。與該組件相關連的程式,可獨立為一個、或數個獨立的程式。在原生 JavaScript 裡面要這麼做的話,開發者就必須靠自己的慣例,才能在高效、可擴展的情況下,實現這個目標。但大多數的 JavaScript 開發者,最後會讓 UI 相關的所有程式,都分散在整個文件中。

路由

web 最重要的功能之一,就是頁面之間的導航:畢竟它就是相互連接文件的網路。在你點選網站上的連結時,瀏覽器會與伺服器溝通、並獲取新內容以便顯示給你看。也因為這樣,地址欄中的 URL 就會更改。你可以保存這個新的 URL 並稍後回來、或與其他人分享該 URL,以便他們輕鬆地找到同一個頁面。你的瀏覽器會記住這個導航歷史記錄,也能在頁面之間來回導航。這就叫伺服器端路由(server-side routing)。

現代的網路應用程式通常不獲取和渲染新的 HTML 文件:它們通常載入單個 HTML Shell,並不斷更新其中的 DOM 同時,不導航用戶到新地址(這被稱為單頁應用single page appsSPA)。每個新的虛擬網頁通常稱為 視圖(view),一般來說也不執行路由。

在 SPA 複雜到一定的程度、也有渲染出足夠獨特的視圖時,給應用程式導入路由功能,就變得很重要:人們習慣在應用程式中,透過連結導航到特定頁面,在導航歷史記錄中前進和後退等,而當這些標準的 Web 功能被破壞時,他們的體驗也會受到影響。如果導航功能以用戶端程式提供,這就叫做用戶端路由(server-side routing)。

可以透過原生 JavaScript 實做路由功能。但比較活躍的框架都有相對應的函式庫,讓路由功能在開發過程中更加直觀。

使用框架時要考慮的事情

想成為一位高效的網路開發,意味著你需要選擇最合適的工具:JavaScript 框架能讓前端開發變簡單,但它並不是萬能仙丹。我們回在這個章節探討選擇框架需要考慮的事情。請注意,你可能完全不需要框架。不要為了用框架而用框架。

熟悉工具

如同原生 JavaScript,框架也需要理解它們各自的特性。在選好框架前,確保有足夠的時間熟悉框架,以便讓它成為開發的墊腳石,而不是絆腳石。同時,也要確保你的同事們能接受它。

過度工程化

一個 web 專案如果是個只有數個個人頁面、還幾乎沒有交互功能的話,你完全不需要 JavaScript 框架、甚至 JavaScript 本身也不需要。也就是說,框架並不是整體式的,其中一些可能更適合於小型專案。一篇在 Smashing Magazine 的文章中,作者 Sarah Drasner 寫了一篇怎麼用 Vue 取代 jQuery 的文章、讓網頁的一小部分具有交互性。

更大的程式庫和抽象化

框架能寫出宣告式程式、有時候還會少寫程式。這一切都是透過框架在背後處理 DOM 互動所達成的。這種抽象化給開發者提供了相當棒的體驗,但這並不是免費的。為了把編寫的程式變成能與 DOM 互動的玩意,框架必須執行自己的程式;而這反過來又會讓專案變大,執行演算的開銷也更高昂。

不可避免地,這會產生一些額外的程式;而儘管一個支持 tree-shaking(刪除在構建過程中未實際用到的程式)的框架,會讓應用程式保持小巧,但在考慮性能時,這將是必須牢記在心的因素之一,尤其是在受網路/儲存空間受限的設備上(像是手機)。

框架的抽象化不僅影響你的 JavaScript、它也會影響你對與網絡本質的關係:無論如何構建 Web,最後與用戶交流的都是 HTML。用 JavaScript 編寫整個程式,會讓你看不到 HTML 本身、以及各標籤的用途,最後會生出不語義、且有障礙的 HTML 文件。實際上,你甚至能寫出一個完全依賴 JavaScript,沒有它就完全動不了的脆弱應用程式。

框架不是問題的根源。如果優先事項設錯了,任何應用程式都會變得脆弱、腫大、且障礙多舛。不過,框架確實擴大了我們作為開發人員的優先事項。如果想做出一個很複雜的 Web 程式,這當然能作到;但如果優先事項無法確保性能與無障礙的話,框架將放大這方面的問題。現在的 Web 不再是一個健壯的,內容優先的文件網絡,而是常將 JavaScript 放在首位,用戶體驗則放在最後。

框架網站的無障礙議題

讓我們以上一節的內容為基礎,並進一步討論無障礙問題。消除用戶界面的障礙總是需要點思考與努力,而框架會使該過程複雜化。你通常要用上進階的框架 API 來訪問本機瀏覽器功能,例如 ARIA live region 或 focus 管理。

在某些情況下,框架應用程式會發生在傳統網站不存在的障礙。最明顯的例子,就是前述的客戶端路由。

使用傳統(伺服器端)路由瀏覽 Web 會出現可預測的結果。瀏覽器知道將焦點設置在頁面頂部、輔助技術將宣布頁面標題。在導航到新頁面時,這些事情都一定會發生。

使用客戶端路由時,瀏覽器不會加載新頁面,因此它不知道要自動調整焦點、或宣告新的頁面標題。框架作者會花費大量時間和精力,來編寫可重現這些功能的 JavaScript,但沒有一個框架能做到如此完美。

結論是,所有的 Web 專案一開始,就要應該考慮無障礙問題。如果專案使用抽象的框架、又不考慮無障礙問題的話,未來衍生的無障礙問題會更嚴重。

如何選擇框架

不同模塊的框架,會採用不同的方法。來開發 Web 應用程式。框架都會定期變化、也都有其優缺點。選擇哪個框架的過程,是與團隊及專案息息相關的。你需要透過研究,來找出合適的需求。換句話說,我們已經找出了一些能有效地研究出選擇的問題:

  1. 框架支援哪些瀏覽器?
  2. 框架使用哪個特定領域語言(domain-specific language)?
  3. 框架有夠大的社群與夠好的文件(或其他東西)支援嗎?

接下來我們退提供一個表格,來展示各大框架的瀏覽器支援、還有能用的特定領域語言

一般來說,特定領域語言(domain-specific language, DSL)是一種與的特定領域軟體開發相關的程式語言。以框架的脈絡來說,DSL 是能讓開發更簡單的 JavaScript 或 HTML 變體。最重要的是,沒有哪個框架要求開發者使用某種特定領域語言,但框架們在挑選 DSL 方面,早已心有所屬了。選擇不採用該框架的首選 DSL,可能就會失去本可增添開發人員體驗的功能。

在為任何新專案做出選擇時,你需要認真考慮框架的支持矩陣(support matrix)和 DSL。瀏覽器要是不支援,會成為用戶的障礙;而 DSL 要是不支援,則會你和你開發團隊的障礙。

框架 瀏覽器支援 首選的 DSL 支援的 DSL
Angular IE9+ TypeScript 基於 HTML; TypeScript
React 當代瀏覽器(IE9+ 含有 Polyfill) JSX JSX; TypeScript
Vue IE9+ 基於 HTML 基於 HTML, JSX, Pug
Ember 當代瀏覽器(IE9+ 直到 2.18 為止) Handlebars Handlebars, TypeScript

:「基於 HTML」的 DSL 並沒有官方名字。雖然它們不完全是 DSL,但也不是標準的 HTML,所在我們在此附帶一題。

表格的引用來源:

框架有堅實的社區嗎?

這大概是最難評估的,因為社區沒辦法用什麼數字直接估算。你或許可以用 GitHub 的星星、或是 npm 的下載量來看,但有時最好的辦法,是找看看一些論壇、或和其他開發者聊聊看。這不只是社區大小的問題而已,你還需要看看社區多麼熱情和包容、以及文件多好讀。

Web 的看法

在選框架這件事,不要只聽我們的話:網路上也有不少的討論。例如說,維基媒體基金會最近就選了 Vue 作為他們的前端框架,還發表了相關的請求意見稿。請求意見稿的作者 Eric Gardner 花了不少時間,概述維基媒體基金會的需求、還有為什麼他認為這個框架,對開發團隊有益。這個請求意見稿,是你在研究要使用什麼框架時,需要考量什麼的一個好例子。

State of JavaScript survey 也是有用的 JavaScript 開發者反饋集合。它包含了很多 JavaScript 相關的要點、包括有關框架使用情況、還有開發者對各框架看法的數據。網站也有數年的可用數據,以便了解框架的消長。

Vue 的開發團隊也寫了有關 Vue 與其他框架的詳盡比較。如同他們自知,這種比較可能會有一些偏見,但這仍然是一種寶貴的資源。

前端框架的替代

如果在尋找能夠加速開發的工具、卻又發現專案不需要前端 JavaScript 框架的話,可以試試其他用於構建 Web 的解決方案:

  • 內容管理系統(content management system)
  • 伺服器渲染(Server-side rendering)
  • 靜態網站產生器(static site generator)

內容管理系統

內容管理系統( Content Management System, CMS)是能讓用戶在不自己寫程式的情況下,創建內容的工具。針對大型專案,尤其是撰寫者不熟悉程式、或者開發者想省時間的情況下,內容管理系統是個相當不錯的解決方案。不過,內容管理系統也需要費神去設定。使用 CMS 也同時意味著你會在最終輸出的控制方面,做出一定程度的退讓。比方說,如果 CMS 不太著墨在無障礙方面,那你也很難在這方面有所改進。

坊間常見的內容管理系統有 WordpressJoomlaDrupal

伺服器渲染

伺服器端渲染(Server-side rendering, SSR)是由伺服器負責渲染單頁應用的程式架構,與構建 JavaScript 程式中最常見,最直接的用戶端渲染(client-side rendering)相對。伺服器端渲染只單純傳送 HTML 檔案,所以對用戶端更友善;但設置起來就比用戶端渲染程式難得多。

本模塊的所有用戶端渲染框架,都有相對應的伺服器端渲染。像是 React 的 Next.js、Vue 的 Nuxt.js(對這的確很教人困惑,不過兩者沒有關係)、Ember 的 FastBoot、Angular 的 Angular Universal

:某些伺服器端渲染的解決方案,是由社區編寫和維護;但也有「官方」的解決方案,是由框架維護者提供的。

靜態網站產生器

靜態網站產生器是個可以給網站生成多個網頁的程式──這包括相對應的 CSS 或 JavaScript──以便在任何地方發布。發布主機可以是 GitHub pages 分支、或著 Netlify 實體、抑或著是私人的伺服器。這種方法有很多優點,主要是性能方面(用戶端收到網頁時,已經載入了整個網頁,所以不需要執行 JavaScript)與安全性(靜態網站的攻擊因為變少了)。靜態網站還是能在需要時用上 JavaScript,但靜態網站並不依賴 JavaScript。一如其他工具,靜態網站產生器需要點時間去搞懂。這可能是開發過程的障礙。

靜態網站的獨立網頁,要多少就能有多少。如同框架能加快開發用戶端 JavaScript 程式一般,靜態網站產生器也能快速產生 HTML 檔案(要不然,你本來要自己寫的)。靜態網站產生器也像框架一樣,能允許用戶給網頁撰寫組件;並在最後建立頁面時,把組件都結合起來。以靜態網站產生器而言,組件被稱為樣板(template)。靜態網站產生器構建的網頁,甚至可以作為框架應用程式的宿主:比方說你可以完成「如果希望用戶造訪某個特定頁面時,啟動 React 程式」這樣的需求。

靜態網站產生器已經存在好一段時間了,但他們最近在 Web 歷史中再度復興起來。現在有一些強大的選擇,像是 HugoJekyllEleventyGatsby

如果想深入理解靜態網站產生器的概念,看一下 Tatiana Mac 的 Beginner's guide to Eleventy。在該系列的第一篇文章中,她解釋了什麼是靜態網站生成器,以及它與發布 Web 內容的其他方式之間的關係。

結論

我們終於把框架介紹完了。我們還沒有教任何程式,但我們希望提供了有用的背景知識,說明為什麼要使用框架,如何選擇框架,還有燃起想要學習的興趣!

我們的下一篇文章,將探討更底層的東西,著眼於框架傾向於提供的特定種類的功能,以及它們為什麼能動。

在本模塊