近幾年可謂是 JavaScript 的大爆炸紀元,各類框架類庫層出不窮,它們給前端帶來一個又一個的新思想。從之前咱們用的 jQuery 直接操做 DOM,到 BackboneJS、Dojo 提供監聽器的形式,在到 Ember.js、AngularJS 數據綁定的理念,再到如今的 React、Vue 虛擬 DOM 的思想。都是在當前 Web 應用日益複雜的時代,對於如何處理「應用狀態」與「用戶界面」之間如何更新的問題,帶來更先進的解決方案。javascript
本文是一篇從技術上,以數據變動和UI同步爲方向,按部就班的講述 JavaScript 框架如何演進過來的。html
本篇文章,給了我一個更加高緯度的視角,來看待 JavaScript 這些個框架。前端
在 2015 年,JavaScript 框架的選擇並很多。在 Angular,Ember,React,Backbone 以及它們衆多的競爭者中,有足夠多的選擇。java
雖然能夠經過很多方面來對比這些框架的不一樣,可是最讓人感興趣的是它們分別如何管理狀態(state)的。特別的,經過思考這些框架分別如何處理狀態變化是頗有用的。它們都提供了什麼樣的工具讓你把這些變化呈現給用戶? 後端
如何處理應用狀態(app state)與用戶界面(user interface)之間的同步,長期以來都是用戶界面開發如此複雜的主要緣由。如今,咱們有幾個不一樣的處理方案。本文探索如下:Ember 的數據綁定,Angular 的髒檢查、React 的虛擬DOM以及它與不可變數據結構(immutable data structures)之間的聯繫。瀏覽器
咱們首先討論程序內部的狀態與屏幕所看到的內容之間的映射。你把各類諸如 object,arrays,strings,以及 numbers 轉換成一顆由諸如 texts、forms、links、buttons 和 images 組成的樹狀結構。在 Web 中,前者一般指 JavaScript 中的數據結構,然後者指的是 DOM (Document Object Model)服務器
咱們常常稱這個過程爲渲染(rendering),你能夠想象這個過程是從數據模型到用戶界面的一個映射。當你把數據渲染成一個模板,你獲得的是一個 DOM(或者說 HTML)。數據結構
這個過程自己已經足夠簡單了,數據模型到用戶界面之間的映射,並不老是那麼的瑣碎。它基本只是一個接受輸入而後直接輸出的函數。app
在咱們須要考慮數據開始隨着時間而變化的時候,這件事就變得更有挑戰性了。當用戶進行操做或者其它某些操做致使數據產生變化的時候,用戶界面須要呈現出這些變化。並且,因爲從新構建 DOM 樹的代價是極其昂貴的,咱們要儘量產生小的影響。框架
由於狀態產生了變化,這比只是一次性渲染用戶界面變得更加難。這就到了如下解決方案開始表演的時候了。
宇宙是永恆不變的,沒有任何變化
在 JavaScript 新紀元以前,你的 Web 應用的任何交互都會觸發一趟服務器的環繞旅行。每個點擊和每個表單提交都會卸載當前頁面,一個請求發送到服務器,服務器響應一個新的頁面,而後瀏覽器從新渲染。
這種方式不須要前端管理任何的狀態(state)。就前端範疇而言,當一些事情發生了(後端返回的數據),整個過程就結束了。就算有狀態,那也只是後端的範疇。前端只是由 HTML 和 CSS 構成,也許有時候會有些 JavaScript 撒在表面調味。
從前端來講,這是一個很簡單的實現方式,但也是一個很慢的方式。每個交互並不只僅觸發UI的重渲染,還涉及服務器的數據查詢以及服務端渲染。
大多數人已經再也不這樣作了,咱們能夠在服務器端初始化咱們的應用,而後轉移到前端來作狀態的管理(這也是 isomorphic JavaScript 致力於的。)。已經有人在相似的更復雜的設計思想中取得成功。
我不知道哪些須要渲染的,你來告訴我。
第一代革命的 JavaScript 框架,如:Backbone.js, Ext JS 以及 Dojo。第一次在瀏覽器端引入了數據模型(Data Model)的概念,代替了之前那些直接操做 DOM 的輕量級的腳本代碼。這意味着你終於能夠在瀏覽器端管理狀態了。當數據模型的上下文改變時,你須要作一些工做,讓改變呈如今用戶界面中。
這些框架的體系能分離你的模型和界面代碼,但同時也留下了一大部分同步的工做給你。你能夠監聽某類事件的發生,可是你有義務去計算如何從新渲染以及如何落實到用戶界面中。
基於這種模型,做爲開發者,你須要考慮大量的性能問題。因爲你能控制何時和怎麼處理更新,你能夠從中作任意的作一些調整。這常常會面臨一些權衡:簡單的處理致使大面積的頁面更新,或者強性能的處理來更新一小塊頁面。
因爲我在控制你的模型和試圖,我會確切知道如何從新渲染。
當應用狀態改變的時候,手動處理渲染工做,無可避免的增長了複雜度。不少框架旨在解決這個問題,Ember.js 就是其中之一。
Ember,像 Backbone 同樣,當數據模型改變的時候會觸發某個事件。不一樣之處在於 Ember 同時提供了一些方法來接收這些事件。你能夠把 UI 綁定到數據模型中,這意味着有一個監聽器綁定到了 UI 上。該監聽器當收到事件的時候,知道如何更新 UI。
這是一個高效率的機制。儘管設置所有的監聽器須要在初始化時多出一些工做,可是以後就能保證同步狀態時的最小影響。當狀態產生變化時, 只有真正須要更新的部分纔會發生改變。
這種方式最大的犧牲是 Ember 須要時刻盯着數據模型。這意味着你須要經過 Ember 的 API 封裝你的數據,以及你要更新數據的時候是使用 foo.set('x',42)
而不是 foo.x = 42
,以此類推。
在將來 ES6 的 Proxies 可能會對這種模式產生必定的幫助。它讓 Ember 能夠經過裝飾 object 來綁定那些監聽器的代碼。這就不用像傳統方式那樣重寫 object 的 setter 方法了。