[譯文] 現代 js 框架存在的根本原因

sea_ljf發表於2018-06-02

原文連結:The deepest reason why modern JavaScript frameworks exist

眾成翻譯地址:現代 js 框架存在的根本原因(翻譯一樣是我翻譯的,但它缺了幾張圖,因此掘金重新發了~)

精讀《現代 js 框架存在的根本原因》 by 黃子毅(感謝大神的精讀,也是根據他的建議看了原文,也順便為大家帶來翻譯)

我曾見過很多很多人盲目地使用(前端)框架,如 React,Angular 或 Vue等等。這些框架提供了許多有意思的東西,然而通常人們(自以為)使用框架是因為:

  • 它們支援元件化;
  • 它們有強大的社群支援;
  • 它們有很多(基於框架的)第三方庫來解決問題;
  • 它們有很多(很好的)第三方元件;
  • 它們有瀏覽器擴充套件工具來幫助除錯;
  • 它們適合做單頁應用。

[譯文] 現代 js 框架存在的根本原因

但這些都不是使用框架的根本原因。

最最本質的原因是:

[譯文] 現代 js 框架存在的根本原因
(UI 與狀態同步非常困難)

是的,就是這原因,讓我們來看看為什麼

假設你正在設計這樣一個 Web 應用:使用者可以通過群發電子郵件來邀請其他人(參加某活動)。UX/UI 設計師設計如下:(在使用者填寫任何郵箱地址之前,)有一個空白狀態,併為此新增一些幫助資訊;(當使用者填寫郵箱之後,)展示郵箱的地址,每個地址的右側均有一個按鈕用於刪除對應的地址。

[譯文] 現代 js 框架存在的根本原因

這個表單的狀態,可以被設計為一個陣列,裡面包含若干物件,物件由郵箱地址和唯一標識組成。開始的時候,陣列為空。當(使用者)輸入郵箱地址並按下Enter鍵之後,往陣列中新增一項並更新 UI。當使用者點選刪除按鈕時,刪除(陣列中對應的)郵箱地址並更新 UI。你感覺到了嗎?每當你改變狀態時,你都需要更新 UI

(你可能會說:)那又怎樣?好吧,讓我們看看如何在不用框架的情況下實現它:

用原生(JS)實現相對複雜的 UI

以下程式碼很好地說明了使用原生 JavaScript 實現一個相對複雜的 UI 所需的工作量,使用像 jQuery 這樣經典的庫也需要差不多的工作量。

在這個例子中,HTML 負責建立靜態頁面,JavaScript 通過 document.createElement 動態改變(DOM 結構)。這引來了第一個問題:構建 UI 相關的 JavaScript 程式碼並不直觀易讀,我們將 UI 構建分為了兩部分(譯者注:應該是指 HTML與 JavaScript 兩部分)。儘管我們使用了 innerHTML,可讀性是增強了,但降低了(頁面的)效能,同時可能存在 CSRF 漏洞。我們也可以使用模板引擎,但如果是大面積地修改 DOM,會面臨兩個問題:效率不高與需要重新繫結事件處理器。

但這也不是(不使用框架的)最大問題。最大的問題是每當狀態發生改變時都要(手動)更新 UI。每次狀態更新時,都需要很多程式碼來改變 UI。當新增電子郵件地址時,只需要兩行程式碼來更新狀態,但要十三行程式碼更新 UI。(此例中)我們已經讓 UI(介面與邏輯)儘可能簡單了!!

[譯文] 現代 js 框架存在的根本原因

程式碼既難寫又難理解,更麻煩的是它非常脆弱。假設我們需要(新增)同步伺服器資料到郵件地址列表的功能,我們需要對比伺服器返回結果與陣列中資料的差異。這涉及對比所有資料的標識與內容,(當使用者修改後,)可能需要在記憶體中保留一份標識相同但內容不同的資料。

為了高效地改變 DOM,我們需要編寫大量點對點(譯者注:指狀態到 UI)的程式碼。但只要你犯下了很小的錯誤,UI 與狀態將不再保持同步:(可能會出現)丟失或呈現錯誤的資訊、不再響應使用者的操作,更糟糕的是觸發了錯誤的動作(如點了刪除按鈕後刪除了非對應的一項)。

因此,保持 UI 與狀態同步,需要編寫大量乏味且非常脆弱的程式碼。

響應式 UI 拯救一切

[譯文] 現代 js 框架存在的根本原因

所以,(之所以使用框架,)不是因為社群,不是因為工具,不是因為生態,不是因為第三方庫......

目前為止,框架最大的改進是(為我們)提供了應用內部狀態與 UI 同步的可靠保證。

只要你清楚特定框架的某些(特定)規則(如不可變狀態),就差不多(可以正常使用)了。

我們只需要定義一次 UI 介面,不再需要為每個操作編寫特定的 UI 程式碼,同時,每個相同的狀態均有相同的輸出(譯者注:指 UI 一致):當狀態改變後,框架自動更新(對應的)檢視。

框架是如何工作的呢?

基於兩個基本的策略:

  • 重新渲染整個元件,如React。當元件中的狀態發生改變時,在記憶體中計算出(新的)DOM 結構後與已有的 DOM 結構進行對比。實際上,這是非常昂貴的。因而採取(將真實 DOM)對映為虛擬 DOM ,通過對比狀態變化前後虛擬 DOM 的不同,計算出變化後再改變真實 DOM 結構。這個過程稱為調和(reconciliation)。

  • 通過(新增)觀察者監測變化,如 Angular 和 Vue.js。應用中狀態的屬性會被監測,當它們發生變化時,只有依賴了(發生變化)屬性的 DOM 元素會被重新渲染。

那 Web components 呢?

很多時候,人們會把 React、 Angular 和 Vue.js (等框架)與 Web components 進行對比。這顯然體現了人們並不理解這些框架所提供的最大好處:保持 UI 與狀態同步。Web components 並不提供這種同步機制。它僅僅提供了一個<template>標籤,但它不提供任何(狀態與 UI 之間的)協調機制。如果你在應用中使用 Web components 時,想保持 UI 與內部狀態同步,則需要(開發者)手工完成,或者使用如 Stencil.js (內部和 React一樣,使用虛擬 DOM)之類的庫。

讓我們明確一點:框架表現出的巨大潛力並不體現在元件化上,保持 UI 與狀態同步才是具體的體現。Web components 並未提供相關的功能,你必須手工或使用第三方庫去解決(同步的)問題。使用原生 JavaScript 去編寫複雜、高效且易於維護的 UI 介面基本上是不可能的。這就是你需要使用現代 JavaScript 框架的根本原因。

自己動手,豐衣足食

如果熱衷於瞭解底層原理,想知道虛擬 DOM 的具體實現。那,為何不試著在不使用框架的情況下,僅使用虛擬 DOM 來重寫原生 UI呢?

這裡是框架的核心,所有元件的基礎類。

[譯文] 現代 js 框架存在的根本原因

這裡是重寫後的 AddressList 元件(藉助 babel 來支援 JSX 的轉換)。

[譯文] 現代 js 框架存在的根本原因

現在 UI 是宣告式的,我們並未使用任何框架。我們能任意新增新邏輯來改變狀態的同時,不需要編寫額外的程式碼來保持 UI 同步。問題解決了!

現在,除了事件處理之外,這看起來就像個 React 應用對吧?我們有haverender()componentDidMount()setState()等等。一旦解決了保持應用內 UI 與狀態的同步問題,所有東西就會很自然地疊加起來(形成元件)。

可以在這個 Github 倉庫中找到完整的原始碼。

[譯文] 現代 js 框架存在的根本原因

結論

  • 現代 js 框架解決的主要問題是保持 UI 與狀態同步。
  • 使用原生 JavaScript 編寫複雜、高效而又易於維護的 UI 介面幾乎是不可能的。
  • Web components 並未提供解決同步問題的方案。
  • 使用現有的虛擬 DOM 庫去搭建自己的框架並不困難。但並不建議這麼做!

相關文章