<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知識點梳理

          共 8511字,需瀏覽 18分鐘

           ·

          2022-04-16 01:57

          本文適合對React知識點存在疑惑的小伙伴閱讀

          歡迎關(guān)注前端早茶,與廣東靚仔攜手共同進階~

          一、前言

          最近交流群里有不少小伙伴因為各種原因,不得已要尋找新的機會。廣東靚仔這里收集了一些關(guān)于React的知識點,希望對小伙伴們有所幫助。
          相信大家也都知道最近React18.0.0新發(fā)布的熱潮,建議小伙伴們可以看看,說不定對尋找新機會路上有所幫助。


          二、正文

          react 生命周期函數(shù)有哪些

          componentWillMount?組件渲染之前調(diào)用
          componentDidMount?在第一次渲染之后調(diào)用
          componentWillReceiveProps?在組件接收到一個新的 props 時調(diào)用
          shouldComponentUpdate?判斷組件是否更新 html
          componentWillupdate?組件即將更新 html 時調(diào)用
          componentDidupdate?在組件完成更新后立即調(diào)用
          componentWillUnmount?在組件移除之前調(diào)用

          React生命周期有哪些坑,如何避免

          1. getDerivedStateFromProps 容易編寫反模式代碼,使受控組件與非受控組件區(qū)分模糊。

          2. componentWillMount 在 React 中已被標記棄用,不推薦使用,主要原因是新的異步渲染架構(gòu)會導致它被多次調(diào)用。所以網(wǎng)絡請求及事件綁定代碼應移至 componentDidMount 中。

          3. componentWillReceiveProps 同樣被標記棄用,被 getDerivedStateFromProps 所取代,主要原因是性能問題(componentWillReceiveProps 更新狀態(tài)是同步進行的,而 getDerivedStateFromProps ?的設計更適用于異步場景。)。

          4. shouldComponentUpdate 通過返回 true 或者 false 來確定是否需要觸發(fā)新的渲染。主要用于性能優(yōu)化。

          5. componentWillUpdate 同樣是由于新的異步渲染機制,而被標記廢棄,不推薦使用,原先的邏輯可結(jié)合

          getSnapshotBeforeUpdatecomponentDidUpdate 改造使用。

          6. 如果在 componentWillUnmount 函數(shù)中忘記解除事件綁定,取消定時器等清理操作,容易引發(fā) bug。

          7. 如果沒有添加錯誤邊界處理,當渲染發(fā)生異常時,用戶將會看到一個無法操作的白屏,所以一定要添加。

          React 的請求應該放在哪里,why?

          對于異步請求,應該放在 componentDidMount 中去操作。從時間順序來看,除了 componentDidMount 還可以有以下選擇:

          constructor:可以放,但從設計上而言不推薦。constructor 主要用于初始化 state 與函數(shù)綁定,并不承載業(yè)務邏輯。而且隨著類屬性的流行,constructor 已經(jīng)很少使用了。

          componentWillMount:已被標記廢棄,在新的異步渲染架構(gòu)下會觸發(fā)多次渲染,容易引發(fā) Bug,不利于未來 React 升級后的代碼維護。

          所以React 的請求放在 componentDidMount 里是最好的選擇。

          hooks使用中有什么要注意的?

          1.?useState--不使用函數(shù)就set依賴之前狀態(tài)的的狀態(tài)
          //?initialState
          const?[visible,?setVisible]?=?useState(false)
          //?modify?the?state
          //?bad?
          setVisible(!visible)
          //?good
          setVisible(visible?=>?!visible)
          2.?useState--它的setter方法其實是異步的
          3. 有時候使用hook莫名其妙組件卡頓了,這個時候可以考慮用useMemo
          4.區(qū)分好useCallbackuseMemo
          一個是「緩存函數(shù)」, 一個是緩存「函數(shù)的返回值」
          5. 在組件內(nèi)部,會成為其他useEffect依賴項的方法,建議用?useCallback?包裹,或者直接編寫在引用它的useEffect中。
          ?6. 如果function會作為props傳遞給子組件,一定要使用?useCallback?包裹
          7. 使用高階函數(shù)的場景,建議使用?useMemo

          類組件與函數(shù)組件有什么區(qū)別?

          共同點:

          它們的實際用途是一樣的,無論是高階組件,還是異步加載,都可以用它們作為基礎(chǔ)組件展示 UI。因為組件是 React 的最小編碼單位,所以無論是函數(shù)組件還是類組件,在使用方式和最終呈現(xiàn)效果上都是完全一致的。

          不同點:

          基礎(chǔ)認識

          類組件與函數(shù)組件本質(zhì)上代表了兩種不同的設計思想與心智模式。類組件的根基是 OOP(面向?qū)ο缶幊蹋运欣^承、有屬性、有內(nèi)部狀態(tài)的管理。

          函數(shù)組件的根基是 FP,也就是函數(shù)式編程。它屬于“結(jié)構(gòu)化編程”的一種,與數(shù)學函數(shù)思想類似。也就是假定輸入與輸出存在某種特定的映射關(guān)系,那么輸入一定的情況下,輸出必然是確定的。

          函數(shù)組件更純粹、簡單、易測試。

          獨特性

          類組件通過生命周期包裝業(yè)務邏輯,這是類組件所特有的。

          使用場景

          在不使用 Recompose 或者 Hooks 的情況下,如果需要使用生命周期,那么就用類組件,限定的場景是非常固定的;

          但在 recomposeHooks 的加持下,這樣的邊界就模糊化了,類組件與函數(shù)組件的能力邊界是完全相同的,都可以使用類似生命周期等能力。

          設計模式

          在設計模式上,因為類本身的原因,類組件是可以實現(xiàn)繼承的,而函數(shù)組件缺少繼承的能力。
          當然在 React 中也是不推薦繼承已有的組件的,因為繼承的靈活性更差,細節(jié)屏蔽過多,所以有這樣一個鐵律,組合優(yōu)于繼承。

          性能優(yōu)化

          類組件的優(yōu)化主要依靠 shouldComponentUpdate 函數(shù)去阻斷渲染。

          而函數(shù)組件一般靠 React.memo 來優(yōu)化。

          hooks中為什么不能使用if else 邏輯判斷?

          確保 Hook 在每一次渲染中都按照同樣的順序被調(diào)用。這讓 React 能夠在多次的 useState 和 useEffect 調(diào)用之間保持 hook 狀態(tài)的正確
          在hooks/src/index.js 下,找到 useState源碼,底層調(diào)用了 useReducer是通過全局索引去獲取 Hook state。

          平時如何設計 React 組件?

          組件分為:展示組件與靈巧組件

          展示組件內(nèi)部沒有狀態(tài)管理,僅僅用于最簡單的展示表達。展示組件中最基礎(chǔ)的一類組件稱作代理組件。代理組件常用于封裝常用屬性、減少重復代碼。很經(jīng)典的場景就是引入 Antd 的 Button 時,你再自己封一層。如果未來需要替換掉 Antd 或者需要在所有的 Button 上添加一個屬性,都會非常方便。基于代理組件的思想還可以繼續(xù)分類,分為樣式組件與布局組件兩種,分別是將樣式與布局內(nèi)聚在自己組件內(nèi)部。
          靈巧組件由于面向業(yè)務,其功能更為豐富,復雜性更高,復用度低于展示組件。最經(jīng)典的靈巧組件是容器組件。在開發(fā)中,我們經(jīng)常會將網(wǎng)絡請求與事件處理放在容器組件中進行。容器組件也為組合其他組件預留了一個恰當?shù)目臻g。還有一類靈巧組件是高階組件。高階組件被 React 官方稱為 React 中復用組件邏輯的高級技術(shù),它常用于抽取公共業(yè)務邏輯或者提供某些公用能力。常用的場景包括檢查登錄態(tài),或者為埋點提供封裝,減少樣板代碼量。高階組件可以組合完成鏈式調(diào)用,如果基于裝飾器使用,就更為方便了。高階組件中還有一個經(jīng)典用法就是反向劫持,通過重寫渲染函數(shù)的方式實現(xiàn)某些功能,比如場景的頁面加載圈等。但高階組件也有兩個缺陷,第一個是靜態(tài)方法不能被外部直接調(diào)用,需要通過向上層組件復制的方式調(diào)用,社區(qū)有提供解決方案,使用 hoist-non-react-statics 可以解決;第二個是 refs 不能透傳,使用 React.forwardRef API 可以解決。
          從工程實踐而言,通過文件夾劃分的方式切分代碼。我初步常用的分割方式是將頁面單獨建立一個目錄,將復用性略高的 components 建立一個目錄,在下面分別建立 basic、container 和 hoc 三類。這樣可以保證無法復用的業(yè)務邏輯代碼盡量留在 Page 中,而可以抽象復用的部分放入 components 中。其中 basic 文件夾放展示組件,由于展示組件本身與業(yè)務關(guān)聯(lián)性較低,所以可以使用 Storybook 進行組件的開發(fā)管理,提升項目的工程化管理能力。

          setState 是同步更新還是異步更新?

          setState 并非真異步,只是看上去像異步。在源碼中,通過 isBatchingUpdates 來判斷
          setState 是先存進 state 隊列還是直接更新,如果值為 true 則執(zhí)行異步操作,為 false 則直接更新。

          那么什么情況下 isBatchingUpdates 會為 true 呢?在 React 可以控制的地方,就為 true,比如在 React 生命周期事件和合成事件中,都會走合并操作,延遲更新的策略。

          但在 React 無法控制的地方,比如原生事件,具體就是在 addEventListenersetTimeoutsetInterval 等事件中,就只能同步更新
          一般認為,做異步設計是為了性能優(yōu)化、減少渲染次數(shù),React 團隊還補充了兩點。
          保持內(nèi)部一致性。如果將 state 改為同步更新,那盡管 state 的更新是同步的,但是 props不是。

          啟用并發(fā)更新,完成異步渲染。

          setState 是同步還是異步的核心關(guān)鍵點:更新隊列。

          如何面向組件跨層級通信?

          在父與子的情況下,因為 React 的設計實際上就是傳遞 Props 即可。那么場景體現(xiàn)在容器組件與展示組件之間,通過 Props 傳遞 state,讓展示組件受控

          在子與父的情況下,有兩種方式,分別是回調(diào)函數(shù)與實例函數(shù)。回調(diào)函數(shù),比如輸入框向父級組件返回輸入內(nèi)容,按鈕向父級組件傳遞點擊事件等。實例函數(shù)的情況有些特別,主要是在父組件中通過 React 的 ref API 獲取子組件的實例,然后是通過實例調(diào)用子組件的實例函數(shù)。這種方式在過去常見于 Modal 框的顯示與隱藏。這樣的代碼風格有著明顯的 jQuery 時代特征,在現(xiàn)在的 React 社區(qū)中已經(jīng)很少見了,因為流行的做法是希望組件的所有能力都可以通過 Props 控制。

          多層級間的數(shù)據(jù)通信,有兩種情況。第一種是一個容器中包含了多層子組件,需要最底部的子組件與頂部組件進行通信。在這種情況下,如果不斷透傳 Props 或回調(diào)函數(shù),不僅代碼層級太深,后續(xù)也很不好維護。第二種是兩個組件不相關(guān),在整個 React 的組件樹的兩側(cè),完全不相交。那么基于多層級間的通信一般有三個方案。
          第一個是使用 React 的 Context API,最常見的用途是做語言包國際化。
          第二個是使用全局變量與事件。全局變量通過在 Windows 上掛載新對象的方式實現(xiàn),這種方式一般用于臨時存儲值,這種值用于計算或者上報,缺點是渲染顯示時容易引發(fā)錯誤。全局事件就是使用 document 的自定義事件,因為綁定事件的操作一般會放在組件的 componentDidMount 中,所以一般要求兩個組件都已經(jīng)在頁面中加載顯示,這就導致了一定的時序依賴。如果加載時機存在差異,那么很有可能導致兩者都沒能對應響應事件。
          第三個是使用狀態(tài)管理框架,比如 Flux、Redux 及 Mobx。優(yōu)點是由于引入了狀態(tài)管理,使得項目的開發(fā)模式與代碼結(jié)構(gòu)得以約束,缺點是學習成本相對較高。

          Virtual DOM 的原理是什么?

          虛擬 DOM 的工作原理是通過 JS 對象模擬 DOM 的節(jié)點。在 Facebook 構(gòu)建 React 初期時,考慮到要提升代碼抽象能力、避免人為的 DOM 操作、降低代碼整體風險等因素,所以引入了虛擬 DOM。

          虛擬 DOM 在實現(xiàn)上通常是 Plain Object,以 React 為例,在 render 函數(shù)中寫的 JSX 會在 Babel 插件的作用下,編譯為 React.createElement 執(zhí)行 JSX 中的屬性參數(shù)。

          React.createElement 執(zhí)行后會返回一個 Plain Object,它會描述自己的 tag 類型、props 屬性以及 children 情況等。這些 Plain Object 通過樹形結(jié)構(gòu)組成一棵虛擬 DOM 樹。當狀態(tài)發(fā)生變更時,將變更前后的虛擬 DOM 樹進行差異比較,這個過程稱為 diff,生成的結(jié)果稱為 patch。計算之后,會渲染 Patch 完成對真實 DOM 的操作。

          虛擬 DOM 的優(yōu)點主要有三點:改善大規(guī)模 DOM 操作的性能、規(guī)避 XSS 風險、能以較低的成本實現(xiàn)跨平臺開發(fā)。

          虛擬 DOM 的缺點在社區(qū)中主要有兩點。

          內(nèi)存占用較高,因為需要模擬整個網(wǎng)頁的真實 DOM。

          高性能應用場景存在難以優(yōu)化的情況,類似像 Google Earth 一類的高性能前端應用在技術(shù)選型上往往不會選擇 React。

          React 的 diff 算法有什么不同?

          diff 算法是指生成更新補丁的方式,主要應用于虛擬 DOM 樹變化后,更新真實 DOM。所以 diff 算法一定存在這樣一個過程:觸發(fā)更新生成補丁應用補丁

          React 的 diff 算法,觸發(fā)更新的時機主要在 state 變化與 hooks 調(diào)用之后。此時觸發(fā)虛擬 DOM 樹變更遍歷,采用了深度優(yōu)先遍歷算法。但傳統(tǒng)的遍歷方式,效率較低。為了優(yōu)化效率,使用了分治的方式。將單一節(jié)點比對轉(zhuǎn)化為了 3 種類型節(jié)點的比對,分別是樹、組件及元素,以此提升效率。

          樹比對:由于網(wǎng)頁視圖中較少有跨層級節(jié)點移動,兩株虛擬 DOM 樹只對同一層次的節(jié)點進行比較。

          組件比對:如果組件是同一類型,則進行樹比對,如果不是,則直接放入到補丁中。

          元素比對:主要發(fā)生在同層級中,通過標記節(jié)點操作生成補丁,節(jié)點操作對應真實的 DOM 剪裁操作。

          以上是經(jīng)典的 React diff 算法內(nèi)容。自 React 16 起,引入了 Fiber 架構(gòu)。為了使整個更新過程可隨時暫停恢復,節(jié)點與樹分別采用了 FiberNode 與 FiberTree 進行重構(gòu)。fiberNode 使用了雙鏈表的結(jié)構(gòu),可以直接找到兄弟節(jié)點與子節(jié)點。

          整個更新過程由 currentworkInProgress 兩株樹雙緩沖完成。workInProgress 更新完成后,再通過修改 current 相關(guān)指針指向新節(jié)點。

          然后拿 Vue 和 Preact 與 React 的 diff 算法進行對比。

          Preact 的 Diff 算法相較于 React,整體設計思路相似,但最底層的元素采用了真實 DOM 對比操作,也沒有采用 Fiber 設計。Vue 的 Diff 算法整體也與 React 相似,同樣未實現(xiàn) Fiber 設計。

          然后進行橫向比較,React 擁有完整的 Diff 算法策略,且擁有隨時中斷更新的時間切片能力,在大批量節(jié)點更新的極端情況下,擁有更友好的交互體驗。

          Preact 可以在一些對性能要求不高,僅需要渲染框架的簡單場景下應用。

          Vue 的整體 diff 策略與 React 對齊,雖然缺乏時間切片能力,但這并不意味著 Vue 的性能更差,因為在 Vue 3 初期引入過,后期因為收益不高移除掉了。除了高幀率動畫,在 Vue 中其他的場景幾乎都可以使用防抖和節(jié)流去提高響應性能。

          react更新的流程是怎樣的?

          React在props或state發(fā)生改變時,會調(diào)用render()方法,創(chuàng)建一棵不同的樹。


          • 同層節(jié)點之間相互比較,不會垮節(jié)點比較
          • 不同類型的節(jié)點,產(chǎn)生不同的樹結(jié)構(gòu)
          • 通過key來指定哪些節(jié)點在不同的渲染下保持穩(wěn)定


          Diffing算法(調(diào)和算法):

          1.對比不同類型的元素,節(jié)點不同就會拆解原來的樹,建立新樹
          2.對比同類型元素
          3.對子節(jié)點進行遞歸

          詳細描述下React 的渲染流程

          圖片來源網(wǎng)絡

          React 的渲染過程大致一致,但協(xié)調(diào)并不相同,以 React 16 為分界線,分為 Stack ReconcilerFiber Reconciler。這里的協(xié)調(diào)從狹義上來講,特指 React 的 diff 算法,廣義上來講,有時候也指 React 的 reconciler 模塊,它通常包含了 diff 算法和一些公共邏輯。

          回到 Stack Reconciler 中,Stack Reconciler 的核心調(diào)度方式是遞歸。調(diào)度的基本處理單位是事務,它的事務基類是 Transaction,這里的事務是 React 團隊從后端開發(fā)中加入的概念。在 React 16 以前,掛載主要通過 ReactMount 模塊完成,更新通過 ReactUpdate 模塊完成,模塊之間相互分離,落腳執(zhí)行點也是事務。

          在 React 16 及以后,協(xié)調(diào)改為了 Fiber Reconciler。它的調(diào)度方式主要有兩個特點,第一個是協(xié)作式多任務模式,在這個模式下,線程會定時放棄自己的運行權(quán)利,交還給主線程,通過requestIdleCallback 實現(xiàn)。第二個特點是策略優(yōu)先級,調(diào)度任務通過標記 tag 的方式分優(yōu)先級執(zhí)行,比如動畫,或者標記為 high 的任務可以優(yōu)先執(zhí)行。Fiber Reconciler的基本單位是 Fiber,F(xiàn)iber 基于過去的 React Element 提供了二次封裝,提供了指向父、子、兄弟節(jié)點的引用,為 diff 工作的雙鏈表實現(xiàn)提供了基礎(chǔ)。

          在新的架構(gòu)下,整個生命周期被劃分為 RenderCommit 兩個階段。Render 階段的執(zhí)行特點是可中斷、可停止、無副作用,主要是通過構(gòu)造 workInProgress 樹計算出 diff。以 current 樹為基礎(chǔ),將每個 Fiber 作為一個基本單位,自下而上逐個節(jié)點檢查并構(gòu)造 workInProgress 樹。這個過程不再是遞歸,而是基于循環(huán)來完成。

          在執(zhí)行上通過 requestIdleCallback 來調(diào)度執(zhí)行每組任務,每組中的每個計算任務被稱為 work,每個 work 完成后確認是否有優(yōu)先級更高的 work 需要插入,如果有就讓位,沒有就繼續(xù)。優(yōu)先級通常是標記為動畫或者 high 的會先處理。每完成一組后,將調(diào)度權(quán)交回主線程,直到下一次 requestIdleCallback 調(diào)用,再繼續(xù)構(gòu)建 workInProgress 樹。

          在 commit 階段需要處理 effect 列表,這里的 effect 列表包含了根據(jù) diff 更新 DOM 樹、回調(diào)生命周期、響應 ref 等。

          但一定要注意,這個階段是同步執(zhí)行的,不可中斷暫停,所以不要在 componentDidMount、componentDidUpdate、componentWiilUnmount 中去執(zhí)行重度消耗算力的任務。

          React Hook 限制了哪些內(nèi)容?

          主要有兩條:

          1、不要在循環(huán)、條件或嵌套函數(shù)中調(diào)用 Hook;

          2、在 React 的函數(shù)組件中調(diào)用 Hook。

          Hooks 的設計初衷是為了改進 React 組件的開發(fā)模式。在舊有的開發(fā)模式下遇到了三個問題。
          組件之間難以復用狀態(tài)邏輯。過去常見的解決方案是高階組件、render props 及狀態(tài)管理框架。
          復雜的組件變得難以理解。生命周期函數(shù)與業(yè)務邏輯耦合太深,導致關(guān)聯(lián)部分難以拆分。
          人和機器都很容易混淆類。常見的有 this 的問題,但在 React 團隊中還有類難以優(yōu)化的問題,他們希望在編譯優(yōu)化層面做出一些改進。
          這三個問題在一定程度上阻礙了 React 的后續(xù)發(fā)展,所以為了解決這三個問題,Hooks 基于函數(shù)組件開始設計。然而第三個問題決定了 Hooks 只支持函數(shù)組件。
          那為什么不要在循環(huán)、條件或嵌套函數(shù)中調(diào)用 Hook 呢?因為 Hooks 的設計是基于數(shù)組實現(xiàn)。在調(diào)用時按順序加入數(shù)組中,如果使用循環(huán)、條件或嵌套函數(shù)很有可能導致數(shù)組取值錯位,執(zhí)行錯誤的 Hook。當然,實質(zhì)上 React 的源碼里不是數(shù)組,是鏈表。



          三、最后

          希望本文對小伙伴們尋找新機會有所幫助~加油

          歡迎關(guān)注前端早茶,與廣東靚仔攜手共同進階~

          瀏覽 139
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <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>
                  亚洲波多野结衣在线 | 午夜福利无码视频 | www.欧美在线观看 | 日韩性做爰免费A片AA片 | 黄色电影自拍 毛片 |