現代 JavaScript 框架存在的主要原因


簡評:現代 JavaScript 框架的出現最主要是解決哪個問題?這篇文章很好的解釋了這個問題。

我見過許多人盲目地使用像 React,Angular 或 Vue.js 這樣的現代框架。這些框架提供了許多有趣的東西,通常人們會忽略這些框架存在最主要的原因,這些原因不是:

  • 它們基於組件;
  • 它們有一個強大的社區;
  • 它們有很多第三方庫;
  • 它們有很多有用的第三方組件;
  • 它們有瀏覽器插件,可以幫助調試;
  • 它們適用於單頁面應用程序。

這些都不是最本質的原因,最本質的原因是保持 UI 和狀態同步並不容易。

UI 和 狀態同步難在哪?

假如,您正在構建一個 Web 應用程序,用戶可以填寫他人的 email 地址來發起邀請。並且邀請列表有兩種狀態:

  1. 空狀態,我們在這個狀態下提示用戶填寫郵箱。
  2. 非空狀態,這種狀態我們需要列出出等待被邀請的用戶,並且提供刪除按鈕。

嘗試使用純 JavaScript 實現這種功能

源碼和效果可以到參考:codepen

index.html 代碼

`

Type an email address and hit enter

`

JS 代碼

`class AddressList {
constructor(root) {
// state variables
this.state = []

// UI variables
this.root = root
this.form = root.querySelector('form')
this.input = this.form.querySelector('input')
this.help = this.form.querySelector('.help')
this.ul = root.querySelector('ul')
this.items = {} // id -> li element

// event handlers
this.form.addEventListener('submit', e => {
  e.preventDefault()
  const address = this.input.value
  this.input.value = ''
  this.addAddress(address)
})

this.ul.addEventListener('click', e => {
  const id = e.target.getAttribute('data-delete-id')
  if (!id) return // user clicked in something else      
  this.removeAddress(id)
})

}

addAddress(address) {
// state logic
const id = String(Date.now())
this.state = this.state.concat({ address, id })

// UI logic
this.updateHelp()

const li = document.createElement('li')
const span = document.createElement('span')
const del = document.createElement('a')
span.innerText = address
del.innerText = 'delete'
del.setAttribute('data-delete-id', id)

this.ul.appendChild(li)
li.appendChild(del)
li.appendChild(span)
this.items[id] = li

}

removeAddress(id) {
// state logic
this.state = this.state.filter(item => item.id !== id)

// UI logic
this.updateHelp()
const li = this.items[id]
this.ul.removeChild(li)

}

// utility method
updateHelp() {
if (this.state.length > 0) {
this.help.classList.add('hidden')
} else {
this.help.classList.remove('hidden')
}
}
}

const root = document.getElementById('addressList')
new AddressList(root)`

這段代碼很好的說明了使用純 JavaScript 實現一個有點小復雜的 UI 所需要的工作量。

在示例中,靜態結構在 HTML 中創建,動態內容使用 JavaScript 來創建。這種方式有幾個問題:

構建 UI 的 JavaScript 代碼可讀性不高,我們用兩個不同的部分來定義 UI。我們可以使用 innerHTML來讓代碼更容讀,但是這樣效率不高,而且容易引發跨站腳本漏洞。我們也可以使用模板引擎,但是如果重新生成大的 DOM 的子節點又會遇到兩個問題:效率不高,通常需要重新連接 event handler。

但這都是小問題,最主要的問題是:我們需要在狀態變更的時候更新 UI。每一次狀態出現變更我們都需要使用大量的代碼來更新 UI。上面的例子我們更新狀態是用了兩行的代碼,但是更新 UI 卻耗費了 13 行代碼(盡管這個 UI 並不復雜)。

它不僅編寫起來復雜而且還很脆弱。想象一下,我們需要實現將列表於服務器同步的功能。我們需要將本地數據和服務器發來的數據進行比較。並且需要點對點的對每個變更同步到 DOM 節點中。如果這個過程中有每一步出現差錯都直接導致 UI 同步失敗。

因此,維護 UI 與數據同步需要編寫大量繁瑣,脆弱和脆弱的代碼。

聲明式 UI 解決方案

它是不是社區,它不是工具,也不是生態系統,也不是第三方庫......

到目前為止,這些框架提供的最大的改進是實現應用狀態和 UI 同步。

我們只需要定義一次 UI,不必編寫為每一次動作編寫 UI。相同的狀態總能得到相同的 UI 輸出(狀態和 UI 同步,狀態變更后會自動更新 UI)。

原理

有兩個基本策略:

  • 重新渲染整個組件: React。當組件的狀態發送變化時,它會在內存中渲染一個 DOM,並和現有 DOM 進行比較。但是為了降低成本,實際上它會渲染一個虛擬 DOM,來和之前的虛擬 DOM 進行比較,然后計算更改並對真實 DOM 進行修改。
  • 使用觀察者來監聽變化: Angular 和 Vue.js 觀察你的狀態變化,並且只會更新關聯的 DOM 元素。

和 Web Component 比較?

很多時候人們將 React,Angular 和 Vue.js 和 Web 組件進行比較。很多人不理解這些框架提供的最大好處:保持 UI 和狀態同步。而 Web 組件並不提供內容,它是一套規范,以便開發者可以自由創建可重用的元素。所以單純使用 Web Component + 純 JavaScript 仍然需要手動保證狀態同步,要實現高效易維護的 UI 還需要使用 現代 JavaScript 框架。

自己實現

自己實現一個類似的功能,能夠加深對原理的理解。我們嘗試使用 虛擬DOM (而不是直接用第三方框架)實現一個類似 React 的框架,來重寫剛剛的 demo。

下面是我們 Framework 的核心部分,代表所有組件的基類:

下面是基於 Component 重寫的郵箱邀請的應用(借助 babel 變換來支持 JSX)這里是源碼

現在的 UI 是聲明性的,而且我們沒有直接使用任何框架。我們可以實現以任何方式更改狀態的邏輯,並且不需要額外編寫 UI 同步的代碼。

原文:The deepest reason why modern JavaScript frameworks exist


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM