摘要:寫在最前原文首發(fā)于作者的知乎專欄中間件思想遇見的靈感附,感興趣的同學(xué)可以知乎關(guān)注,進(jìn)行交流。其中,最重要的一個便是對多線程的支持。在中提出了工作線程的概念,并且規(guī)范出的三大主要特征能夠長時間運(yùn)行響應(yīng)理想的啟動性能以及理想的內(nèi)存消耗。
寫在最前
原文首發(fā)于作者的知乎專欄:React Redux 中間件思想遇見 Web Worker 的靈感(附demo),感興趣的同學(xué)可以知乎關(guān)注,進(jìn)行交流。
熟悉 React 技術(shù)棧的同學(xué),想必對 Redux 數(shù)據(jù)流框架并不陌生。其倡導(dǎo)的單向數(shù)據(jù)流等思想獨(dú)樹一幟,雖然樣板代碼會有一定程度上的增多,但是對于開發(fā)效率和調(diào)試效率的提高是顯著的。同時還帶來了很多諸如 “時間旅行”,“ undo/redo ” 等黑魔法。
其實(shí)這還只是表象。如果你深入去了解 Redux 的設(shè)計(jì)理念,探索中間件奧秘,玩轉(zhuǎn)高階 reducer 等等,迎接你的就會是另一扇門。透過它,函數(shù)式編程思想之光傾斜如注。
思想背景但是隨著這個 web app 復(fù)雜度的提升,數(shù)據(jù)計(jì)算量壓力徒增,你所設(shè)計(jì)的 Reducer 變得臃腫不堪。好吧,我們可以拆分 Reducer 使得代碼看上去更加舒服??墒怯?jì)算量呢?也許有一些“夢魘”,瓶頸般永遠(yuǎn)無法消除。
冥冥之中,“各種處理計(jì)算既然注定在同一時空,那么能否永遠(yuǎn)平行?”
曾幾何時,你是否聽說過 JS 單線程異步?聽說過瀏覽器卡頓或卡死?聽說過 60 fps?
其實(shí)一個很嚴(yán)峻的事實(shí)是:根據(jù) 60 fps 計(jì)算,每一幀留給我們 JS 執(zhí)行的時間為 16ms(甚至更少)。那么一旦當(dāng) Reducer 計(jì)算時間過長,必然會影響瀏覽器渲染。
多線程思路關(guān)于瀏覽器主線程、render queue、event loop、call stack 等內(nèi)容,本文不再復(fù)述,因?yàn)槔锩娴闹R完全都夠?qū)懸槐緯恕<俣ㄗx者對其有一二認(rèn)知,那么你也不難理解我們即將登場的救星—— Web Worker!
我們先來簡單認(rèn)識一下 web worker:
2008 年 W3C 制定出第一個 HTML5 草案開始,HTML5 承載了越來越多嶄新的特性和功能。其中,最重要的一個便是對多線程的支持。在 HTML5 中提出了工作線程(Web Worker)的概念,并且規(guī)范出 Web Worker 的三大主要特征:
能夠長時間運(yùn)行(響應(yīng));
理想的啟動性能;
以及理想的內(nèi)存消耗。
Work 線程可以執(zhí)行任務(wù)而不干擾用戶界面。
于是,腦洞大開,能否將我們的 Redux Reducer 計(jì)算狀態(tài)部分放進(jìn) Worker 線程中處理呢?
答案是肯定的。
那么要如何實(shí)施呢?
我們先來看一下經(jīng)典的 Redux workflow,如下圖:
如果要接入 Web Work,那么我們改動流程圖如下:
具體實(shí)現(xiàn)和一個demo當(dāng)然,有了思路,還需要在實(shí)戰(zhàn)中演練。
我使用 “N-皇后問題” 模擬大型計(jì)算,并且實(shí)現(xiàn)的 demo 中可以任意設(shè)置 n 值,增加計(jì)算耗時。
如果你不理解此算法也沒有關(guān)系,只需要知道N-皇后問題這個算法的計(jì)算耗時很長,且和 n 值相關(guān):n 越大,計(jì)算成本越大。
除了一個極其耗時的計(jì)算,頁面中還運(yùn)行這么幾個模塊,來實(shí)現(xiàn)復(fù)雜的渲染邏輯操作:
一個實(shí)時每16毫秒,顯示計(jì)數(shù)(每秒增加1)的 blinker 模塊;
一個定時每500毫秒,更新背景顏色的 counter 模塊;
一個永久往復(fù)運(yùn)動的 slider 模塊;
一個每16毫秒翻轉(zhuǎn)5度的 spinner 模塊
這些模塊都定時頻繁地更新 dom 樣式,進(jìn)行大量復(fù)雜的渲染計(jì)算。正常情況下,由于 JS 主線程進(jìn)行N-皇后計(jì)算,這些渲染過程都將被卡頓。
同時,我設(shè)置“N-皇后問題”的 n 值,來觀察在計(jì)算時這些模塊的表現(xiàn)(是否卡頓)。在不開啟 Work 線程的情況下,n 設(shè)置為13時,有 gif 圖,左半部分:
我們非常清晰地看到:由于瀏覽器 call stack 進(jìn)行 n=13 的皇后問題計(jì)算,而無法“按時”渲染,所以造成了這幾個模塊的卡頓,這些模塊都無法更新狀態(tài)。在這個卡頓過程中,用戶的任何事件(如點(diǎn)擊,敲鍵盤等)都無法被瀏覽器響應(yīng)。這就是用戶體會到的“慢”!
如果我把 n 值設(shè)置的大與13呢,比如24?
千萬不要這么做!因?yàn)槟愕臑g覽器會被卡死!我使用 Mac Pro 8G 內(nèi)存情況下,設(shè)置到14,瀏覽器就無法響應(yīng)了。
在開啟 Work 線程時,請參考上 gif 圖右半部分,幾個模塊的渲染絲毫不受影響。完美達(dá)到了我們的目的。
因?yàn)?Reducer 的超級耗時計(jì)算被放入 Worker 線程當(dāng)中,所以絲毫沒有影響瀏覽器的渲染和響應(yīng)。完全解決了用戶覺得“電腦慢”的問題。
看到了如此完美的對比,也許你想問 Web Worker 的兼容性如何呢?
總結(jié)其實(shí),這篇文章的意義并不在于這個 demo 和應(yīng)用。而是在啟發(fā)一種新的想法的同時,review 了很多 JS 當(dāng)中關(guān)鍵概念和基本知識。比如:單線程異步、宿主環(huán)境、60 fps、一個算法等等。
更值得一提的是,如果你去深入 demo 代碼,你更會發(fā)現(xiàn) Redux 設(shè)計(jì)精妙的思想,比如我們將 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);
當(dāng)然,Redux-Worker 這個中間件的實(shí)現(xiàn)原理更是巧妙,這里不再展開。感興趣的同學(xué)可以參考我的此項(xiàng)目 Github 倉庫。我 fork 了此庫源碼,并在核心邏輯加入了中文注釋,感興趣的同學(xué)可以關(guān)注。
我的其他關(guān)于 React 文章:
通過實(shí)例,學(xué)習(xí)編寫 React 組件的“最佳實(shí)踐”
React 組件設(shè)計(jì)和分解思考
[從 React 綁定 this,看 JS 語言發(fā)展和框架設(shè)計(jì)]()
React 服務(wù)端渲染如此輕松 從零開始構(gòu)建前后端應(yīng)用
做出Uber移動網(wǎng)頁版還不夠 極致性能打造才見真章
解析Twitter前端架構(gòu) 學(xué)習(xí)復(fù)雜場景數(shù)據(jù)設(shè)計(jì)
React Conf 2017 干貨總結(jié)1: React + ES next = ?
React+Redux打造“NEWS EARLY”單頁應(yīng)用 一個項(xiàng)目理解最前沿技術(shù)棧真諦
一個react+redux工程實(shí)例
Happy Coding!
PS:
作者Github倉庫 和 知乎問答鏈接
歡迎各種形式交流。
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://www.ezyhdfw.cn/yun/91385.html
摘要:現(xiàn)在關(guān)于最新版本新特性的宣傳講解已經(jīng)鋪天蓋地了。測試場景是反復(fù)操作數(shù)組,這個反復(fù)操作有所講究,我們計(jì)劃持續(xù)不斷地改變數(shù)組的某一項(xiàng)而不是整個數(shù)組的大范圍變動。代碼和性能測試在使用開發(fā)時,相信很多開發(fā)者在搭配函數(shù)式的狀態(tài)管理框架使用。 現(xiàn)在關(guān)于 React 最新 v16 版本新特性的宣傳、講解已經(jīng)鋪天蓋地了。你最喜歡哪一個 new feature?截至目前,組件構(gòu)建方式已經(jīng)琳瑯滿目。那么,...
摘要:前端日報精選理解的專題之偏函數(shù)譯理解事件驅(qū)動機(jī)制游戲開發(fā)前端面試中的常見的算法問題發(fā)布中文前端頁面?zhèn)鲄⑸袏y產(chǎn)品技術(shù)刊讀基礎(chǔ)系列二之實(shí)現(xiàn)大轉(zhuǎn)盤抽獎掘金指南眾成翻譯編程插入排序眾成翻譯源碼講解函數(shù)技術(shù)風(fēng)暴初體驗(yàn)個人文 2017-08-16 前端日報 精選 理解 JavaScript 的 async/awaitJavaScript專題之偏函數(shù)[譯]理解 Node.js 事件驅(qū)動機(jī)制Pokem...
摘要:我們來從設(shè)計(jì)思想上,和官方團(tuán)隊(duì)的回應(yīng)上,了解一下否決理由。此外,還有一個方法新的接口設(shè)計(jì)支持接收一個回調(diào)函數(shù),當(dāng)其子組件掛載時,這個回調(diào)函數(shù)就會相應(yīng)觸發(fā)。 從 setState 那個眾所周知的小秘密說起... 在 React 組件中,調(diào)用 this.setState() 是最基本的場景。這個方法描述了 state 的變化、觸發(fā)了組件 re-rendering。但是,也許看似平常的 th...
摘要:我們來從設(shè)計(jì)思想上,和官方團(tuán)隊(duì)的回應(yīng)上,了解一下否決理由。此外,還有一個方法新的接口設(shè)計(jì)支持接收一個回調(diào)函數(shù),當(dāng)其子組件掛載時,這個回調(diào)函數(shù)就會相應(yīng)觸發(fā)。 從 setState 那個眾所周知的小秘密說起... 在 React 組件中,調(diào)用 this.setState() 是最基本的場景。這個方法描述了 state 的變化、觸發(fā)了組件 re-rendering。但是,也許看似平常的 th...
摘要:表示調(diào)用棧在下一將要執(zhí)行的任務(wù)。兩方性能解藥我們一般有兩種方案突破上文提到的瓶頸將耗時高成本高易阻塞的長任務(wù)切片,分成子任務(wù),并異步執(zhí)行這樣一來,這些子任務(wù)會在不同的周期執(zhí)行,進(jìn)而主線程就可以在子任務(wù)間隙當(dāng)中執(zhí)行更新操作。 showImg(https://segmentfault.com/img/remote/1460000016008111); 性能一直以來是前端開發(fā)中非常重要的話題...
閱讀 4446·2021-09-24 09:47
閱讀 1257·2021-09-03 10:33
閱讀 2135·2019-08-30 11:13
閱讀 1081·2019-08-30 10:49
閱讀 1808·2019-08-29 16:13
閱讀 2103·2019-08-29 11:28
閱讀 3147·2019-08-26 13:31
閱讀 3699·2019-08-23 17:14