Ubuntu 云计算 php centos Python 开源 微软 wordpress 编程 java Android nginx 程序员 apache Firefox shell mysql Windows google linux

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

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

它們支持組件化;
它們有強大的社區支持;
它們有很多(基於框架的)第三方庫來解決問題;
它們有很多(很好的)第三方組件;
它們有瀏覽器擴展工具來幫助調試;
它們適合做單頁應用。

 

163bab730430ce9f?imageslim

 

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

最最本質的原因是:

 

1 (UI 与状态同步非常困难)

 

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

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

 

1

 

這個表單的狀態,可以被設計為一個數組,裏面包含若幹對象,對象由郵箱地址和唯一標識組成。開始的時候,數組為空。當(用戶)輸入郵箱地址並按下回車鍵之後,往數組中添加一項並更新 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(界面與邏輯)盡可能簡單了!!

 

1

 

代碼既難寫又難理解,更麻煩的是它非常脆弱。假設我們需要(添加)同步服務器數據到郵件地址列表的功能,我們需要對比服務器返回結果與數組中數據的差異。這涉及對比所有數據的標識與內容,(當用戶修改後,)可能需要在內存中保留一份標識相同但內容不同的數據。

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

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

響應式 UI 拯救一切

 

1

 

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

目前為止,框架最大的改進是(為我們)提供了應用內部狀態與 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呢?

這裏是框架的核心,所有組件的基礎類。

 

1

 

这里是重写后的 AddressList 组件(借助 babel 来支持 JSX 的转换)。

 

1

 

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

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

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

延伸阅读

    评论