寫在最前
原文首發(fā)于作者的知乎專欄:React Redux 中間件思想遇見 Web Worker 的靈感(附demo)输虱,感興趣的同學(xué)可以知乎關(guān)注,進行交流窗怒。
熟悉 React 技術(shù)棧的同學(xué)写半,想必對 Redux 數(shù)據(jù)流框架并不陌生。其倡導(dǎo)的單向數(shù)據(jù)流等思想獨樹一幟埃难,雖然樣板代碼會有一定程度上的增多,但是對于開發(fā)效率和調(diào)試效率的提高是顯著的涤久。同時還帶來了很多諸如 “時間旅行”涡尘,“ undo/redo ” 等黑魔法。
其實這還只是表象响迂。如果你深入去了解 Redux 的設(shè)計理念考抄,探索中間件奧秘,玩轉(zhuǎn)高階 reducer 等等蔗彤,迎接你的就會是另一扇門川梅。透過它,函數(shù)式編程思想之光傾斜如注然遏。
思想背景
但是隨著這個 web app 復(fù)雜度的提升贫途,數(shù)據(jù)計算量壓力徒增,你所設(shè)計的 Reducer 變得臃腫不堪待侵。好吧丢早,我們可以拆分 Reducer 使得代碼看上去更加舒服。可是計算量呢怨酝?也許有一些“夢魘”傀缩,瓶頸般永遠無法消除。
冥冥之中农猬,“各種處理計算既然注定在同一時空赡艰,那么能否永遠平行?”
曾幾何時斤葱,你是否聽說過 JS 單線程異步瞄摊?聽說過瀏覽器卡頓或卡死?聽說過 60 fps苦掘?
其實一個很嚴峻的事實是:根據(jù) 60 fps 計算,每一幀留給我們 JS 執(zhí)行的時間為 16ms(甚至更少)楔壤。那么一旦當 Reducer 計算時間過長鹤啡,必然會影響瀏覽器渲染。
多線程思路
關(guān)于瀏覽器主線程蹲嚣、render queue递瑰、event loop、call stack 等內(nèi)容隙畜,本文不再復(fù)述抖部,因為里面的知識完全都夠?qū)懸槐緯恕<俣ㄗx者對其有一二認知议惰,那么你也不難理解我們即將登場的救星—— Web Worker!
我們先來簡單認識一下 web worker:
2008 年 W3C 制定出第一個 HTML5 草案開始慎颗,HTML5 承載了越來越多嶄新的特性和功能。其中言询,最重要的一個便是對多線程的支持俯萎。在 HTML5 中提出了工作線程(Web Worker)的概念,并且規(guī)范出 Web Worker 的三大主要特征:
- 能夠長時間運行(響應(yīng))运杭;
- 理想的啟動性能夫啊;
- 以及理想的內(nèi)存消耗。
Work 線程可以執(zhí)行任務(wù)而不干擾用戶界面辆憔。
于是撇眯,腦洞大開,能否將我們的 Redux Reducer 計算狀態(tài)部分放進 Worker 線程中處理呢虱咧?
答案是肯定的熊榛。
那么要如何實施呢?
我們先來看一下經(jīng)典的 Redux workflow彤钟,如下圖:
如果要接入 Web Work来候,那么我們改動流程圖如下:
具體實現(xiàn)和一個demo
當然,有了思路逸雹,還需要在實戰(zhàn)中演練营搅。
我使用 “N-皇后問題” 模擬大型計算云挟,并且實現(xiàn)的 demo 中可以任意設(shè)置 n 值,增加計算耗時转质。
如果你不理解此算法也沒有關(guān)系园欣,只需要知道N-皇后問題這個算法的計算耗時很長,且和 n 值相關(guān):n 越大休蟹,計算成本越大沸枯。
除了一個極其耗時的計算,頁面中還運行這么幾個模塊赂弓,來實現(xiàn)復(fù)雜的渲染邏輯操作:
- 一個實時每16毫秒绑榴,顯示計數(shù)(每秒增加1)的 blinker 模塊;
- 一個定時每500毫秒盈魁,更新背景顏色的 counter 模塊翔怎;
- 一個永久往復(fù)運動的 slider 模塊;
- 一個每16毫秒翻轉(zhuǎn)5度的 spinner 模塊
這些模塊都定時頻繁地更新 dom 樣式杨耙,進行大量復(fù)雜的渲染計算赤套。正常情況下,由于 JS 主線程進行N-皇后計算珊膜,這些渲染過程都將被卡頓容握。
同時,我設(shè)置“N-皇后問題”的 n 值车柠,來觀察在計算時這些模塊的表現(xiàn)(是否卡頓)剔氏。在不開啟 Work 線程的情況下,n 設(shè)置為13時堪遂,有 gif 圖介蛉,左半部分:
我們非常清晰地看到:由于瀏覽器 call stack 進行 n=13 的皇后問題計算,而無法“按時”渲染溶褪,所以造成了這幾個模塊的卡頓币旧,這些模塊都無法更新狀態(tài)。在這個卡頓過程中猿妈,用戶的任何事件(如點擊吹菱,敲鍵盤等)都無法被瀏覽器響應(yīng)。這就是用戶體會到的“慢”彭则!
如果我把 n 值設(shè)置的大與13呢鳍刷,比如24?
千萬不要這么做俯抖!因為你的瀏覽器會被卡死输瓜!我使用 Mac Pro 8G 內(nèi)存情況下,設(shè)置到14,瀏覽器就無法響應(yīng)了尤揣。
在開啟 Work 線程時搔啊,請參考上 gif 圖右半部分,幾個模塊的渲染絲毫不受影響北戏。完美達到了我們的目的负芋。
因為 Reducer 的超級耗時計算被放入 Worker 線程當中,所以絲毫沒有影響瀏覽器的渲染和響應(yīng)嗜愈。完全解決了用戶覺得“電腦慢”的問題旧蛾。
看到了如此完美的對比,也許你想問 Web Worker 的兼容性如何呢蠕嫁?
總結(jié)
其實锨天,這篇文章的意義并不在于這個 demo 和應(yīng)用。而是在啟發(fā)一種新的想法的同時剃毒,review 了很多 JS 當中關(guān)鍵概念和基本知識绍绘。比如:單線程異步、宿主環(huán)境迟赃、60 fps、一個算法等等厂镇。
更值得一提的是纤壁,如果你去深入 demo 代碼,你更會發(fā)現(xiàn) Redux 設(shè)計精妙的思想捺信,比如我們將 Web Worker 的應(yīng)用抽象出一個公共庫:Redux-Worker酌媒,并包裝為 Redux 的中間件(middleware),所有 React Redux 都可以無侵入迄靠,采用中間件的思想使用:
import { applyWorker } from 'redux-worker';
const enhancerWithWorker = compose(
applyMiddleware(thunk, logger),
applyWorker(worker)
);
const store = createStore(rootReducer, {}, enhancerWithWorker);
當然秒咨,Redux-Worker 這個中間件的實現(xiàn)原理更是巧妙,這里不再展開掌挚。感興趣的同學(xué)可以參考我的此項目 Github 倉庫雨席。我 fork 了此庫源碼,并在核心邏輯加入了中文注釋吠式,感興趣的同學(xué)可以關(guān)注陡厘。
我的其他關(guān)于 React 文章:
- 通過實例,學(xué)習(xí)編寫 React 組件的“最佳實踐”
- React 組件設(shè)計和分解思考
- 從 React 綁定 this特占,看 JS 語言發(fā)展和框架設(shè)計
- React 服務(wù)端渲染如此輕松 從零開始構(gòu)建前后端應(yīng)用
- 做出Uber移動網(wǎng)頁版還不夠 極致性能打造才見真章
- 解析Twitter前端架構(gòu) 學(xué)習(xí)復(fù)雜場景數(shù)據(jù)設(shè)計
- React Conf 2017 干貨總結(jié)1: React + ES next = ?
- React+Redux打造“NEWS EARLY”單頁應(yīng)用 一個項目理解最前沿技術(shù)棧真諦
- 一個react+redux工程實例
Happy Coding!