[譯]現代框架存在的根本緣由

原文連接:medium.com/dailyjs/the…javascript

前言

我曾見過許多人盲目地使用像 ReactAngularVue 這樣的現代框架。這些框架提供了許多有趣的東西,但一般人們會忽略它們存在的根本緣由。html

並非咱們所想的如下緣由:前端

  1. 它們基於組件;
  2. 它們有強大的社區;
  3. 它們有不少第三方庫來解決問題;
  4. 它們有不少第三方組件;
  5. 它們有瀏覽器擴展工具來幫助調試;
  6. 它們適合作單頁應用。

最基本的、最根本的、最深入的緣由是:java

UI 與狀態同步很是困難數組

爲何

假設你在開發一個這樣需求:瀏覽器

用戶能夠經過發送郵件來邀請其餘用戶。bash

UI 交互設計以下:服務器

  1. 輸入框有一個空狀態(帶有提示信息)
  2. 輸入郵箱後展現相應的 郵箱,每一個地址的右側都有一個刪除按鈕。

原型以下: babel

這個表單是一個包含電子郵件地址和惟一標識符的對象數組。最初它將是空的。輸入郵件回車後,向該數組中添加一項並更新 UI。當用戶點擊刪除時,刪除對應的項並更新 UI。app

感覺到了嗎?每次更改狀態時,都須要更新 UI。

我聽到你再說,那又怎樣?OK,讓咱們看看如何在不用框架的狀況下實現它。

原生實現相對複雜的 UI

// html
<html>
  <body>
    <div id="addressList">
      <form>
        <input>
        <p class="help">Type an email address and hit enter</p>
        <ul>
        </ul>
      </form>
    </div>
  </body>
</html>

// 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);
複製代碼

codepen 地址:codepen.io/gimenete/em…

以上代碼很好地說明了使用原生 JavaScript 實現一個相對複雜的 UI 所需的工做量。

在這個例子中,HTML 負責建立靜態頁面,JavaScript 經過 document.createElement 改變 DOM 結構。

這引來了第一個問題:

構建 UI 相關的 JavaScript 代碼比較複雜,並且 UI 構建分爲了兩部分。咱們本能夠用 innerHTML,雖然它有更高的可讀性,但下降了頁面的性能,同時可能存在 CSRF 漏洞。

咱們也可使用模板引擎,但若是是大面積地修改 DOM,會面臨兩個問題:效率不高與須要從新綁定事件處理器。

但這不是最大問題。最大的問題是每當狀態發生改變時都要手動更新 UI。每次狀態更新時,都須要不少代碼來改變 UI。當添加電子郵件地址時,只須要兩行代碼來更新狀態,但要十三行代碼更新 UI。並且咱們已經讓 UI 儘量簡單了!

它不只難以編寫並且難以推理,更重要的是:它也很是脆弱。

假設咱們咱們須要實現將列表與服務器同步的功能,咱們須要將數據同服務器返回的數據做對比。

咱們須要寫大量代碼,使 DOM 更新更加高效。但若是有任何微小的錯誤,視圖將與數據再也不同步。

所以,爲了保持視圖與狀態同步,咱們須要寫大量乏味且脆弱的代碼。

響應式拯救一切

之因此使用框架不是由於社區,不是由於工具,不是由於生態,不是由於第三方庫......

目前爲止,框架最大的改進是保證 UI 和數據同步。

只要你清楚框架的使用規則,就能夠很愉快的使用他們。

We define the UI in a single shot, not having to write particular UI code in every action, and we always get the same output due to a particular state: the framework automatically updates it after the state changes.

框架是如何工做的呢?

有兩個基本的策略:

  1. 從新渲染整個組件,如 React。當組件中的狀態發生改變時,在內存中計算出新的 DOM 結構後與已有的 DOM 結構進行對比。實際上,這是很是昂貴的。於是採起虛擬 DOM ,經過對比狀態變化先後虛擬 DOM 的不一樣,計算出變化後再改變真實 DOM 結構。這個過程稱爲調和(reconciliation)。

  2. 經過觀察者監測變化,如 Angular 和 Vue。應用中狀態的屬性會被監測,當它們發生變化時,相應的 DOM 元素會從新渲染。

Web components 怎麼樣

不少狀況,人們會把 React、 Angular 和 Vue 與 Web components 進行對比。這些人顯然不理解這些框架所提供的最大好處:保持 UI 與狀態同步。

Web components 並不提供這種同步機制。它只是提供了一個<template> 標籤。若是你在應用中使用 Web components 時,想保持 UI 與狀態同步,則須要開發者手工完成,或者使用相關庫。

本身開發一個框架?

若是熱衷於瞭解底層原理,想知道虛擬 DOM 的具體實現。那,爲什麼不試着在不使用框架的狀況下,僅使用虛擬 DOM 來重寫原生 UI呢?

這裏是框架的核心,全部組件的基礎類。

我喜歡學習事物的原理 —— 虛擬 DOM 實現。那麼,爲何咱們學習 Virtual DOM 的實現呢?

這是框架的核心,是任何組件的基類。

這裏是重寫後的 AddressList 組件(使用 babel 來支持 JSX )。

如今 UI 是聲明式的,沒有使用任何框架。咱們添加新邏輯來改變狀態的同時,再也不須要編寫額外的代碼來保持 UI 同步。

結論

  1. 現代 JavaScript 框架解決的主要問題是保持 UI 與狀態同步。
  2. 使用原生 JavaScript 編寫複雜、高效而又易於維護的 UI 界面幾乎是不可能的。
  3. Web components 並無提供解決 UI 與狀態同步的方案。
  4. 使用現有的虛擬 DOM 庫去開發本身的框架並不困難,但不建議。

最後

若是你想進【大前端交流羣】,關注公衆號點擊「交流加羣」添加機器人自動拉你入羣。關注我第一時間接收最新干貨。

相關文章
相關標籤/搜索