<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          【React】1329- 一份 2.5k star 的《React 開發(fā)思想綱領(lǐng)》

          共 7131字,需瀏覽 15分鐘

           ·

          2022-05-24 08:56

          翻譯自:https://github.com/mithi/react-philosophies[1] 2.5k star
          原文作者:mithi[2]
          已獲作者授權(quán)

          概要

          1. 介紹
          2. 最低要求
          3. 面向幸福設(shè)計(jì)
          4. 性能優(yōu)化技巧
          5. 測(cè)試原則

          ?? 0. 介紹

          《React 開發(fā)思想綱領(lǐng)》是:

          • 我開發(fā) React 時(shí)的一些思考
          • 每當(dāng)我 review 他人或自己的代碼時(shí)自然而然會(huì)思考的東西
          • 僅僅作為參考和建議,并非嚴(yán)格的要求
          • 會(huì)隨著我的經(jīng)驗(yàn)不斷更新
          • 大多數(shù)技術(shù)點(diǎn)是基礎(chǔ)的重構(gòu)方法論SOLID 原則以及極限編程等思想的變體,僅僅是在 React 中的實(shí)踐而已 ??

          你可能會(huì)覺得我寫的這些非常基礎(chǔ)。但以下示例都來自一些復(fù)雜大型項(xiàng)目的線上代碼。

          《React 開發(fā)思想綱領(lǐng)》的靈感來源于我實(shí)際開發(fā)中遇到的各種場(chǎng)景。

          ?? 1. 最低要求

          1.1 計(jì)算機(jī)比你更「智能」

          1. 使用 ESLint 來靜態(tài)分析你的代碼,開啟 rule-of-hooksexhaustive-deps 這兩個(gè)規(guī)則來捕獲 React 錯(cuò)誤。
          2. 開啟 JS 嚴(yán)格模式吧,都 2202 年了。
          3. 直面依賴,解決在useMemo,useCallbackuseEffectexhaustive-deps 規(guī)則提示的 warning 或 error 問題??梢詫⒆钚碌闹祾煸?ref 上來保證這些 hook 在回調(diào)中拿到的都是最新的值,同時(shí)避免不必要的重新渲染。
          4. 使用 map 批量渲染組件時(shí),都加上 key
          5. 只在最頂層使用 hook,不要在循環(huán)、條件或嵌套語(yǔ)句中使用 hook。
          6. 理解不能對(duì)已經(jīng)卸載的組件執(zhí)行狀態(tài)更新的控制臺(tái)警告。
          7. 給不同層級(jí)的組件都添加錯(cuò)誤邊界(Error Boundary)來防止白屏,還可以用它來向錯(cuò)誤監(jiān)控平臺(tái)(比如 Sentry)上報(bào)錯(cuò)誤,并設(shè)置報(bào)警。
          8. 不要忽略了控制臺(tái)中打印的錯(cuò)誤和警告。
          9. 記得要 tree-shaking!
          10. 使用 Prettier 來保證代碼的格式化一致性!
          11. 使用 TypescriptNextJS這樣的框架來提升開發(fā)體驗(yàn)。
          12. 強(qiáng)烈推薦 Code Climate(或其他類似的)開源庫(kù)。這類工具會(huì)自動(dòng)檢測(cè)代碼異味(Code Smell,代碼中的任何可能導(dǎo)致深層次問題的癥狀),它可以促使我去處理項(xiàng)目里留下的技術(shù)債。

          1.2 Code is just a necessary evil

          譯者注:程序員的目標(biāo)是解決客戶的問題,代碼只是副產(chǎn)品

          1.2.1 先思考,再加依賴

          依賴加的越多,提供給瀏覽器的代碼就越多。捫心問問自己,你是否真的使用了某個(gè)庫(kù)的 feature?

          ?? ?你真的需要它嗎? 看看這些你可能不需要的依賴

          1. 你是否真的需要 Redux?有可能需要,但其實(shí) React 本身也是一個(gè)狀態(tài)管理庫(kù)。

          2. 你是否真的需要 Apollo clientApollo client 有許多很強(qiáng)大的功能,比如數(shù)據(jù)規(guī)范化。但使用的同時(shí)也會(huì)顯著提高包體積。如果你的項(xiàng)目使用的并非是 Apollo client 特有的 feature,可以考慮使用一些輕量的庫(kù)來替代,比如 react-querySWR(或者根本不用)。

          3. Axios 呢?Axios 是一個(gè)很棒的庫(kù),它的一些特性不容易通過原生的 fetch API 來復(fù)刻。但是如果使用 Axios 只是因?yàn)樗懈玫?API,完全可以考慮在 fetch 上做一層封裝(比如 redaxios 或自己實(shí)現(xiàn))。取決于你的 App 是否真正地使用了 Axios 的核心 feature。

          4. Decimal.js 呢?或許 Big.js 或者其他輕量的庫(kù)就足夠了。

          5. Lodash/underscoreJS呢?推薦你看看【你不需要系列之“你不需要 Lodash/Underscore”】[3]。

          6. MomentJS呢?【你不需要系列之“你不需要 Momentjs”】[4]。

          7. 你不需要為了主題(淺色/深色模式)而使用 Context,考慮下用 css 變量 代替。

          8. 你甚至不需要 Javascript,CSS 也足夠強(qiáng)大。【你不需要系列之“你不需要 JavaScript”】[5]

          1.2.2 不要自作聰明,提前設(shè)計(jì)

          "我們的軟件在未來會(huì)如何迭代?可能會(huì)這樣或者那樣,如果在當(dāng)下就開始往這些方向進(jìn)行代碼設(shè)計(jì),這就叫 future-proof(防過時(shí),面向未來編程)。"

          不要這樣搞! 應(yīng)該在面臨需求的時(shí)候再去實(shí)現(xiàn)相應(yīng)功能,而不是在你預(yù)見到可能需要的時(shí)候。代碼應(yīng)該越少越好!

          1.3 發(fā)現(xiàn)了就優(yōu)化它

          1.3.1 檢測(cè)代碼異味(Code Smell),并在必要時(shí)對(duì)其進(jìn)行處理。

          當(dāng)你意識(shí)到某個(gè)地方出現(xiàn)了問題,那就馬上處理掉。但如果當(dāng)前不容易修復(fù),或者沒有時(shí)間,那請(qǐng)至少添加一條注釋(FIXME 或者 TODO),附上對(duì)該問題的簡(jiǎn)要描述。來讓項(xiàng)目里的每個(gè)人都知道這里有問題,讓他們意識(shí)到當(dāng)他們遇到這樣的情況時(shí)也該這樣做。

          ?? 來看看這些容易發(fā)現(xiàn)的代碼異味

          • ? 定義了很多參數(shù)的函數(shù)或方法
          • ? 難以理解的,返回 Boolean 值的邏輯
          • ? 單個(gè)文件中代碼行數(shù)太多
          • ? 在語(yǔ)法上可能相同(但格式化可能不同)的重復(fù)代碼
          • ? 可能難以理解的函數(shù)或方法
          • ? 定義了大量函數(shù)或方法的類/組件
          • ? 單個(gè)函數(shù)或方法中的代碼行數(shù)太多
          • ? 具有大量返回語(yǔ)句的函數(shù)或方法
          • ? 不完全相同但代碼結(jié)構(gòu)類似的重復(fù)代碼(比如變量名可能不同)

          切記,代碼異味并不一定意味著代碼需要修改,它只是告訴你,你應(yīng)該可以想出更好的方式來實(shí)現(xiàn)相同的功能。

          1.3.2 無情的重構(gòu)。簡(jiǎn)單比復(fù)雜好。

          ???♀? 小技巧: 簡(jiǎn)化復(fù)雜的條件語(yǔ)句,最好能提前 return。

          ?? 提前 return 的示例

          #???不太好

          if?(loading)?{
          ??return?
          }?else?if?(error)?{
          ??return?
          }?else?if?(data)?{
          ??return?
          }?else?{
          ??throw?new?Error('This?should?be?impossible')
          }

          #???推薦

          if?(loading)?{
          ??return?
          }

          if?(error)?{
          ??return?
          }

          if?(data)?{
          ??return?
          }

          throw?new?Error('This?should?be?impossible')

          ???♀? 小技巧: 比起傳統(tǒng)的循環(huán)語(yǔ)句,鏈?zhǔn)降母唠A函數(shù)更優(yōu)雅

          如果沒有明顯的性能差異,盡量使用鏈?zhǔn)降母唠A函數(shù)(map, filter, find, findIndex, some等) 來代替?zhèn)鹘y(tǒng)的循環(huán)語(yǔ)句。

          1.4 你可以做的更好

          ???♀? 小技巧: 可以在 setState 時(shí)傳入回調(diào)函數(shù),所以沒必要把 state 作為一個(gè)依賴項(xiàng)

          你不用把 setStatedispatch 放在 useEffectuseCallback 這些 hook 的依賴數(shù)組中。ESLint 也不會(huì)給你提示,因?yàn)?React 已經(jīng)確保了它們不會(huì)出錯(cuò)。

          #???不太好
          const?decrement?=?useCallback(()?=>?setCount(count?-?1),?[setCount,?count])
          const?decrement?=?useCallback(()?=>?setCount(count?-?1),?[count])

          #???推薦
          const?decrement?=?useCallback(()?=>?setCount(count?=>?(count?-?1)),?[])

          ???♀? 小技巧: 如果你的 useMemouseCallback 沒有任何依賴,那你可能用錯(cuò)了

          #???不太好
          const?MyComponent?=?()?=>?{
          ???const?functionToCall?=?useCallback(x:?string?=>?`Hello?${x}!`,[])
          ???const?iAmAConstant?=?useMemo(()?=>?{?return?{x:?5,?y:?2}?},?[])
          ???/*?接下來可能會(huì)用到?functionToCall?和?iAmAConstant?*/
          }

          #???推薦
          const?I_AM_A_CONSTANT?=??{?x:?5,?y:?2?}
          const?functionToCall?=?(x:?string)?=>?`Hello?${x}!`
          const?MyComponent?=?()?=>?{
          ???/*?接下來可能會(huì)用到?functionToCall?和?I_AM_A_CONSTANT?*/
          }

          ???♀? 小技巧: 巧用 hook 封裝自定義的 context,會(huì)提升 API 可讀性

          它不僅看起來更清晰,而且你只需要 import 一次,而不是兩次。

          ? 不太好

          //?你每次需要?import?兩個(gè)變量
          import?{?useContext?}?from?'react';
          import?{?SomethingContext?}?from?'some-context-package';

          function?App()?{
          ??const?something?=?useContext(SomethingContext);?//?看起來?ok,但可以更好
          ??//?...
          }

          ? 推薦

          //?在另一個(gè)文件中,定義這個(gè)?hook
          function?useSomething()?{
          ??const?context?=?useContext(SomethingContext);
          ??if?(context?===?undefined)?{
          ????throw?new?Error('useSomething?must?be?used?within?a?SomethingProvider');
          ??}
          ??return?context;
          }

          //?你只需要?import?一次
          import?{?useSomething?}?from?'some-context-package';

          function?App()?{
          ??const?something?=?useSomething();?//?看起來會(huì)更清晰
          ??//?...
          }

          ???♀? 小技巧: 在寫組件之前,先思考該怎么用它

          設(shè)計(jì) API 很難,README 驅(qū)動(dòng)開發(fā)(RDD)是個(gè)很有用的辦法,可以幫助你設(shè)計(jì)出更好的 API。并不是說應(yīng)該無腦使用 RDD,但它背后的思想是很值得學(xué)習(xí)的。我自己發(fā)現(xiàn),在設(shè)計(jì)實(shí)現(xiàn)組件 API 之前,使用 RDD 通常比不用時(shí)設(shè)計(jì)地更好。

          ?? 2. 面向幸福設(shè)計(jì)

          太長(zhǎng)不看版

          1. ?? 通過刪除冗余的狀態(tài)來減少狀態(tài)管理的復(fù)雜性。
          2. ?? “傳遞香蕉,而不是拿著香蕉的大猩猩和整個(gè)叢林“(意思是組件要什么傳什么,不要傳大對(duì)象)。
          3. ?? 讓你的組件小而簡(jiǎn)單 —— 單一職責(zé)原則。
          4. ?? 復(fù)制比錯(cuò)誤的抽象要“便宜”的多(避免提早/不恰當(dāng)?shù)脑O(shè)計(jì))。
          5. 避免 prop 層層傳遞(又叫 prop 鉆取,prop drilling)。Context 不是解決狀態(tài)共享問題的銀彈。
          6. 將巨大的 useEffect 拆分成獨(dú)立的小 useEffect。
          7. 將邏輯提取出來都放到 hook 和工具函數(shù)中。
          8. useCallback, useMemouseEffect 依賴數(shù)組中的依賴項(xiàng)最好都是基本類型。
          9. 不要在 useCallback, useMemouseEffect 中放入太多的依賴項(xiàng)。
          10. 為了簡(jiǎn)單起見,如果你的狀態(tài)依賴其他狀態(tài)和上次的值,考慮使用 useReducer,而不是使用很多個(gè) useState。
          11. Context 不一定要放在整個(gè) app 的全局。把 Context 放在組件樹中盡可能低的位置。同樣的道理,你的變量,注釋和狀態(tài)(和普通代碼)也應(yīng)該放在靠近他們被使用的地方。

          ?? 2.1 刪除冗余的狀態(tài)來減少狀態(tài)管理的復(fù)雜性

          冗余的狀態(tài)指可以通過其他狀態(tài)經(jīng)過推導(dǎo)得到的狀態(tài),不需要單獨(dú)維護(hù)(類似 Vue computed),當(dāng)你有冗余的狀態(tài)時(shí),一些狀態(tài)可能會(huì)丟失同步性,在面對(duì)復(fù)雜交互的場(chǎng)景時(shí),你可能會(huì)忘記更新它們。

          刪除這些冗余的狀態(tài),除了避免同步錯(cuò)誤外,這樣的代碼也更容易維護(hù)和推理,而且代碼更少。

          ?? 2.2 “傳遞香蕉,而不是拿著香蕉的大猩猩和整個(gè)叢林“

          為了避免掉入這種坑,最好將基本類型(boolean, string, number 等)作為 props 傳遞。(傳遞基本類型也能更好的讓你使用 React.memo 進(jìn)行優(yōu)化)

          組件應(yīng)該僅僅只了解和它運(yùn)作相關(guān)的內(nèi)容就足夠了。應(yīng)該盡可能地與其他組件產(chǎn)生協(xié)作,而不需要知道它們是什么或做什么。

          這樣做的好處是,組件間的耦合會(huì)更松散,依賴程度會(huì)更低。低耦合更利于組件修改,替換和移除,而不會(huì)影響其他組件。

          ?? 2.3 讓你的組件小而簡(jiǎn)單

          什么是「單一職責(zé)原則」?

          一個(gè)組件應(yīng)該有且只有一個(gè)職責(zé)。應(yīng)該盡可能的簡(jiǎn)單且實(shí)用,只有完成其職責(zé)的責(zé)任。

          具有各種職責(zé)的組件很難被復(fù)用。幾乎不可能只復(fù)用它的部分能力,很容易與其他代碼耦合在一起。那些抽離了邏輯的組件,改起來負(fù)擔(dān)不大而且復(fù)用性更強(qiáng)。

          如何判斷一個(gè)組件是否符合單一職責(zé)?

          可以試著用一句話來描述這個(gè)組件。如果它只負(fù)責(zé)一個(gè)職責(zé),描述起來會(huì)很簡(jiǎn)單。如果描述中出現(xiàn)了“和“或“或”,那么這個(gè)組件很大概率不是單一職責(zé)的。

          檢查組件的 state,props 和 hooks,以及組件內(nèi)部聲明的變量和方法(不應(yīng)該太多)。問問自己:是否這些內(nèi)容必須組合到一起這個(gè)組件才能工作?如果有些不需要,可以考慮把它們抽離到其他地方,或者把這個(gè)大組件拆解成小組件。

          ?? 3. 性能優(yōu)化技巧

          1. 如果你覺得應(yīng)用速度慢,就應(yīng)該做一次基準(zhǔn)測(cè)試(benchmark)來證明。 "面對(duì)模凌兩可的情況,拒絕猜測(cè)。" 多使用 Chrome 插件 - React 開發(fā)者工具的 profiler!
          2. useMemo 主要用在大開銷的計(jì)算上。
          3. 如果你打算使用 React.memo, useMemo, 和 useCallback 來減少重新渲染,它們不該有過多的依賴項(xiàng),且這些依賴項(xiàng)最好都是基本類型。
          4. 確保你清楚代碼里 React.memo, useCallbackuseMemo 它們都是為了什么而使用的(是否真的能防止重新渲染?是否能證明在這些場(chǎng)景中真的可以顯著提高性能? Memoization 有時(shí)會(huì)起到反作用,所以需要關(guān)注?。?/section>
          5. 優(yōu)先修復(fù)慢渲染,再修復(fù)重新渲染。
          6. 把狀態(tài)盡可能地放在它被使用的地方,一方面讓代碼讀起來更順,另一方面,能讓你的 app 更快(state colocation(狀態(tài)托管))
          7. Context 應(yīng)該按邏輯分開,不要在一個(gè) provider 中管理多個(gè) value。如果其中某個(gè)值變化了,所有使用該 context 的組件(即便沒有用到這個(gè)值),都會(huì)重新渲染。
          8. 可以通過拆分 statedispatch 來優(yōu)化 context
          9. 了解下 lazy loading(懶加載)bundle/code splitting(代碼分割)。
          10. 長(zhǎng)列表請(qǐng)使用 tannerlinsley/react-virtual 或其它類似的庫(kù)。
          11. 包體積越小,app 越快。你可以使用 source-map-explorer 或者 @next/bundle-analyzer(用于 NextJS) 來進(jìn)行包體積分析。
          12. 關(guān)于表單的庫(kù),推薦使用 react-hook-forms,它在性能和開發(fā)體驗(yàn)各方面都做的比較好。

          ?? 4. 測(cè)試原則

          1. 測(cè)試應(yīng)該始終與軟件的使用方式相似。
          2. 確保不是在測(cè)試一些邊界細(xì)節(jié)(用戶不會(huì)使用,看不到甚至感知不到的內(nèi)容)。
          3. 如果你的測(cè)試不能讓你對(duì)自己的代碼產(chǎn)生信任,那測(cè)試就是無意義的。
          4. 如果你正在重構(gòu)某個(gè)代碼,且最后實(shí)現(xiàn)的功能都是完全一致的,其實(shí)幾乎不需要修改測(cè)試,而且可以通過測(cè)試結(jié)果來判定你正確的重構(gòu)了。
          5. 對(duì)于前端來說,不需要 100% 的測(cè)試覆蓋率,70% 就足夠了。測(cè)試應(yīng)該提升你的開發(fā)效率,雖然維護(hù)測(cè)試會(huì)暫時(shí)地阻塞你目前的開發(fā),但當(dāng)你不斷地增加測(cè)試,會(huì)在不同階段得到不同的回報(bào)。
          6. 我個(gè)人喜歡使用 JestReact testing library,Cypress,和 Mock service worker。

          End

          翻譯的不好,請(qǐng)大家見諒。如有任何想法,歡迎評(píng)論交流


          ?? 支持

          如果本文對(duì)你有幫助,點(diǎn)贊 ?? 支持下我吧,你的「贊」是我創(chuàng)作的動(dòng)力。

          關(guān)于我,目前是字節(jié)跳動(dòng)一線開發(fā),工作四年半,工作中使用 React,業(yè)余時(shí)間開發(fā)喜歡 Vue。

          平時(shí)會(huì)不定期對(duì)前端的工作思考與實(shí)踐進(jìn)行深度分享和總結(jié),公眾號(hào)「小李的前端小屋」,感謝關(guān)注~

          參考資料

          [1]

          https://github.com/mithi/react-philosophies: https://github.com/mithi/react-philosophies

          [2]

          mithi: https://github.com/mithi

          [3]

          【你不需要系列之“你不需要 Lodash/Underscore”】: https://github.com/you-dont-need/You-Dont-Need-Lodash-Underscore

          [4]

          【你不需要系列之“你不需要 Momentjs”】: https://github.com/you-dont-need/You-Dont-Need-Momentjs

          [5]

          【你不需要系列之“你不需要 JavaScript”】: https://github.com/you-dont-need/You-Dont-Need-JavaScript

          瀏覽 19
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  国产拍拍视频 | 天堂在线中文视频 | a在线免费观看视频 | 国产精品欧美久久久久久久久久久久 | 伊人网在线视频 |