客戶端框架簡介

我們從對該領域的一般概述開始我們對框架的考察,考察了 JavaScript 和框架的簡要歷史,框架存在的原因以及它們帶給我們的東西,如何開始思考選擇要學習的框架以及客戶端框架有哪些替代方案。

先決條件 熟悉核心 HTMLCSSJavaScript 語言。
目標 瞭解客戶端 JavaScript 框架是如何誕生的,它們解決了什麼問題,有哪些替代方案,以及如何選擇一個框架。

簡史

當 JavaScript 在 1996 年首次亮相時,它為一個原本由靜態文件組成的 Web 添加了偶爾的互動性和興奮感。Web 不僅僅成為了一個閱讀內容的地方,而是做事情的地方。JavaScript 的受歡迎程度穩步上升。使用 JavaScript 的開發人員編寫了工具來解決他們遇到的問題,並將它們打包成可重用包,稱為,以便他們可以與其他人分享他們的解決方案。這個共享的庫生態系統幫助塑造了 Web 的發展。

現在,JavaScript 是 Web 的一個重要組成部分,在所有網站中使用率為 98%,而 Web 是現代生活中不可或缺的一部分。使用者用文字、音訊或影片聊天即時撰寫論文、管理預算、流媒體音樂、觀看電影並與他人進行遠端交流。Web 使我們能夠完成以前只能透過安裝在我們計算機上的原生應用程式才能完成的事情。這些現代、複雜、互動式的網站通常被稱為Web 應用程式

現代 JavaScript 框架的出現使構建高度動態、互動式應用程式變得更加容易。框架是一個提供關於軟體構建方式的意見的庫。這些意見允許應用程式中的可預測性和同質性;可預測性允許軟體擴充套件到龐大規模並仍然可維護;可預測性和可維護性對於軟體的健康和壽命至關重要。

JavaScript 框架為現代 Web 上的大部分令人印象深刻的軟體提供動力 - 包括您可能每天使用的許多網站。您當前正在閱讀的 MDN Web 文件使用 React/ReactDOM 框架為其前端提供動力。

有哪些框架?

那裡有很多框架,但目前被認為是“四大天王”的框架有以下幾種。

Ember

Ember 最初於 2011 年 12 月釋出,是 SproutCore 專案中開始的工作的延續。它是一個較舊的框架,使用者比 React 和 Vue 等更現代的替代方案少,但由於其穩定性、社群支援和一些巧妙的編碼原則,它仍然享有相當高的知名度。

開始學習 Ember

Angular

Angular 是一個開源的 Web 應用程式框架,由 Google 的 Angular 團隊以及個人和公司組成的社群領導。它是來自構建 AngularJS 的同一個團隊的完全重寫。Angular 於 2016 年 9 月 14 日正式釋出。

Angular 是一個基於元件的框架,它使用宣告式 HTML 模板。在構建時,框架的編譯器透明地將模板轉換為最佳化的 JavaScript 指令。Angular 使用 TypeScript,它是 JavaScript 的超集,我們將在下一章中更詳細地介紹它。

開始學習 Angular

Vue

Evan You 在參與並學習了最初的 AngularJS 專案之後,於 2014 年釋出了 Vue。Vue 是四大天王中最年輕的,但最近人氣飆升。

Vue 與 AngularJS 一樣,使用了一些自己的程式碼擴充套件了 HTML。除此之外,它主要依賴於現代的標準 JavaScript。

開始學習 Vue

React

Facebook 於 2013 年釋出了 React。到那時,它已經使用 React 在內部解決了它的許多問題。從技術上講,React 本身不是一個框架;它是一個用於渲染 UI 元件的庫。React 與其他庫結合使用來構建應用程式 - React 和 React Native 使開發人員能夠構建移動應用程式;React 和 ReactDOM 使他們能夠構建 Web 應用程式,等等。

由於 React 和 ReactDOM 通常一起使用,因此 React 在口頭上被理解為一個 JavaScript 框架。在您閱讀本模組時,我們將使用這種口頭上的理解。

React 使用類似 HTML 的語法擴充套件了 JavaScript,稱為 JSX

開始學習 React

為什麼存在框架?

我們已經討論了激發框架建立的環境,但實際上並沒有討論開發人員為什麼需要建立它們。探索原因首先需要檢查軟體開發的挑戰。

考慮一種常見的應用程式:一個待辦事項清單建立器,我們將在以後的章節中使用各種框架來實現它。此應用程式應允許使用者執行諸如渲染任務列表、新增新任務和刪除任務等操作;並且它必須在可靠地跟蹤和更新應用程式底層資料的同時執行此操作。在軟體開發中,這種底層資料被稱為狀態。

我們的每一個目標在孤立的情況下理論上都很簡單。我們可以迭代資料以渲染它;我們可以新增一個物件來建立一個新任務;我們可以使用一個識別符號來查詢、編輯或刪除任務。當我們記住應用程式必須透過瀏覽器讓使用者完成所有這些操作時,就會出現一些裂痕。真正的問題是:每次更改應用程式的狀態時,都需要更新 UI 以匹配。

我們可以透過檢視待辦事項應用程式的一個功能來檢查這個問題的難度:渲染任務列表。

DOM 更改的冗長性

在瀏覽器中構建 HTML 元素並在適當的時間渲染它們需要令人驚訝的大量程式碼。假設我們的狀態是一個結構類似於此的物件陣列

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

我們如何向用戶展示其中一個任務?我們希望將每個任務表示為一個列表項 - 一個位於無序列表元素(<ul>)內的 HTML <li> 元素。我們如何製作它?這可能看起來像這樣

js
function buildTodoItemEl(id, name) {
  const item = document.createElement("li");
  const span = document.createElement("span");

  span.textContent = name;

  item.id = id;
  item.appendChild(span);
  item.appendChild(buildDeleteButtonEl(id));

  return item;
}

在這裡,我們使用 document.createElement() 方法來建立我們的<li>,以及更多行程式碼來建立它需要的屬性和子元素。

前面的程式碼段引用了另一個構建函式:buildDeleteButtonEl()。它遵循與我們用於構建列表項元素的模式類似的模式

js
function buildDeleteButtonEl(id) {
  const button = document.createElement("button");
  button.setAttribute("type", "button");
  button.textContent = "Delete";

  return button;
}

此按鈕目前還沒有任何作用,但稍後在我們決定實現刪除功能時它將發揮作用。將在頁面上渲染我們的專案的程式碼可能如下所示

js
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 中渲染內容 - 而且我們還沒有新增任何可以稍後用來設定列表項樣式的類!

直接使用 DOM,就像在這個例子中一樣,需要了解有關 DOM 工作原理的很多知識:如何建立元素;如何更改其屬性;如何將元素彼此放在一起;如何將它們放在頁面上。這些程式碼都沒有實際處理使用者互動,也沒有解決新增或刪除任務的問題。如果我們新增這些功能,我們必須記住在正確的時間和正確的方式更新我們的 UI。

JavaScript 框架的建立是為了讓這類工作變得更加容易 - 它們的存在是為了提供更好的開發人員體驗。它們不會為 JavaScript 帶來自帶全新的力量;它們讓您更容易訪問 JavaScript 的力量,以便您能夠為今天的 Web 構建應用程式。

如果您想檢視本節中程式碼示例的實際應用,您可以檢視 CodePen 上應用程式的工作版本,該版本還允許使用者新增和刪除新任務。

瞭解有關本節中使用的 JavaScript 功能的更多資訊

另一種構建 UI 的方法

每個 JavaScript 框架都提供了一種以更宣告式的方式編寫使用者介面。也就是說,它們允許您編寫描述 UI 應該如何顯示的程式碼,框架將在幕後完成 DOM 中的操作。

以重複的方式構建新 DOM 元素的 vanilla JavaScript 方法在乍一看很難理解。相比之下,以下程式碼塊說明了您可能使用 Vue 來描述我們的任務列表的方式

html
<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 特定語法的更多資訊。這裡要帶走的是,這段程式碼看起來像它所代表的 UI,而 vanilla JavaScript 程式碼則沒有。

感謝 Vue,我們不必為構建 UI 編寫自己的函式;框架將以最佳化的、高效的方式為我們處理這些工作。我們在這裡唯一需要做的是向 Vue 描述每個專案應該是什麼樣子。熟悉 Vue 的開發人員可以在加入我們的專案時快速瞭解正在發生的事情。Vue 並非孤軍奮戰:使用框架可以提高團隊和個人效率。

在 vanilla JavaScript 中可以做類似的事情。模板字面量字串 使得編寫表示最終元素外觀的 HTML 字串變得容易。這對於我們待辦事項應用程式這樣簡單的應用程式可能是一個有用的想法,但對於管理數千條資料記錄的大型應用程式而言,它無法維護,並且可能在使用者介面中渲染同樣多的唯一元素。

框架提供的其他功能

讓我們看看框架提供的其他優勢。正如我們之前提到的,框架的優勢可以在 vanilla JavaScript 中實現,但使用框架消除了必須自己解決這些問題的認知負擔。

工具

由於本模組中的每個框架都有一個龐大而活躍的社群,因此每個框架的生態系統都提供了可以提高開發人員體驗的工具。這些工具使新增諸如測試(以確保您的應用程式按預期執行)或 linting(以確保您的程式碼沒有錯誤並且在樣式上一致)等功能變得容易。

注意:如果您想了解有關 Web 工具概念的更多詳細資訊,請檢視我們的 客戶端工具概述

隔間化

大多數主流框架鼓勵開發者將使用者介面的不同部分抽象成元件——可維護、可複用的程式碼塊,這些程式碼塊可以相互通訊。與特定元件相關的程式碼可以儲存在一個檔案中(或幾個特定檔案中),這樣你作為開發者就可以清楚地知道要在哪裡進行更改。在一個普通的 JavaScript 應用中,你必須建立一套自己的約定來以高效、可擴充套件的方式實現這一點。許多 JavaScript 開發者,如果任其自由發揮,最終可能會將與 UI 的一部分相關的程式碼分散在整個檔案中——甚至是在另一個檔案中。

路由

網路最重要的功能是允許使用者從一個頁面導航到另一個頁面——畢竟,它是一個相互連結的文件網路。當你點選這個網站上的一個連結時,你的瀏覽器會與伺服器通訊並獲取新的內容來顯示給你。在這個過程中,位址列中的 URL 會發生變化。你可以儲存這個新的 URL 並稍後返回該頁面,或者與他人分享,以便他們可以輕鬆找到相同的頁面。你的瀏覽器會記住你的導航歷史記錄,並允許你向前和向後導航。這被稱為伺服器端路由

現代 Web 應用程式通常不會獲取和渲染新的 HTML 檔案——它們載入一個單一的 HTML 外殼,並不斷更新其中的 DOM(稱為單頁面應用程式SPA),而不會將使用者導航到網路上的新地址。每個新的偽網頁通常被稱為一個檢視,預設情況下,沒有路由。

當 SPA 足夠複雜並渲染足夠的獨特檢視時,將路由功能引入你的應用程式非常重要。人們習慣於能夠連結到應用程式中的特定頁面,在他們的導航歷史記錄中前進和後退等,當這些標準網路功能被破壞時,他們的體驗就會受到影響。當路由以這種方式由客戶端應用程式處理時,它被恰當地稱為客戶端路由

使用 JavaScript 和瀏覽器的原生功能來建立路由是可能的,但是流行的、積極開發的框架都有配套的庫,這些庫使路由成為開發過程中的一個更直觀的組成部分。

使用框架時需要考慮的事項

成為一名有效的 Web 開發人員意味著使用最適合工作的工具。JavaScript 框架使前端應用程式開發變得容易,但它們不是解決所有問題的靈丹妙藥。本節將討論在使用框架時應該考慮的一些事項。請記住,你可能根本不需要框架——要小心不要僅僅為了框架而使用框架。

對工具的熟悉度

就像普通的 JavaScript 一樣,框架需要時間學習,並且有自己的怪癖。在你決定為一個專案使用框架之前,請確保你有時間學習足夠的特性,以便它對你有用,而不是對你不利,並確保你的團隊成員也對它感到舒適。

過度設計

如果你的 Web 開發專案是一個包含幾個頁面的個人投資組合,並且這些頁面幾乎沒有或根本沒有互動功能,那麼框架(及其所有 JavaScript)可能根本沒有必要。也就是說,框架不是整體的,其中一些比其他框架更適合小型專案。在 Smashing Magazine 的一篇文章中,Sarah Drasner 撰寫了關於Vue 如何取代 jQuery作為使網頁的一部分互動式工具。

更大的程式碼庫和抽象

框架允許你編寫更具宣告性的程式碼——有時甚至可以編寫更少的程式碼——透過為你處理 DOM 互動,在幕後進行操作。這種抽象對於你作為開發者的體驗來說很棒,但它不是免費的。為了將你編寫的程式碼轉換為 DOM 更改,框架必須執行自己的程式碼,這反過來會使你的最終軟體更大,並且操作起來在計算上更加昂貴。

一些額外的程式碼是不可避免的,而支援樹形搖動(在構建過程中刪除應用程式中實際沒有使用的任何程式碼)的框架將允許你保持應用程式的體積小,但這仍然是你需要在考慮應用程式效能時牢記的一個因素,特別是在網路/儲存受限的裝置上,例如手機。

框架的抽象不僅影響你的 JavaScript,也影響你與網路本身本質的關係。無論你如何為網路構建,最終結果,即使用者最終互動的層,都是 HTML。用 JavaScript 編寫整個應用程式可能會讓你忽略 HTML 及其各種標籤的目的,並導致你生成一個非語義且不可訪問的 HTML 文件。事實上,有可能編寫一個完全依賴 JavaScript 的脆弱應用程式,如果沒有 JavaScript,該應用程式將無法正常工作。

框架不是我們問題的根源。在優先順序錯誤的情況下,任何應用程式都可能變得脆弱、臃腫且不可訪問。然而,框架確實放大了我們作為開發者的優先順序。如果你優先考慮的是製作一個複雜的 Web 應用程式,那麼很容易做到這一點。但是,如果你的優先順序沒有仔細保護效能和可訪問性,框架會放大你的脆弱性、臃腫和不可訪問性。現代開發者優先順序,被框架放大後,在許多地方顛覆了網路結構。網路不再是一個健壯的、以內容為中心的文件網路,而是經常將 JavaScript 放在首位,將使用者體驗放在最後。

框架驅動的 Web 上的可訪問性

讓我們在上一節的基礎上,再談談可訪問性。使使用者介面可訪問始終需要一些思考和努力,而框架可能會使這個過程變得複雜。你通常必須使用高階框架 API 來訪問本機瀏覽器功能,例如 ARIA 活動區域或焦點管理。

在某些情況下,框架應用程式會建立傳統網站不存在的可訪問性障礙。其中最大的例子是前面提到的客戶端路由。

對於傳統的(伺服器端)路由,在網路中導航會產生可預測的結果。瀏覽器知道將焦點設定為頁面頂部,輔助技術會宣佈頁面的標題。這些事情在你每次導航到新頁面時都會發生。

對於客戶端路由,你的瀏覽器不會載入新的網頁,因此它不知道它應該自動調整焦點或宣佈新的頁面標題。框架作者投入了大量時間和精力來編寫 JavaScript 以重新建立這些功能,即使這樣,也沒有任何框架能夠完美地做到這一點。

總而言之,你應該從每個 Web 專案的開始就考慮可訪問性,但要記住,如果不小心,使用框架的抽象程式碼庫更容易出現重大的可訪問性問題。

如何選擇框架

本模組中討論的每個框架都採用不同的方法來進行 Web 應用程式開發。每個框架都在不斷改進或改變,並且都有其優缺點。選擇合適的框架是一個依賴於團隊和專案的流程,你應該自己進行研究,以發現最適合你需求的框架。也就是說,我們已經確定了一些可以用來更有效地研究你的選擇的問題。

  1. 框架支援哪些瀏覽器?
  2. 框架使用哪些特定領域的語言?
  3. 框架是否有強大的社群和良好的文件(以及其他支援)?

本節中的表格概述了每個框架目前提供的瀏覽器支援,以及它可以使用的特定領域語言

總的來說,特定領域語言 (DSL)是在軟體開發的特定領域相關的程式語言。在框架的上下文中,DSL 是 JavaScript 或 HTML 的變體,它使使用該框架進行開發變得更容易。重要的是,沒有任何框架要求開發者使用特定的 DSL,但它們幾乎全部都是圍繞著特定的 DSL 設計的。選擇不使用框架的首選 DSL 將意味著你錯過了原本可以改善開發者體驗的功能。

在為任何新專案做出選擇時,你應該認真考慮框架的支援矩陣和 DSL。不匹配的瀏覽器支援可能成為使用者的一道障礙;不匹配的 DSL 支援可能成為你和你的團隊成員的一道障礙。

框架 瀏覽器支援 首選 DSL 支援的 DSL 引用
Angular 現代 TypeScript 基於 HTML;TypeScript 官方文件
React 現代 JSX JSX;TypeScript 官方文件
Vue 現代(Vue 2 中的 IE9+) 基於 HTML 基於 HTML、JSX、Pug 官方文件
Ember 現代(Ember 版本 2.18 中的 IE9+) Handlebars Handlebars、TypeScript 官方文件

注意:我們描述為“基於 HTML”的 DSL 沒有官方名稱。它們不是真正的 DSL,但它們是非標準的 HTML,因此我們認為它們值得強調。

框架是否有強大的社群?

這可能是最難衡量的指標,因為社群規模與易於訪問的數字沒有直接關係。你可以檢視專案的 GitHub 星級數量或每週的 npm 下載量,以瞭解其受歡迎程度,但有時最好的方法是在幾個論壇上搜索或與其他開發者交談。這不僅關乎社群的規模,還關乎其歡迎和包容程度,以及現有文件的質量。

網路上的觀點

不要僅僅相信我們的話——網路上到處都有討論。維基媒體基金會最近選擇使用 Vue 來構建其前端,併發布了框架採用意見徵集 (RFC)。RFC 的作者 Eric Gardner 花時間概述了維基媒體專案的需要,以及為什麼某些框架是團隊的好選擇。這個 RFC 是一個很好的例子,說明了你計劃使用前端框架時應該為自己做哪種研究。

The State of JavaScript survey 是來自 JavaScript 開發者的一份有用的反饋集合。它涵蓋了與 JavaScript 相關的許多主題,包括有關框架使用情況和開發者對框架情緒的資料。目前,有幾年的資料可用,允許你瞭解框架的受歡迎程度。

Vue 團隊已經全面比較了 Vue 與其他流行框架。這種比較可能存在一些偏差(他們也注意到了這一點),但它仍然是一個寶貴的資源。

客戶端框架的替代方案

如果你正在尋找可以加快 Web 開發流程的工具,並且你知道你的專案不需要密集的客戶端 JavaScript,那麼你可以使用其他幾種構建 Web 的解決方案。

  • 內容管理系統
  • 伺服器端渲染
  • 靜態網站生成器

內容管理系統

內容管理系統 (CMS) 是任何允許使用者在不直接編寫程式碼的情況下建立網站內容的工具。它們是大型專案的良好解決方案,尤其是需要內容編寫者(他們對程式碼能力有限)或希望節省時間的程式設計師輸入的專案。但是,它們確實需要大量的設定時間,並且使用 CMS 意味著您至少要放棄對網站最終輸出的某些控制。例如:如果您的選定 CMS 預設情況下不生成可訪問的內容,那麼改進這一點通常很困難。

一些流行的 CMS 系統包括 WordpressJoomlaDrupal

伺服器端渲染

伺服器端渲染 (SSR) 是一種應用程式架構,其中伺服器負責渲染單頁應用程式。這與客戶端渲染相反,客戶端渲染是構建 JavaScript 應用程式最常見和最直接的方式。伺服器端渲染對客戶端裝置更容易,因為您只需將渲染後的 HTML 檔案傳送給他們,但與客戶端渲染的應用程式相比,它可能很難設定。

本模組中介紹的所有框架都支援伺服器端渲染和客戶端渲染。檢視 Next.js(用於 React)、Nuxt(用於 Vue - 是的,這很令人困惑,而且這兩個專案沒有關係!)、FastBoot(用於 Ember)和 Angular Universal(用於 Angular)。

注意:一些 SSR 解決方案是由社群編寫和維護的,而另一些則是由框架維護者提供的“官方”解決方案。

靜態網站生成器

靜態網站生成器 是動態生成多頁網站的所有網頁(包括任何相關的 CSS 或 JavaScript)的程式,以便它們可以釋出到任何數量的地方。例如,釋出主機可以是 GitHub Pages 分支、Netlify 例項或您選擇的任何私有伺服器。這種方法有很多優點,主要圍繞效能(使用者的裝置不會使用 JavaScript 構建頁面;它已經完成)和安全性(靜態頁面攻擊向量更少)。這些網站仍然可以在需要時使用 JavaScript,但它們並不依賴於它。靜態網站生成器需要時間學習,就像任何其他工具一樣,這可能成為開發過程的障礙。

靜態網站可以擁有任意數量的唯一頁面。就像框架使您能夠快速編寫客戶端 JavaScript 應用程式一樣,靜態網站生成器為您提供了一種快速建立您原本需要單獨編寫的 HTML 檔案的方法。與框架一樣,靜態網站生成器允許開發人員編寫元件來定義網頁的通用部分,並將這些元件組合在一起以建立最終頁面。在靜態網站生成器的上下文中,這些元件稱為模板。由靜態網站生成器構建的網頁甚至可以是框架應用程式的所在地:例如,如果您希望靜態生成的網站的某個特定頁面在使用者訪問該頁面時啟動 React 應用程式,您可以這樣做。

靜態網站生成器已經存在了相當長的時間,並且它們一直在不斷最佳化和創新。存在各種選擇,包括 AstroEleventyHugoJekyllGatsby,它們建立在各種技術棧之上並提供獨特的特性。其他選項,例如 DocusaurusVitePress,使用客戶端框架而不是模板,但生成類似的最佳化靜態檔案。

如果您想了解有關靜態網站生成器的更多資訊,請檢視 Tatiana Mac 的 Eleventy 入門指南。在該系列的第一篇文章中,他們解釋了什麼是靜態網站生成器,以及它如何與其他釋出網站內容的方式相關。

總結

這讓我們結束了對框架的介紹 - 我們還沒有教你任何程式碼,但希望我們已經為你提供了一個有用的背景,解釋了為什麼首先要使用框架以及如何選擇框架,並讓你對學習更多並投入其中感到興奮!

我們的下一篇文章將深入探討更低階的層面,探討框架傾向於提供的特定型別的功能,以及它們為何以這種方式工作。