<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12

      記錄---從零開始編寫 useWindowSize Hook

      ????? 寫在開頭

      點贊 + 收藏 === 學會??????

      在 React 開發中,我們經常需要根據窗口大小來調整組件的行為。今天我們將從最簡單的實現開始,逐步優化,最終構建出一個高性能的 useWindowSize Hook。

      第一步:最簡單的實現

      讓我們從最基礎的版本開始:

      import { useState, useEffect } from 'react'
      
      function useWindowSize() {
        const [windowSize, setWindowSize] = useState({
          width: window.innerWidth,
          height: window.innerHeight,
        })
      
        useEffect(() => {
          function handleResize() {
            setWindowSize({
              width: window.innerWidth,
              height: window.innerHeight,
            })
          }
      
          window.addEventListener('resize', handleResize)
          return () => window.removeEventListener('resize', handleResize)
        }, [])
      
        return windowSize
      }

      這個版本能工作,但存在幾個問題:

      • 每次窗口變化都會創建新對象,導致不必要的重新渲染
      • 沒有考慮服務端渲染
      • 性能不夠優化

      第二步:解決 SSR 問題

      服務端渲染時沒有 window 對象,而且需要避免 hydration mismatch 錯誤:

      import { useState, useEffect } from 'react'
      
      function useWindowSize() {
        // 關鍵:服務端和客戶端首次渲染都返回相同的初始值
        const [windowSize, setWindowSize] = useState({
          width: 0,
          height: 0,
        })
      
        useEffect(() => {
          function updateSize() {
            setWindowSize({
              width: window.innerWidth,
              height: window.innerHeight,
            })
          }
      
          // 客戶端首次執行時立即獲取真實尺寸
          updateSize()
      
          // 然后監聽后續變化
          window.addEventListener('resize', updateSize)
          return () => window.removeEventListener('resize', updateSize)
        }, [])
      
        return windowSize
      }

      這里的關鍵是確保服務端和客戶端首次渲染時返回相同的值,避免 hydration mismatch。

      第三步:性能優化 - 減少不必要的更新

      現在我們思考一個問題:如果組件只使用了 width,那么 height 變化時是否需要重新渲染?答案是不需要。

      讓我們引入依賴追蹤的概念:

      import { useRef, useState, useEffect } from 'react'
      
      function useWindowSize() {
        const stateDependencies = useRef<{ width?: boolean; height?: boolean }>({})
        
        const [windowSize, setWindowSize] = useState({
          width: 0,
          height: 0,
        })
      
        const previousSize = useRef(windowSize)
      
        useEffect(() => {
          function handleResize() {
            const newSize = {
              width: window.innerWidth,
              height: window.innerHeight,
            }
      
            // 只檢查組件實際使用的屬性
            let shouldUpdate = false
            for (const key in stateDependencies.current) {
              if (newSize[key as keyof typeof newSize] !== previousSize.current[key as keyof typeof newSize]) {
                shouldUpdate = true
                break
              }
            }
      
            if (shouldUpdate) {
              previousSize.current = newSize
              setWindowSize(newSize)
            }
          }
      
          // 立即獲取初始尺寸
          handleResize()
      
          window.addEventListener('resize', handleResize)
          return () => window.removeEventListener('resize', handleResize)
        }, [])
      
        // 使用 getter 來追蹤依賴
        return {
          get width() {
            stateDependencies.current.width = true
            return windowSize.width
          },
          get height() {
            stateDependencies.current.height = true
            return windowSize.height
          },
        }
      }
       

      這里的核心思路是:當組件訪問 widthheight 時,我們記錄下這個依賴關系,然后在窗口變化時只檢查被使用的屬性。

      第四步:使用 useSyncExternalStore 提升并發安全性

      React 18 引入了 useSyncExternalStore,專門用于同步外部狀態,讓我們重構代碼:

      import { useRef } from 'react'
      import { useSyncExternalStore } from 'use-sync-external-store/shim/index.js'
      
      // 訂閱函數
      function subscribe(callback: () => void) {
        window.addEventListener('resize', callback)
        return () => {
          window.removeEventListener('resize', callback)
        }
      }
      
      function useWindowSize() {
        const stateDependencies = useRef<{ width?: boolean; height?: boolean }>({}).current
        const previous = useRef({ width: 0, height: 0 })
      
        // 比較函數:只比較被使用的屬性
        const isEqual = (prev: any, current: any) => {
          for (const key in stateDependencies) {
            if (current[key] !== prev[key]) {
              return false
            }
          }
          return true
        }
      
        const cached = useSyncExternalStore(
          subscribe, // 訂閱函數
          () => {
            // 獲取當前狀態
            const data = {
              width: window.innerWidth,
              height: window.innerHeight,
            }
            
            // 如果有變化,更新緩存
            if (!isEqual(previous.current, data)) {
              previous.current = data
              return data
            }
            return previous.current
          },
          () => {
            // SSR 回退值 - 避免 hydration mismatch
            return { width: 0, height: 0 }
          },
        )
      
        return {
          get width() {
            stateDependencies.width = true
            return cached.width
          },
          get height() {
            stateDependencies.height = true
            return cached.height
          },
        }
      }

      第五步:添加 TypeScript 類型支持

      最后,讓我們添加完整的類型定義:

      import { useRef } from 'react'
      import { useSyncExternalStore } from 'use-sync-external-store/shim/index.js'
      
      interface WindowSize {
        width: number
        height: number
      }
      
      interface StateDependencies {
        width?: boolean
        height?: boolean
      }
      
      interface UseWindowSize {
        (): {
          readonly width: number
          readonly height: number
        }
      }
      
      function subscribe(callback: () => void) {
        window.addEventListener('resize', callback)
        return () => {
          window.removeEventListener('resize', callback)
        }
      }
      
      export const useWindowSize: UseWindowSize = () => {
        const stateDependencies = useRef<StateDependencies>({}).current
        const previous = useRef<WindowSize>({
          width: 0,
          height: 0,
        })
      
        const isEqual = (prev: WindowSize, current: WindowSize) => {
          for (const _ in stateDependencies) {
            const t = _ as keyof StateDependencies
            if (current[t] !== prev[t]) {
              return false
            }
          }
          return true
        }
      
        const cached = useSyncExternalStore(
          subscribe,
          () => {
            const data = {
              width: window.innerWidth,
              height: window.innerHeight,
            }
            if (!isEqual(previous.current, data)) {
              previous.current = data
              return data
            }
            return previous.current
          },
          () => {
            // SSR 安全的初始值
            return { width: 0, height: 0 }
          },
        )
      
        return {
          get width() {
            stateDependencies.width = true
            return cached.width
          },
          get height() {
            stateDependencies.height = true
            return cached.height
          },
        }
      }

      設計思路總結

      在構建這個 Hook 的過程中,我們遵循了以下設計思路:

      1. 從簡單開始:先實現基本功能,再逐步優化
      2. 解決 SSR 問題:確保服務端和客戶端首次渲染一致,避免 hydration mismatch
      3. 性能優化:通過依賴追蹤減少不必要的重新渲染
      4. 現代化 API:使用 React 18 的新特性提升并發安全性
      5. 類型安全:添加 TypeScript 支持提供更好的開發體驗

      關鍵概念解釋

      依賴追蹤系統

      這個實現的精髓在于依賴追蹤系統。通過使用 getter 函數,我們可以檢測組件實際使用了哪些屬性,并且只在這些特定屬性發生變化時才觸發更新。

      SSR 兼容性

      關鍵是確保服務端渲染和客戶端首次渲染返回相同的初始值。useSyncExternalStore 的第三個參數專門用于提供 SSR 安全的初始值。

      智能比較策略

      我們維護一個緩存,只在必要時更新,顯著減少了內存分配和渲染周期。

      使用示例

      function MyComponent() {
        const { width, height } = useWindowSize()
        
        // 處理初始狀態(SSR 或首次加載)
        if (width === 0 && height === 0) {
          return <div>加載中...</div>
        }
        
        return (
          <div>
            <p>寬度: {width}px</p>
            <p>高度: {height}px</p>
          </div>
        )
      }
      
      // 只使用寬度的組件不會因為高度變化而重新渲染
      function WidthOnlyComponent() {
        const { width } = useWindowSize()
        
        if (width === 0) {
          return <div>加載中...</div>
        }
        
        return <div>寬度: {width}px</div>
      }
      
      // 響應式布局
      function ResponsiveLayout() {
        const { width } = useWindowSize()
        
        if (width === 0) {
          return <div>加載中...</div>
        }
        
        return (
          <div>
            {width < 768 ? <MobileLayout /> : <DesktopLayout />}
          </div>
        )
      }

      性能優勢

      這個實現提供了幾個性能優勢:

      1. 選擇性更新:只有訪問的屬性變化時才重新渲染
      2. 事件去重:多個組件共享同一個事件監聽器
      3. 內存效率:盡可能重用對象而不是創建新對象
      4. 并發安全:與 React 的并發特性完美配合

      通過這樣的步驟,我們從最簡單的實現開始,逐步解決了各種問題,最終得到了一個高性能、類型安全、SSR 兼容的 useWindowSize Hook。

      本文轉載于:https://juejin.cn/post/7530635412848836646

      如果對您有所幫助,歡迎您點個關注,我會定時更新技術文檔,大家一起討論學習,一起進步。


      posted @ 2025-07-28 17:07  林恒  閱讀(90)  評論(0)    收藏  舉報
      主站蜘蛛池模板: 国产精品自产拍在线播放| 老色鬼在线精品视频在线观看| 久久国产精品波多野结衣| 男同精品视频免费观看网站| 欧美精品v国产精品v日韩精品| 丰满少妇在线观看网站| 99热精品国产三级在线观看| 加勒比中文字幕无码一区| 奇米777四色在线精品| 久久一区二区三区黄色片| 亚洲精品国产精品国在线| 亚洲男人的天堂久久香蕉| 亚洲日韩AV秘 无码一区二区| 色综合色狠狠天天综合网| 亚洲熟女乱色综合一区| 天堂亚洲免费视频| 色综合人人超人人超级国碰| 久久99热只有频精品8| 亚洲无人区一区二区三区| 最新精品露脸国产在线| 国产一区二区在线观看粉嫩| 国产亚洲精品2021自在线| 国产一区二区日韩经典| 中文字幕成熟丰满人妻| 黑人巨大av无码专区| 无码人妻aⅴ一区二区三区蜜桃 | 深夜av免费在线观看| 久久久久国产精品熟女影院 | 毛片免费观看视频| 一本色道久久东京热| 久久人体视频| 高清国产美女一级a毛片在线| 亚洲第一精品一二三区| 99久久99久久久精品久久| 大香伊蕉在人线国产免费| 羞羞影院午夜男女爽爽免费视频| 欧美喷潮最猛视频| 亚洲午夜成人精品电影在线观看| 性xxxx视频播放免费| 国产精品日本一区二区不卡视频 | 蜜桃无码一区二区三区|