日韩成人免费在线_国产成人一二_精品国产免费人成电影在线观..._日本一区二区三区久久久久久久久不

當前位置:首頁 > 科技  > 軟件

有點東西?。∫粋€被小瞧的冷門Hook 補全了 React 19 異步優秀實踐的最后一環

來源: 責編: 時間:2024-06-19 15:36:53 260觀看
導讀先預警一下,完全消化本文內容有點難。useDeferredValue 解決真實場景問題的案例。useDeferredValue 基礎知識。復雜案例渲染過程分析。useDeferredValue 底層執行原理分析。重新分析取消請求案例。全文共 5104 字,閱讀

Gwi28資訊網——每日最新資訊28at.com

先預警一下,完全消化本文內容有點難。Gwi28資訊網——每日最新資訊28at.com

  • useDeferredValue 解決真實場景問題的案例。
  • useDeferredValue 基礎知識。
  • 復雜案例渲染過程分析。
  • useDeferredValue 底層執行原理分析。
  • 重新分析取消請求案例。

全文共 5104 字,閱讀需要花費 10 分鐘。Gwi28資訊網——每日最新資訊28at.com

useDeferredValue,一個出了很久,但是我幾乎沒咋在實踐中用到過的超冷門 hook。它有多冷門呢,我之前甚至都覺得沒必要介紹它。Gwi28資訊網——每日最新資訊28at.com

直到前幾天,一個粉絲給了我重要的思路,我才認識到它的威力,逐漸深入了解之后發現它簡直就是一個寶藏 hook,說它是為了 Suspense 量身訂做的都不為過。Gwi28資訊網——每日最新資訊28at.com

Gwi28資訊網——每日最新資訊28at.com

此時,我使用 useTransition 勉強實現了該功能。主要代碼如下:Gwi28資訊網——每日最新資訊28at.com

export default function Index() {  const [api, setApi] = useState(postApi)  const [isPending, startTransition] = useTransition()  function __inputChange() {    startTransition(() => {      api.cancel()      setApi(postApi())    })  }  ....
<Suspense fallback={<div>loading...</div>}>  <List api={api} isPending={isPending} /></Suspense>
const List = ({api, isPending}) => {  const posts = use(api)    return (    <ul className='_04_list' style={{opacity: isPending ? 0.5 : 1}}>      {posts.map((post) => (        <div key={post.id} className='_04_item'>          <h2>{post.title}</h2>          <p>{post.body}</p>        </div>      ))}    </ul>  )}

useTransition 能夠阻止 Suspense 在請求發生時,渲染 fallback 中的 Loading 組件,并且,isPending 也能表示請求正在發生,因此,我把 isPending 傳入到子組件中,那么我們就可以在子組件中自定義請求狀態。Gwi28資訊網——每日最新資訊28at.com

這基本達到了我想要的交互效果。Gwi28資訊網——每日最新資訊28at.com

但是一個嚴重的問題是,我每次輸入,都會發送一個請求,當我快速輸入時,我希望通過取消上一次還沒完成的請求的方式來優化交互效果。useTransition 并不支持我這樣做。Gwi28資訊網——每日最新資訊28at.com

核心原因是因為 useTransition 的任務會排隊依次執行,當我想要在下一個任務開始時,取消上一個請求時,上一個任務已經執行完了。因此 api.cancel() 雖然成功執行了,但是并起不到取消請求的效果,它執行時,已經沒有未完成的請求了。Gwi28資訊網——每日最新資訊28at.com

useTransition 無法取消請求。我思考了很久,也沒摸索出來一個合適的方案。因此之前我只能使用防抖來做這個優化。Gwi28資訊網——每日最新資訊28at.com

const [api, setApi] = useState(postApi)const [isPending, startTransition] = useTransition()const timer = useRef(null)function __inputChange() {  clearTimeout(timer.current)  timer.current = setTimeout(() => {    startTransition(() => {      api.cancel()      setApi(postApi())    })  }, 300)  }...

但是很顯然,這不是很優雅,因為防抖實際上和 useTransition 有類似的作用,用了防抖之后,useTransition 在這里的存在就變得有點尷尬了。Gwi28資訊網——每日最新資訊28at.com

意外之喜的是,有大佬級別的粉絲在評論區給我提供了一個非常優雅的解決思路。那就是利用 useDeferredValue。Gwi28資訊網——每日最新資訊28at.com

Gwi28資訊網——每日最新資訊28at.com

肅然起敬!!!!Gwi28資訊網——每日最新資訊28at.com

在保證了代碼優雅的情況之下,輕松實現了我理想中的效果。useDeferredValue 直接補齊了 React 19 異步開發中,最佳實踐的最后一塊短板!Gwi28資訊網——每日最新資訊28at.com

代碼就這么幾行,但是要理解 useDeferredValue,可能就要花點時間了。我們一起來學習一下。Gwi28資訊網——每日最新資訊28at.com

二、useDeferredValue 基礎

useDeferredValue 是一個可以推遲 UI 更新的 hook。這句話理解起來有點困難。需要我稍微給各位道友解讀一下。Gwi28資訊網——每日最新資訊28at.com

在正常情況下,一個 state 的變化,會導致 UI 發生變化。例如下面這個案例。Gwi28資訊網——每日最新資訊28at.com

function Index() {  const [counter, setCounter] = useState(0)  function __clickHanler() {    setCounter(counter + 1)  }  return (    <div>      <div id='tips'>基礎案例,state 遞增</div>      <button onClick={__clickHanler}>counter++</button>      <div className="counter">counter: {counter}</div>      <div className="counter">counter: {counter}</div>    </div>  )}

這里需要注意的是,狀態 counter 被兩個元素使用,因此,這兩個元素的更改,實際上是一個任務。他們必定會同時響應 counter 的變化。Gwi28資訊網——每日最新資訊28at.com

但是這個時候,我們可以利用 useDeferredValue,把他們拆分成兩個任務。Gwi28資訊網——每日最新資訊28at.com

function Index() {  const [counter, setCounter] = useState(0)  const deferred = useDeferredValue(counter)  function __clickHanler() {    setCounter(counter + 1)  }  return (    <div>      <div id='tips'>基礎案例,state 遞增</div>      <button onClick={__clickHanler}>counter++</button>      <div className="counter">        counter: {counter}      </div>      <div className="counter">        counter: {deferred}      </div>    </div>  )}

注意看,我們使用 counter 作為 useDeferredValue 的初始值,并將其返回值替換第二個元素。Gwi28資訊網——每日最新資訊28at.com

const deferred = useDeferredValue(counter)
<div className="counter">  counter: {deferred}</div>

此時,第二個元素的更新,就不再與第一個元素同步。它更新的優先級被降低。這個時候它的執行在理論上是可以被更高的優先級插隊和中斷的。Gwi28資訊網——每日最新資訊28at.com

但是由于渲染都太短了,我們肉眼無法區分出來兩個任務已經被分開了,因此我們把第二個元素重構成一個子組件,并模擬成一個耗時組件。此時我們就能明顯看出區別來。Gwi28資訊網——每日最新資訊28at.com

<Expensive counter={deferred} />
const Expensive = ({counter}) => {  const start = performance.now()  while (performance.now() - start < 200) {}  return (    <div className="counter">Deferred: {counter}</div>  )}

演示效果如下。Gwi28資訊網——每日最新資訊28at.com

Gwi28資訊網——每日最新資訊28at.com

因此,我們可以利用 useDeferredValue 推遲 UI 的更新。將對應任務的優先級降低,使其可以被插隊與中斷。Gwi28資訊網——每日最新資訊28at.com

三、復雜案例分析

在這里,我們要更加清楚的理解任務和渲染任務,才能對案例的分析更加的精準。以上一個例子的 Expensive 組件為例。Gwi28資訊網——每日最新資訊28at.com

狀態變化時,diff 會發生,Expensive 函數本身作為 diff 過程的一部分,它必定也會執行,但是這里我們注意,它對應的渲染任務,卻是可以被阻止執行的。Gwi28資訊網——每日最新資訊28at.com

例如在上面的例子中,當我快速點擊按鈕遞增時,Expensive 組件不會依次遞增。效果如下:Gwi28資訊網——每日最新資訊28at.com

Gwi28資訊網——每日最新資訊28at.com

我們發現,Expensive 組件的渲染直接從 0 變成了 7。Gwi28資訊網——每日最新資訊28at.com

這是因為作為一個耗時任務,又被標記了低優先級,因此它的渲染任務不停的被優先級更高的 counter 中斷并放棄。因此直接從 0 變成了 7。Gwi28資訊網——每日最新資訊28at.com

但是此時我們也發現另外一個情況,那就是 counter 直接對應的高優先級執行也沒有那么流暢,這是為什么呢?其實很簡單,因為在我們的模擬案例中,并沒有把耗時定位在渲染上。這可能和實踐情況會不太一樣。我們把耗時寫在了 Expensive 函數里,而這個函數每次都會執行,它的執行阻塞了渲染。Gwi28資訊網——每日最新資訊28at.com

const Expensive = ({counter}) => {  const start = performance.now()  while (performance.now() - start < 200) {}  return (    <div className="counter">Deferred: {counter}</div>  )}

?Gwi28資訊網——每日最新資訊28at.com

所以這里我們一定要區分開渲染任務和 Expensive 函數,他們是不同的,UI 渲染是一個異步任務,而 Expensive 函數是同步執行的。useDeferredValue 推遲的是 UI 渲染任務。因此,我們需要特別注意的是,不要在同步邏輯上執行過多的耗時任務。Gwi28資訊網——每日最新資訊28at.com

但是我們可以通過任務拆分的方式,把執行耗時時間分散到更多的子組件中去,這樣 React 就可以利用任務中斷的機制,在不阻塞渲染的情況下,中斷低優先級的任務。Gwi28資訊網——每日最新資訊28at.com

借用官網的一個復雜案例來跟大家演示。Gwi28資訊網——每日最新資訊28at.com

function SlowList({ text }) {  // Log once. The actual slowdown is inside SlowItem.  console.log('[ARTIFICIALLY SLOW] Rendering 250 <SlowItem />');  let items = [];  for (let i = 0; i < 250; i++) {    items.push(<SlowItem key={i} text={text} />);  }  return (    <ul className="items">      {items}    </ul>  );}function SlowItem({ text }) {  let startTime = performance.now();  while (performance.now() - startTime < 1) {    // Do nothing for 1 ms per item to emulate extremely slow code  }  return (    <li className="item">      Text: {text}    </li>  )}

此時我們注意觀察,不要錯漏這個細節。slowList 中包含了 250 個子組件。每個子組件都渲染 1ms,那么整個組件渲染就需要耗時至少 250ms。Gwi28資訊網——每日最新資訊28at.com

在父組件中,我們把 deferred 傳遞給 SlowList。Gwi28資訊網——每日最新資訊28at.com

<SlowList text={deferred} />

那么此時表示,slowList 的任務是低優先級。counter 對應的任務可以中斷它的執行。當我快速點擊時,執行效果如下。Gwi28資訊網——每日最新資訊28at.com

Gwi28資訊網——每日最新資訊28at.com

此時一個很明顯的區別就是,counter 的 UI 變化變得更加流暢了。這是因為耗時被拆分到了多個子組件中,React 就有機會中斷這些函數的執行,并執行優先級更高的任務,以確保高優先級任務的流暢。Gwi28資訊網——每日最新資訊28at.com

如果你沒有使用 React Compiler,你需要使用 memo 手動緩存 SlowList。Gwi28資訊網——每日最新資訊28at.com

const SlowList = memo(function SlowList({ text }) {  // ...});

useDefferdValue 會首先使用舊值傳遞給組件。Gwi28資訊網——每日最新資訊28at.com

<SlowList text={deferred} />

因此,當 counter 發生變化時,deferred 依然是舊值,那么此時,如果我們使用 memo 包裹,SlowList 的 props 就沒有發生變化,我們可以跳過此次針對 SlowList 的更新。Gwi28資訊網——每日最新資訊28at.com

這跟 React 的性能優化策略有關。Gwi28資訊網——每日最新資訊28at.com

四、運行原理

看了上面兩個例子,肯定還是有一部分人會覺得很懵,不要急,接下來我們把運行原理分析一下,整個情況就清晰了。Gwi28資訊網——每日最新資訊28at.com

useDeferredValue 會嘗試將 UI 任務更新兩次。Gwi28資訊網——每日最新資訊28at.com

第一次,會給子組件傳遞舊值。此時 SlowList 接收到的 props 會與上一次完全相同。如果結合了 React.memo,那么該組件就不會重新渲染。該組件可以重復使用之前的渲染結果。Gwi28資訊網——每日最新資訊28at.com

?Gwi28資訊網——每日最新資訊28at.com

Compiler 編譯之后不需要 memo。Gwi28資訊網——每日最新資訊28at.com

此時,高優先級的任務渲染會發生,渲染完成之后,將會開始第二次渲染。此時,將會傳入剛才更新之后的新值。對于 SlowList 而言,props 發生了變化,整個組件會重新渲染。Gwi28資訊網——每日最新資訊28at.com

我們通常會將已經非常明確的耗時任務標記為 deferred,因此,這些任務都被視為低優先級。當重要的高優先級更新已經完成,低優先級任務在第二次渲染時嘗試更新...Gwi28資訊網——每日最新資訊28at.com

在它第二次更新的過程中,如果又有新的高優先級任務進來,那么 React 就會中斷并放棄第二次更新,去執行高優先級的任務。Gwi28資訊網——每日最新資訊28at.com

iGwi28資訊網——每日最新資訊28at.com

注意:是中斷,并放棄這次更新,所以表現出來的結果就是,中間會漏掉許多任務的執行。Gwi28資訊網——每日最新資訊28at.com

這樣的運行機制有一個非常重要的好處。Gwi28資訊網——每日最新資訊28at.com

那就是,如果你的電腦性能足夠強悍,那么第二次的更新可能會快速完成,高優先級的任務來不及中斷,那么我們的頁面響應就是非常理想的。Gwi28資訊網——每日最新資訊28at.com

但是如果我們的電腦性能比較差,第二次更新還沒完成,新的高優先級任務又來了,那么就可以通過中斷的方式,降級處理,保證重要 UI 的流暢,放棄低優先級任務。Gwi28資訊網——每日最新資訊28at.com

?Gwi28資訊網——每日最新資訊28at.com

在不同性能的設備上,有不同的反應,這個是跟防抖、節流的最重要的區別。Gwi28資訊網——每日最新資訊28at.com

五、重新分析取消請求案例

那我們回過頭來,分析一下最開始的那個案例,重新看一眼代碼Gwi28資訊網——每日最新資訊28at.com

export default function Index() {  const [api, setApi] = useState(postApi)  const deferred = useDeferredValue(api)  function __inputChange(e) {    api.cancel()    setApi(postApi())  }  ...
<Suspense fallback={<div>loading...</div>}>  <List api={deferred} isPending={api !== deferred} /></Suspense>

這里我們將 api 做為 state,當 api 被重新賦值時,List 會經歷兩次更新。Gwi28資訊網——每日最新資訊28at.com

首先點擊事件觸發,請求立即發生。api 被改變。觸發組件更新。Gwi28資訊網——每日最新資訊28at.com

第一次更新時,deferred 使用舊值傳參,此時對于 List 而言,api 沒有發生變化。因此,利用這個機制,我們可以阻止 Suspense 直接渲染成 fallback。Gwi28資訊網——每日最新資訊28at.com

在 Suspense 包裹之下,只有當接口請求成功之后,deferred 的第二次更新才會發生,因此,在這個過程中,如果我們快速進行第二次點擊,可以直接取消上一次請求,讓第二次更新來不及執行。此時新的請求發生。Gwi28資訊網——每日最新資訊28at.com

?Gwi28資訊網——每日最新資訊28at.com

這里要結合 Suspense 的執行機制來理解。Gwi28資訊網——每日最新資訊28at.com

六、總結

這種場景的最佳實踐代碼非常的簡潔和優雅。寫起來也很舒服,性能也非常強悍。但是理解起來會比較困難。因此想要做到靈活運用,還需要多多消化。Gwi28資訊網——每日最新資訊28at.com

但是,等你徹底掌握它之后,你就會發現 React 19 在異步交互上真的太優雅了。這樣的開發體驗,是依賴 useEffect 完全比不了的。Gwi28資訊網——每日最新資訊28at.com

后續的分享中,我將會繼續為大家分享 React Action 的設計核心思維與具體使用。Gwi28資訊網——每日最新資訊28at.com

本文鏈接:http://m.www897cc.com/showinfo-26-94859-0.html有點東西啊!一個被小瞧的冷門Hook 補全了 React 19 異步優秀實踐的最后一環

聲明:本網頁內容旨在傳播知識,若有侵權等問題請及時與本網聯系,我們將在第一時間刪除處理。郵件:2376512515@qq.com

上一篇: Python編程新境界,代碼邏輯分離指南!

下一篇: 百度二面,有點小激動!附面試題

標簽:
  • 熱門焦點
Top 日韩成人免费在线_国产成人一二_精品国产免费人成电影在线观..._日本一区二区三区久久久久久久久不
国产乱码精品一区二区三| 先锋影音国产精品| 亚洲国产aⅴ天堂久久| 亚洲二区在线| 日韩午夜电影av| 一区二区三区高清不卡| 午夜精品福利在线| 老司机精品久久| 欧美日韩国产成人在线观看| 欧美日本精品在线| 国产精品萝li| 激情综合电影网| 日韩一本二本av| 欧美在线首页| 欧美波霸影院| 国产伦精品一区二区三区高清| 韩国精品一区二区三区| 亚洲伦理精品| 欧美专区亚洲专区| 欧美裸体一区二区三区| 国产一区av在线| 99视频精品全国免费| 久久精品欧美| 欧美女激情福利| 国内久久婷婷综合| 亚洲视频你懂的| 蜜桃av综合| 国产精品久久久久久久一区探花| 一色屋精品视频在线看| 一区二区三区精品在线| 久久久久久久国产| 欧美性一区二区| 亚洲国产成人在线视频| 午夜精品久久| 欧美日韩大片一区二区三区| 狠狠色综合日日| 亚洲一级影院| 欧美激情亚洲自拍| 韩国精品在线观看| 亚洲欧美日韩中文视频| 欧美片在线观看| 在线观看日韩欧美| 欧美与黑人午夜性猛交久久久| 欧美人与性禽动交情品| 在线观看成人av| 欧美亚洲视频在线观看| 欧美日韩亚洲系列| 亚洲国产精品电影| 久久精品91久久久久久再现| 国产精品高潮呻吟久久av黑人| 亚洲激情视频在线| 久久人人精品| 国产一区二区观看| 亚洲欧美日韩国产精品| 欧美日韩国产精品| 亚洲国产精品嫩草影院| 久久av一区二区| 国产精品自拍在线| 亚洲午夜一区二区| 欧美日韩一区二区免费视频| 亚洲国产婷婷香蕉久久久久久| 久久国产福利| 国产手机视频精品| 午夜欧美大片免费观看 | 欧美日韩a区| 亚洲成色精品| 麻豆乱码国产一区二区三区| 国产主播精品在线| 午夜精品一区二区三区在线| 国产精品久久国产三级国电话系列 | 国产精品永久免费视频| 中文日韩电影网站| 欧美三级精品| 在线视频一区二区| 欧美日韩一区二区三区在线观看免 | 激情久久影院| 久久影音先锋| 精品成人久久| 国产日韩综合| 亚洲女同在线| 国产精品日本| 亚洲夜晚福利在线观看| 欧美日一区二区在线观看 | 久久综合五月| 亚洲国产成人av在线| 欧美h视频在线| 亚洲日本va午夜在线电影| 欧美成人精品福利| 亚洲精品久久久久中文字幕欢迎你 | 国产精品99一区| 亚洲少妇中出一区| 国产精品久久久久久久久久久久久久| 亚洲性视频h| 国产精品亚洲аv天堂网| 午夜免费久久久久| 国产欧美日韩一区二区三区| 欧美在线亚洲在线| 韩国一区二区三区美女美女秀| 久久青草欧美一区二区三区| 亚洲欧洲在线一区| 欧美日韩亚洲一区三区 | 亚洲国产成人精品久久| 欧美黄污视频| 夜夜嗨av一区二区三区四季av | 欧美有码视频| 在线观看亚洲a| 欧美精品乱人伦久久久久久| avtt综合网| 国产精品影视天天线| 久久久噜久噜久久综合| 亚洲国产日韩综合一区| 欧美三级电影一区| 欧美亚洲一区| 亚洲福利在线观看| 欧美日韩免费观看一区| 午夜精品区一区二区三| 狠狠久久亚洲欧美专区| 欧美大胆成人| 亚洲综合色激情五月| 国语精品一区| 欧美日本韩国一区二区三区| 亚洲欧美日本国产有色| 黄色一区二区三区| 欧美日韩国产高清| 久久成人免费电影| 亚洲国产天堂久久综合| 国产精品v亚洲精品v日韩精品| 久久av在线| 亚洲精品欧美日韩| 国产美女精品一区二区三区| 蜜桃av一区二区三区| 亚洲视频在线二区| 黄色成人精品网站| 欧美日韩国产成人精品| 欧美一区二区三区免费观看| 亚洲国产精品女人久久久| 欧美色偷偷大香| 久久男人资源视频| 国产精品99久久不卡二区| 国内外成人免费激情在线视频网站| 欧美精品v日韩精品v韩国精品v | 日韩性生活视频| 国产一区二区你懂的| 欧美片在线观看| 久久久久国色av免费看影院 | 亚洲国产综合在线| 国产精品自拍网站| 欧美波霸影院| 久久av一区| 在线亚洲一区二区| 1024国产精品| 国产精品自在线| 欧美日韩 国产精品| 久久久水蜜桃av免费网站| 亚洲一区国产| 亚洲免费福利视频| 国产综合亚洲精品一区二| 欧美色区777第一页| 免费在线亚洲欧美| 久久国产精彩视频| 亚洲视频免费| 亚洲每日更新| 在线观看中文字幕不卡| 国产农村妇女精品| 欧美三级视频在线播放| 亚洲电影专区| 国产一区二区三区久久久久久久久| 欧美美女bb生活片| 欧美成人中文| 久久综合999| 午夜精品亚洲| 亚洲一区二区三区色| 亚洲成人在线| 韩国av一区二区三区| 国产日韩欧美日韩| 国产精品毛片va一区二区三区| 欧美日韩精品免费观看| 免费在线亚洲| 六月婷婷一区| 久久夜色精品国产亚洲aⅴ| 欧美中文字幕在线视频| 亚洲欧美在线免费观看| 亚洲网站视频福利| 一本一本a久久| 9l国产精品久久久久麻豆| 亚洲国内自拍| 亚洲国产人成综合网站| 亚洲大黄网站| 亚洲国产精品成人| 亚洲第一精品影视| 在线看欧美日韩| 亚洲动漫精品| 亚洲精品1234| 亚洲日本精品国产第一区| 亚洲欧洲一区二区三区久久| 亚洲国产网站| 亚洲精品视频在线看| 亚洲人成人一区二区三区| 91久久夜色精品国产网站| 亚洲国产天堂久久综合| 亚洲欧洲一区二区三区在线观看 | 国产欧美亚洲日本| 国产精品久久77777| 国产精品久久一卡二卡|