當前位置:
首頁 > 最新 > 用上古思想寫現代前端

用上古思想寫現代前端

原文:Code your JS app like it』s 86

原文作者:Victor Perron

原文創作時間:2016-09-28

翻譯時間: 2017-01-10

翻譯作者:無法畢業的fulvaz

譯者註:

這篇文章非常有趣,作者介紹了利用復古的的圖形界面設計模式實現組件間解耦。但說起來古老,實際上說明了flux做了什麼事情,以及為什麼要有flux。

正文

利用過去的編程範式可以避免重寫你的JavaScript應用。

太多時間花在了重寫UI上面

我們會出於不同的原因去重寫UI。

通常來說,我們會把原因歸結到工具上 —- 這很合理。

框架,庫,包管理,guidelines,甚至是語言和元語言本身都變化地非常快,所以現在很難找到一個做應用的普遍適用標準。

更別提測試和測試的方法,那可以另外寫一篇文章進行討論。硬要進行測試,也只是靠眼睛去看他是否可以正常工作。

大多數情況,UI都會連同產品一起發布,用戶會花錢買,然後在一個重寫了資料庫的關鍵更新後,用戶會花錢再買一次。

整個行業的公司都會招新人畢業生,然後叫他們用最新的工具,在最短的時間組裝出Web UI,最後,把UI作為產品的一部分賣掉。

最大的問題在於,沒人會去考慮UI的長期支持(LTS)和維護 — 這裡的工作量差不多是要建一個生態系統。

我們可以做出改變。我們可以建立一個標準。我們可以專註於寫Web UI時出現頻率高的問題,然後解決這些問題。而且我們可以用古老的設計模式和編碼策略去解決這些問題。

三個錯誤

下面看一個簡單的React component例子。

// FooComponent.js import react from react import from ../api_client var FooComponent = React.createClass({ componentDidMount: function(){ ApiClient.getTitle().then((data) => this.setState()) }, handleClick: function(e){ e.preventDefault(); bar_component.resetCoconuts() }, render: function(){ return (

/>

) } })

特別簡單的一個例子,你可以看出上面代碼有什麼問題嗎?

不恰當的導入和數據流

當應用的邏輯分散在你的組件和控制器中時,你也只能毫無辦法地使用 去組織你的代碼。我用還是使用上面的例子,然後用其他方式重寫這段代碼。

首先,代碼中先導入了

import from ../api_client

這段代碼將你的組件和數據源耦合了起來。這不是一種好的實踐。這種設計至少有3個問題。

修改 會導致 跟著也要修改

測試 需要一個mock的 :比如一個HTTP後端

如果(在頁面內)同時有兩個 ,那這個頁面會想伺服器提交兩次請求。

但這些都不是主要問題。

主要問題是:這個API client還沒有初始化。如果它需要一個base URL或者是一個token呢?

你的組件就需要去給這個API client提供參數。意思是你需要給這個組件提供一些選項的參數,然而這樣就違反了關注點分離原則。

const myComponent = FooComponent.bootstrap( #anchor , { baseUrl: "https://xxxx", token: "MY_TOKEN", actualOption: xxxx, })

這段代碼中至少有兩個非必要的參數( 和 )。這兩個參數需要在測試的時候mock。

你有見過組件需要傳URL才能工作嗎?組件只需要數據。

這個組件依賴不可見的全局變數

其次,這段代碼還依賴了全局的 去處理點擊事件和重置 。 這種寫法非常不好。

handleClick: function (e){ e.preventDefault(); bar_component.resetCoconuts() },

imports裡面沒任何東西可以提醒我們,這個組件依賴著在 對象的隱式全局變數 .

另外,問題並不僅是因為它是全局的,這種寫法還會引起其他的bug。比如說, 在 函數的作用域中被定義了。(譯註:那麼bar_component重置的Cocount就是另外一個Cocount了)

下面列出了不同等級的麻煩:

Level 1:你不能在沒有 的情況下測試 .

Level 10: 並不僅是一個依賴,它需要進行實例化

Level 9001:這個實例需要保存在一個全局範圍內。而不能通過顯式聲明,top-level, automatically-retrievable,或者是導入等方式來使用這個實例。

譯註1:

此處top-level意思是通過查詢依賴鏈的根部然後使用實例。

譯註2:automatically-retrievable:自動查詢依賴,RPM的自動查詢依賴

對AngularJS用戶:遇到這種情況的最常見原因是在 內注入 或者

當然,這些依賴都是可以mock的。(用angular特定的方式) 即便如此,他們依然是需要在定義在某處的全局變數。

他們間產生了耦合。

數據流不明確

一個非常常見的問題:代碼各處都是數據查詢(XHR,JSONP)

var FooComponent = React.createClass({ componentDidMount: function(){ ApiClient.getTitle().then((data) => this.setState()) }, // […] })

這段代碼中,除了我們剛才的耦合問題,還有數據流不清晰的問題,即你沒法清晰地看出HTTP請求在哪,什麼時候發出。

更糟糕的是,你部分組件可能依賴於未更新的請求(API可能會改變),而你的應用的其他部分依賴更新過的API請求。

如果通過XHR獲得的(JSON內的) 屬性發生了改變,你就要在組件內部修改才能修正你的組件,這看起來並不太對。

這堆錯誤的實踐累加起來,最終就會導致不久後的重寫。你需要的是嚴格的關注點分離。實現的方法是提前計劃好,儘可能準確地預估你的應用是做什麼的,數據流是怎麼樣的。

然而,這裡還有另一個範式。

「main loop」

下面從介紹一個古老的設計模式開始。

回憶你寫代碼最開始要做什麼:打開一個編輯器,創建一個 循環,然後在某處輸出『Hello World』

這看起來很簡單,但在遊戲和桌面軟體領域,main循環是相關邏輯的骨架。

在Windows API中

下面的簡單代碼來自Windows API例子:

LRESULT APIENTRY WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { PAINTSTRUCT ps; HDC hdc; switch (message) { case WM_PAINT: hdc = BeginPaint(hwnd, &ps); TextOut(hdc, 0, 0, "Hello, Windows!", 15); EndPaint(hwnd, &ps); return 0L; // Process other messages. } } int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { HWND hwnd; hwnd = CreateWindowEx( // parameters ); ShowWindow(hwnd, SW_SHOW); UpdateWindow(hwnd); return msg.wParam; }

非常復古的代碼,對吧?

分析:WinMain函數使用幾個參數創建了應用的window。其中WndProc回調函數負責處理各種事件:用戶事件,重繪事件等等

一個main循環,一個事件循環。

就是這樣。生存了20年,擁有數百萬的應用

的Windows生態系統,只是基於簡單的main函數和事件循環。

SDL API里

SDL API主要用來設計遊戲。經常被當做輕量級的OpenGL。

下面是一個簡單的app

#include #include int main() { SDL_Window*handle(0); SDL_Event events; bool end(false); if(SDL_Init(SDL_INIT_VIDEO)

不同的生態系統,但是東西還是那套東西。一個main入口把事情初始化了,一個事件循環負責處理用戶輸入和其他東西。

OpenGL應用極度簡單,你可在這這裡找到例子。

不敢相信對吧,就是main循環,初始化。這就是我們今天要說的:幾乎所有類型的UI都是基於一個事件循環,然後應用的不同部分觸發不同的事件。

那麼,為什麼大部分前端應用不使用同樣的方法呢?

因為沒人告訴過我們可以這麼用。我們習慣用了jQuery,然後慢慢開始組建Angular組件,訪問不知道定義在哪的全局變數。

Javascript應用:Main loop model

我們已經知道了非常簡單的Windows API例子、對遊戲開發友好的OpenGL和SDL庫。

在某種程度上說,一個web界面就是一個簡單的圖形應用。不同的是它用的是更現代的工具。

如果我們將我們的應用寫成這樣子

// main.js import from ./api_client import from ./components/foo import from ./components/bar // Init the components FooComponent.bootstrap($( #foo_component ), options) const bar = new BarComponent(document.getElementByID( #bar_component ))) // Get the data const api = ApiClient.authenticate(getTokenFromStorage()) api.fetchCoconuts(function(coconuts){ // Handle-based data passing bar.setCoconuts(coconuts) // Event-based data passing document.dispatchEvent(new Event( data_is_fetched , coconuts)) }) api.fetchTitle(function(data){ foo.setTitle(data.title) }) // Event loop document.addEventListener( foo:click , function(){ alert( Foo component was clicked ) bar.resetCoconuts() })// FooComponent.js import react from react var FooComponent = React.createClass({ handleClick: function(e){ e.preventDefault(); // Notify other layers (simplistic, but works) document.dispatchEvent(new Event( foo:click )) }, setTitle: function(str){ this.setState()) }, render: function(){ return (

我們來認真看看上述代碼,特別是

我們獲得了解耦和的組件,FooComponent和BarComponent都不需要知道數據請求的細節。

他們不需要知道,也不想知道。

他們所需要的是,請求自一個hardcoded fixture的 , 關鍵的是, 對外暴露一個非常簡單的 API函數。任何應用邏輯都可以在外部使用它。

事件在代碼中廣播(嫌low使用event bus也可以),不同的組件可以捕獲事件然後進行處理。foo顯式發送點擊事件,在應用中監聽這個事件,就可以實現通過點擊 然後重置 的count。

這樣,組件就真正地解除了耦合。他們可以隨意重用,而不需要知道組件內部是如何實現的。

只有main循環裡面需要寫與app相關的業務邏輯。

註:這裡沒有使用全局變數,我們在main循環中利用了閉包。

總結

文中說的幾個原則其實非常簡單。在你的應用中使用這種簡單的代碼結構,然後繼續使用這個代碼結構。

儘管如此,我們還是看到了許多錯誤的設計模式。(或者根本沒有設計模式)他們甚至連改善設計模式的想法都沒有。

在Polyconseil公司,我們使用了就像上文中說的,經歷時間洗禮的方法。我們使用了 ,而不是 , , ,詳見

我們下次會討論其他的編程範式,路由,還有一堆我們在Polyconseil和其他地方遇到的常見陷阱。

譯註:你可能發發現這些解除耦合的方法很像:Flux

- EOF -

喜歡這篇文章嗎?立刻分享出去讓更多人知道吧!

本站內容充實豐富,博大精深,小編精選每日熱門資訊,隨時更新,點擊「搶先收到最新資訊」瀏覽吧!


請您繼續閱讀更多來自 推酷 的精彩文章:

ECMAScript 2018 標準導讀
堅果智能影院新品的廬山真面目?來跟我終極探秘
學習網頁設計需要懂什麼知識?難學嗎?
為什麼你只能是知識的「快遞員」?
全棧開發——動手打造屬於自己的直播間

TAG:推酷 |

您可能感興趣

孔子為什麼現在仍然在引用他的思想?
思想上的努力
史上最受爭議的皇帝,把現代的思想帶到古代,結果頭顱被敵軍收藏
古希臘羅馬倫理思想 的現代轉化及啟示
孔子儒家的思想是否還有現代價值?
清代女性詩詞的空前繁榮,時代大背景下的思想解放
思想
農閑,就把思想掛上山牆
經常說「思想」,何謂「思想」
做有思想的演講者
論「物盡其用」思想對景德鎮現代陶瓷設計的影響
「人妖」在這個朝代被看做正常人,古代人的思想你跟不上
人類和語言出現之前 一些重要的概念是如何起源的——《思想史》
他是革命精神的代言人,最後卻為自己加冕:拿破崙與現代戰略思想
思想念頭是如何解決的
我國古代有「天上一天地上一年」概念,這不就是相對論的思想嗎?
一代畫壇宗師,近現代畫家的集體偶像,石濤思想上的高度讓人仰望
易經乾卦的思想啟示
3.古希臘思想死於現代藝術
清代的邊防思想是什麼?和之前的歷朝歷代相比又有什麼變化?