溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

React 17有哪些優(yōu)點

發(fā)布時間:2021-10-29 09:50:41 來源:億速云 閱讀:180 作者:iii 欄目:開發(fā)技術

本篇內容主要講解“React 17有哪些優(yōu)點”,感興趣的朋友不妨來看看。本文介紹的方法操作簡單快捷,實用性強。下面就讓小編來帶大家學習“React 17有哪些優(yōu)點”吧!

無新特性

React 17不太尋常,因為它沒有添加任何面向開發(fā)人員的新功能,而主要側重于升級簡化React本身。

我們正在積極開發(fā)React的新功能,但它們并不屬于此版本。React 17是我們進行深度推廣戰(zhàn)略的關鍵所在。

此版本之所以特殊,你可以認為React 17是一個過渡版,它會使得由一個React版本管理樹嵌入到另一個React版本管理樹中時會更加安全。

逐步升級

在過去七年的時間里,React一直遵循著all-or-nothing的升級策略。你可以繼續(xù)使用舊版本,也可以將整個應用程序升級至新版本。但沒有介于兩者之間的情況。

此方式一直延續(xù)至今,但我們確遭遇了all-or-nothing升級策略的局限性。許多API的變更,例如,反對使用 legacy context  API時,并不能以自動化的方式來完成。至今可能大多數(shù)應用程序從未使用過它們,但我們仍然選擇在React中支持它們。我們必須在無限期支持過時的API或針對某些應用仍使用舊版本  React 間進行選擇。但這兩個方案都不合適。

因此,我們想提供另一種方案。

React 17開始支持逐步升級React版本。當從React 15升到16時(或者從 React  16升到17時),通常會一次升級整個應用程序。這適用于大部分應用程序。但是,如果代碼庫是在幾年前編寫的,并且并沒有得到很好的維護,那么升級它會變得越來越有挑戰(zhàn)性。盡管可以在頁面上使用兩個版本的React,但是直到React  17依然會出現(xiàn)events問題。

我們使用React 17解決了許多諸如此類的問題。這將意味著當React  18或未來版本問世時,你將有更多選擇。首選還是像以前一樣,一次升級整個應用程序。但你也可以選擇逐步升級你的應用程序。例如,你可能會將大部分應用程序遷移至React  18,但在React 17上保留一些延遲加載的對話框或子路由。

但這不意味著你必須逐步升級。對于大部分應用程序來說,一次性全部升級仍是最好的解決方案。加載兩個React版本,即使其中一個是按需延遲加載的,仍然不太理想。但是,對于沒有積極維護的大型應用來說,可以考慮此種方案,并且  React 17開始可以保證這些應用程序不落伍。

為了實現(xiàn)逐步升級,我們需要對React的事件系統(tǒng)進行一些更改。而這些更改可能會對代碼產生影響,這也是React  17成為主要版本的原因。實際上,十萬個以上的組件中受影響的組件不超過20個,因此我們希望大多數(shù)應用程序都可以升級到React  17,而不會產生太多影響。如果遇到問題的話可以聯(lián)系我們。

逐步升級的示例

我們準備了一個示例(GitHub)倉庫,展示了如何在必要時延遲加載舊版本的React。該示例使用了Create React  App進行構建,但對其他工具采用類似的方法應該也適用。我們歡迎使用其他工具的開發(fā)者編寫demo并提交pr。

注意: 我們已將其他的更新推遲到React 17之后。此版本的目標是實現(xiàn)逐步升級。如果升級React 17太困難的話,我們的目標會無法實現(xiàn)。

更改事件委托

從技術上講,始終可以在應用程序中嵌套不同版本的React。但由于React事件系統(tǒng)的工作原理導致很難實現(xiàn)。

在React組件中,通常會內聯(lián)編寫事件處理:

<button onClick={handleClick}>

與此代碼等效的DOM操作如下:

myButton.addEventListener('click', handleClick);

但對大多數(shù)事件來說,React并不會將它們附加到DOM節(jié)點上。相反,React會直接在document節(jié)點上為每種事件類型附加一個處理器,這被稱為事件委托。除了在大型應用程序上具有性能優(yōu)勢外,它還使添加類似于replaying  events這樣的新特性變得更容易。

自從其發(fā)布以來,React就一直自動進行事件委托。當document上觸發(fā)DOM事件時,React會找出調用的組件,然后  React事件會在組件中向上"冒泡"。但實際上,原生事件已經冒泡出了"document"級別,React是在document中安裝的事件處理器。

但這就是逐步升級的困難所在。

如果頁面上有多個React版本,他們都將在頂層注冊事件處理器。這會破壞e.stopPropagation()  如果嵌套樹結構中阻止了事件冒泡,但外部樹依然能接收到它。這會使不同版本React的嵌套變得十分困難。這種擔憂并不是沒有根據的 &mdash;&mdash;  例如,四年前Atom編輯器就遇到了相同的問題。

這也是我們?yōu)槭裁匆淖僐eact底層附加事件方式的原因。

在React 17中,React將不再向document添加事件處理器。而會將事件處理器附加到渲染React樹的根DOM節(jié)點中:

const rootNode = document.getElementById('root'); ReactDOM.render(<App />, rootNode);

在React 16或更早版本中,React會對大多數(shù)事件執(zhí)行document.addEventListener()。React  17將會在底層調用rootNode.addEventListener()。

React 17有哪些優(yōu)點

多虧了這個更改,現(xiàn)在可以更加安全地進行新舊版本React樹的嵌套。請注意,要使其正常工作,兩個版本都必須為17或更高版本,這就是為什么強烈建議升級到React  17的根本原因。從某種意義上講,React 17是一個過渡版本,使逐步升級成為可能。

此更改還讓React嵌入使用其他技術構建的應用程序變得更加容易。例如,如果應用程序的"外殼"是用jQuery編寫的,但其中較新的代碼是用React編寫的,則React代碼中的e.stopPropagation()會阻止它影響jQuery的代碼  &mdash;&mdash; 就像你所期盼的那樣。換句話說,如果你不再喜歡React并想重寫應用程序(比如用jQuery),則可以從外層開始將  React轉換為jQuery,而不會破壞事件冒泡。

經核實,多年來在issue tracker上報告的許多問題都已被新特性解決,這些問題大多都與將React與非React代碼集成有關。

注意: 你可能想知道這是否會破壞根容器之外的Portals。答案是React還會監(jiān)聽portals容器上的事件,所以這不是問題。

解決隱患

與其他重大更新一樣,可能需要對代碼進行調整。在Facebook,我們在成千上萬個模塊中大約調整了十個模塊以適應這次更新。

例如,如果模塊中使用document.addEventListener(...)手動添加了DOM監(jiān)聽,你可能希望能捕獲到所有React 事件。在React  16或更早版本中,即使你在React事件處理器中調用e.stopPropagation(),你創(chuàng)建的DOM監(jiān)聽仍會觸發(fā),這是因為原生事件已經處于document級別。使用React  17冒泡將被阻止(按需),因此你的document級別的事件監(jiān)聽不會觸發(fā):

document.addEventListener('click', function() {   // 如果React組件調用了e.stopPropagation()   // 那么這個自定義監(jiān)聽函數(shù)不會收到click事件 });

你可以將監(jiān)聽轉換為使用事件捕獲來修復此類代碼。為此,你可以將{ capture: true }作為  document.addEventListener的第三個參數(shù)傳遞:

document.addEventListener('click', function() {   // 現(xiàn)在這個事件處理函數(shù)使用了事件捕獲,   // 所以它可以接收到所有的點擊事件! }, { capture: true });

請注意,此策略在全局上具有更好的適應性。例如,它可能會修復代碼中現(xiàn)有的錯誤,這些錯誤在 React 事件處理器外部調用  e.stopPropagation() 發(fā)生。換句話說,React 17的事件冒泡更接近常規(guī)DOM。

其他重大更改

我們將 React  17中的重大更改保持在最低水平。例如,它不會刪除以前版本中棄用的任務方法。但是,它的確包含一些其他重大更改,根據經驗,這些更改會相對安全??傮w而言,由于這些因素的存在,在十萬個以上的組件中受影響的組件不超過20個。

對標瀏覽器

我們對事件系統(tǒng)進行了一些較小的更新:

  • onScroll事件不再冒泡,以防止出現(xiàn)一些混淆。

  • React的onFocus和onBlur事件已在底層切換為原生的focusin和focusout事件。它們更接近React現(xiàn)有行為,有時還會提供額外的信息。

  • 捕獲事件(例如,onClickCapture)現(xiàn)在使用的是實際瀏覽器中的捕獲監(jiān)聽器。

這些更改會使React與瀏覽器行為更接近,并提高了互操作性。

注意: 盡管從React  17把focus事件切換成了focusin,但onFocus并未影響冒泡行為。在React中,onFocus事件總是冒泡的,它在React  17中繼續(xù)冒泡,因為通常它是一個更有用的默認值。查看這個sandbox,了解可以針對不同的特定用例添加的不同檢查。

去除事件池

React 17中移除了"event pooling(事件池)"。它并不會提高現(xiàn)代瀏覽器的性能,甚至還會使經驗豐富的開發(fā)者一頭霧水:

function handleChange(e) {   setData(data => ({     ...data,     // This crashes in React 16 and earlier:     text: e.target.value   })); }

這是因為React在舊瀏覽器中重用了不同事件的事件對象,以提高性能,并將所有事件字段在它們之前設置為null。在 React  16及更早版本中,使用者必須調用e.persist()才能正確的使用該事件,或者正確讀取需要的屬性。

在 React 17 中,此代碼可以按照預期效果執(zhí)行。舊的事件池優(yōu)化操作已被完成刪除,因此,使用者可以在需要時讀取事件字段。

這改變了行為,因此我們將其標記為重大更新,但在實踐中我們沒有看到它在Facebook上造成影響(甚至還修復了一些bug!)。請注意,e.persist()在  React事件對象中仍然可用,只不過沒有任何效果罷了。

副作用清理時機

我們正在使useEffect和清理函數(shù)的時機保持一致。

useEffect(() => {   // This is the effect itself.   return () => {     // This is its cleanup.   }; });

大多數(shù)副作用(effect)不需要延遲刷新視圖,因此React在屏幕上反映出更新后立即異步執(zhí)行它們(在極少數(shù)情況下,你需要一種副作用來阻止重繪。例如,如果需要獲取尺寸和位置,請使用useLayoutEffect)。

然而,副作用清理函數(shù)(如果存在)在React16中同步運行。我們發(fā)現(xiàn),對于大型應用程序來說,這不是理想選擇,因為同步會減緩視圖的更新(例如,切換標簽)。

在React 17中,副作用清理函數(shù)會異步執(zhí)行 &mdash;&mdash; 如果要卸載組件,則清理會在視圖更新后運行。

這反映了副作用本身如何更緊密地運行。在極少數(shù)情況下,你可能希望依靠同步執(zhí)行,可以改用useLayoutEffect來代替。

注意:  你可能想知道這是否意味著你現(xiàn)在將無法修復有關未掛載組件上的setState的警告。不必擔心,React專門處理了這種情況,并且不會在卸載和清理之間短暫間隔內發(fā)出setState的警告。因此,取消代碼的請求或間隔幾乎總是可以保存不變的。

此外,React 17會根據它們在tree中的位置,以與效果相同的順序執(zhí)行cleanup。在以前的時候順序有時會不同。

潛在隱患

可復用的庫可能需要對此情況進行深度測試,但我們只遇到了幾個組件會因為此問題中斷執(zhí)行。比如:

useEffect(() => {   someRef.current.someSetupMethod();   return () => {     someRef.current.someCleanupMethod();   }; });

問題在于someRef.current是可變的,因此在運行清除函數(shù)時,它可能已經設置為null。解決方案是在副作用內部存儲會發(fā)生變化的值:

useEffect(() => {   const instance = someRef.current;   instance.someSetupMethod();   return () => {     instance.someCleanupMethod();   }; });

我們不希望此問題對大家造成影響,我們提供的eslint-plugin-react-hooks/exhaustive-deps的lint插件(請確保在項目中使用它)會對此情況發(fā)出警告。

返回一致的undefined錯誤

在React 16及更早版本中,返回undefined始終會報錯:

function Button() {   return; // Error: Nothing was returned from render }

這很容易無意間返回undefined:

function Button() {   // 這里忘記了寫ruturn,所以這個組件返回了一個undefined。   // React會報錯而不會忽略它。   <button />; }

以前,React只對class和函數(shù)組件執(zhí)行此操作,但并不會檢查forwardRef和memo組件的返回值。這是由于編碼錯誤導致。

在React 17中,forwardRef和memo組件的行為會與常規(guī)函數(shù)組件和class組件保持一致。在返回undefined時會報錯

let Button = forwardRef(() => {   // 這里忘記了寫ruturn,所以這個組件返回了一個undefined。   // React17會報錯而不會忽略它。   <button />; });  let Button = memo(() => {   // 這里忘記了寫ruturn,所以這個組件返回了一個undefined。   // React17會報錯而不會忽略它。   <button />; });

對于不想進行任何渲染的情況,請return null。

原生組件棧

當你在瀏覽器中遇到錯誤時,瀏覽器會為你提供帶有JavaScript函數(shù)的名稱及位置的堆棧信息。然而JavaScript堆棧通常不足以診斷問題,因為React樹的層次結構可能同樣重要。你不僅要知道哪個Button拋出了錯誤,而且還想知道  Button在React樹中的哪個位置。

為了解決這個問題,當你遇到錯誤時,從React  16開始會打印"組件棧"信息。盡管如此,它們仍然不如原生的JavaScript堆棧。特別是它們在控制臺中不可點擊,因為React不知道函數(shù)在源代碼中的聲明位置。此外,它們在生產中幾乎無用。不同于常規(guī)壓縮后的JavaScript堆棧,它們可以通過sourcemap的形式自動恢復到原始函數(shù)的位置,而使用React組件棧,在生產環(huán)境下必須在堆棧信息和bundle大小間進行選擇。

在React  17中,使用了不同的機制生成組件堆棧,該機制會將它們與常規(guī)的原生JavaScript堆??p合在一起。這使得你可以在生產環(huán)境中獲得完全符號化的React組件堆棧信息。

React實現(xiàn)這一點的方式有點非常規(guī)。目前,瀏覽器無法提供獲取函數(shù)堆??蚣?源文件和位置)的方法。因此,當  React捕獲到錯誤時,將通過組件上述組件內部拋出的臨時錯誤(并捕獲)來重建其組件堆棧信息。這會增加崩潰時的性能損失,但每個組件類型只會發(fā)生一次。

如果你對此感興趣,可以在這個PR中閱讀更多詳細信息,但是在大多數(shù)情況下,這種機制不會影響你的代碼。從使用者的角度來看,新功能就是可以單擊組件堆棧(因為它們依賴于本機瀏覽器堆棧框架),并且可以像常規(guī)JavaScript錯誤那樣在生產中進行解碼。

構成重大變化的部分是,要使此功能正常工作,React將在捕獲錯誤后在堆棧中重新執(zhí)行上面某些函數(shù)和某些class構造函數(shù)的部分。由于渲染函數(shù)和class構造函數(shù)不應具有副作用(這對于SSR也很重要),因此這不會造成任何實際問題。

移除私有導出

最后,值得注意的重大變化是我們刪除了一些以前暴露給其他項目的React內部組件。特別是,React Native for  Web過去常常依賴于事件系統(tǒng)的某些內部組件,但這種依賴關系很脆弱且經常被破壞。

在React 17中,這些私有導出已被移除。據我們所知,React Native for  Web是唯一使用它們的項目,它們已經完成了向不依賴那些私有導出函數(shù)的其他方法遷移。

這意味著舊版本的React Native for Web不會與React 17兼容,但是新版本可以使用它。實際上,并沒有太大的變化,因為React  Native for Web必須發(fā)布新版本以適應其內部React的變化。

另外,我們刪除了ReactTestUtils.SimulateNative的helper方法。他們從未被記錄,沒有按照他們名字所暗示的那樣去做,也沒有處理我們對事件系統(tǒng)所做的更改。如果你想要一種簡便的方式來觸發(fā)測試中原生瀏覽器的事件,請改用  React Testing Library。

安裝

我們鼓勵你盡快嘗試React 17.0  RC版本,在遷移過程中遇到任何問題都可以向我們提出。請注意,候選版本沒有穩(wěn)定版本穩(wěn)定,因此請不要將其部署到生產環(huán)境。

通過 npm 安裝 React 17 RC 版,請執(zhí)行:

npm install react@17.0.0-rc.0 react-dom@17.0.0-rc.0

通過 yarn 安裝 React 17 RC 版,請執(zhí)行:

yarn add react@17.0.0-rc.0 react-dom@17.0.0-rc.0

我們還通過CDN提供了React RC的UMD構建版本:

<script crossorigin src="https://unpkg.com/react@17.0.0-rc.0/umd/react.production.min.js"></script> <script crossorigin src="https://unpkg.com/react-dom@17.0.0-rc.0/umd/react-dom.production.min.js"></script>

到此,相信大家對“React 17有哪些優(yōu)點”有了更深的了解,不妨來實際操作一番吧!這里是億速云網站,更多相關內容可以進入相關頻道進行查詢,關注我們,繼續(xù)學習!

向AI問一下細節(jié)

免責聲明:本站發(fā)布的內容(圖片、視頻和文字)以原創(chuàng)、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI