面試葵花寶典之React(持續更新中)

1.談談你對HOC的理解

定義: 高階組件是一個接收組件作為參數并返回新組件的函數,用于復用組件邏輯,遵循純函數特性(無副作用,輸出僅依賴輸入)。

  • 組合性:可嵌套使用多個 HOC。
    HOC(Higher-Order Component,高階組件)是 React 中的一種設計模式,它本質上是一個函數,接受一個組件作為參數,返回一個新的組件。這個新組件通常會添加一些額外的功能或者修改原有組件的行為,而不直接修改原組件的代碼。
    屬性代理props,state,反向繼承(生命周期劫持,方法重寫)

主要特點:

  1. 增強組件功能:HOC 允許你在不修改原組件的情況下,給它添加額外的邏輯或功能。比如:權限控制、數據獲取、日志記錄、條件渲染等。
  2. 純函數:HOC 只是一個函數,它不改變原組件的實例,而是返回一個新的組件。傳入的原組件將成為 HOC 的輸入,返回的新組件是帶有附加功能的組件。
  3. 組合性:多個 HOC 可以被組合在一起,形成一個強大的功能組合。這使得 React 的組件變得更加靈活和可復用。

常見應用場景:

  • 狀態共享:多個組件之間可以通過 HOC 共享相同的狀態邏輯。
  • 權限控制:HOC 可以用于根據用戶權限來渲染不同的 UI。
  • 生命周期管理:在 HOC 中添加鉤子函數,可以封裝組件的生命周期操作。
  • 代碼復用:例如,處理 API 請求的 HOC 可以應用于多個組件,而不需要每個組件都重復編寫相同的請求邏輯。

優缺點:

優點

  • 增強可復用性:將常見的邏輯封裝成 HOC,可以在多個地方復用。
  • 邏輯與視圖分離:HOC 使得 UI 和邏輯功能分離,提高代碼的可維護性和可測試性。
  • 組合性強:HOC 可以通過組合多個不同的功能來增強組件的功能。

缺點

  • 命名沖突:HOC 可能會給組件的屬性命名帶來沖突,尤其是在 HOC 之間傳遞 props 時。
  • 復雜性增加:如果過度使用 HOC,可能會導致組件樹變得復雜,難以調試和維護。
  • 性能問題:每次通過 HOC 包裝一個組件時,都會返回一個新的組件,這可能導致不必要的渲染,影響性能。

HOC詳細點擊查看

2.談談你對React Fiber的理解

先概述它的基本概念,Fiber是什么、為什么提出Fiber,主要特點是什么,解決什么問題、以及它如何影響 React 的工作方式。然后,我會深入講解它的核心特性和實現原理,最后給出一個應用場景,展示我對它的實際理解。


React Fiber 是 React 內部的一個新的調度引擎,旨在優化 React 的渲染過程,提高渲染的可控性和性能,尤其是在處理復雜 UI 和高頻率更新時。目標是使React能夠更好地處理大型應用和動態更

1. 為什么需要 Fiber?

JavaScript引擎和頁面渲染引擎兩個線程是互斥的,當其中一個線程執行時,另一個線程只能掛起等待 如果 JavaScript線程長時間地占用了主線程,那么渲染層面的更新就不得不長時間地等待,界面長時間不更新,會導致頁面響應度變差,用戶可能會感覺到卡頓 也就是早期版本中使用的叫做“棧式調度”的渲染算法。這個算法是同步的,也就是說,React 渲染一個組件時,會阻塞后續的工作,直到渲染過程完成。當React在渲染組件時,從開始到渲染完成整個過程是一氣呵成的,無法中斷 如果組件較大,那么js線程會一直執行,然后等到整棵VDOM樹計算完成后,才會交給渲染的線程 這就會導致一些用戶交互、動畫等任務無法立即得到處理,導致卡頓的情況,

JavaScript執行Javascript引擎和頁面渲染在同一個線程中,GUI渲染和Javascript執行兩者之間是互斥的 工 如果某個任務執行時間過長,瀏覽器就會推遲渲染。這就引入了Fiber

Fiber 作為 React 的新架構,主要是為了引入“增量渲染”,使得渲染過程可以被中斷并分片執行,允許 React 在渲染期間執行其他更緊急的任務,從而改善性能。

2. Fiber 解決了什么問題?

  • 異步渲染:Fiber 使得 React 渲染過程可以被分割成多個小任務,任務之間可以被中斷和重新調度。這樣,當 React 正在渲染時,它可以暫停當前的渲染工作,去處理一些更重要的任務(比如用戶輸入、動畫等)。
  • 優先級調度:通過 Fiber,React 能夠為不同的渲染任務設置優先級。例如,用戶輸入和動畫可以擁有更高的優先級,而不那么重要的任務(如更新某個非關鍵的 UI 元素)可以有更低的優先級,從而保證高優先級任務盡快完成。
  • 改進的生命周期管理:React 通過 Fiber 可以更好地管理組件生命周期,處理復雜的場景,如 React Suspense 和 Concurrent Mode,這些特性在 Fiber 的架構下能夠得到更好的支持。

3. Fiber 的核心特性和實現:

  • 增量渲染(Incremental Rendering) :Fiber 將渲染過程分解為多個小任務,每個任務都可以中斷和恢復。通過這種方式,React 可以在渲染中間進行調度,優先處理高優先級的任務,如用戶交互。

  • 優先級調度(Prioritization) :Fiber 引入了優先級的概念。不同的渲染任務可以根據它們的優先級被調度。比如:

    • 用戶交互(例如點擊、滾動等)通常是高優先級的。
    • 狀態更新或背景渲染可能是低優先級的。
  • Fiber 樹:Fiber 引入了一種新的數據結構——Fiber 樹,它是虛擬 DOM 的一個升級版。每個 Fiber 節點都表示一個組件實例,包含了關于組件的所有信息,包括它的狀態、渲染結果、生命周期方法等。

    • Work Units:每個 Fiber 節點對應一個“工作單元”,這些單元可以被異步執行。React 可以把渲染工作分成更小的單位,按需處理。
  • Time Slicing:通過 Fiber,React 可以將大任務切割成多個小任務,在渲染的過程中“切片”時間,讓瀏覽器有機會處理其他任務,比如用戶輸入、動畫等,從而避免界面卡頓。

4. Fiber 與 React 之前版本的區別:

  • 同步 vs 異步:在 React Fiber 之前,React 的渲染過程是同步的。也就是說,組件渲染是阻塞式的,直到整個渲染完成。在 Fiber 中,渲染過程被拆解成多個小任務,可以異步執行。
  • 改進的生命周期:Fiber 引入了新的生命周期方法,特別是針對異步渲染的生命周期方法(如 getDerivedStateFromPropsgetSnapshotBeforeUpdate),這些方法有助于提升組件的性能和響應性。
  • 并發渲染:Fiber 使得 React 能夠支持并發渲染(Concurrent Rendering)。這意味著 React 可以在多個任務之間切換,優先處理用戶交互、動畫等高優先級任務,降低長時間渲染對用戶體驗的影響。

5. Fiber 在實際應用中的優勢:

  • 改善復雜動畫:對于需要頻繁更新的動畫或交互式 UI,Fiber 通過異步渲染和優先級調度可以避免動畫卡頓,提升流暢度。
  • React Suspense:Fiber 是 React Suspense 功能的基礎。它允許 React 在數據加載時“暫停”渲染,等數據準備好后再繼續渲染,提升了數據驅動應用的響應速度和流暢性。
  • 并發模式(Concurrent Mode) :Fiber 為并發模式奠定了基礎,使得 React 可以同時渲染多個版本的 UI,進一步提升性能和用戶體驗。

總結:

React Fiber 是 React 渲染引擎的一次重大升級,通過引入異步渲染、優先級調度和增量渲染,極大提升了 React 的性能和靈活性。它為未來的 React 特性(如并發模式和 Suspense)提供了基礎,同時也優化了復雜 UI 更新和高頻交互的性能。雖然 Fiber 的實現較為復雜,但它為 React 提供了更強大的能力,尤其是在需要精細控制渲染過程的場景中。

具體fiber原理見:https://blog.csdn.net/qq_34645412/article/details/145886426?spm=1001.2014.3001.5501

3.說說對React的理解?有哪些特性


React 是一個用于構建用戶界面的 JavaScript 庫,主要特點包括:

  1. 組件化,可組合和嵌套:React 將 UI 劃分為獨立的、可復用的組件,每個組件可以有自己的狀態和生命周期。組件化的結構讓代碼更具可維護性和可復用性。
  2. 虛擬 DOM:React 通過虛擬 DOM 來優化性能,減少對真實 DOM 的直接操作。每次狀態更新,React 會先在虛擬 DOM 中計算差異,然后高效地更新實際 DOM。
  3. 單向數據流:React 使用單向數據流,父組件通過 props 向子組件傳遞數據,子組件不能直接修改父組件的狀態,確保數據流向清晰,管理更簡單。
  4. JSX:JSX 是 React 使用的語法擴展,它讓開發者能夠在 JavaScript 中直接寫 HTML 結構,提高了代碼的可讀性和開發效率。
  5. 聲明式編程:React采用聲明范式,可以輕松描述應用。開發者只需描述UI應該是什么樣子,React會負責實際渲染工作
  6. Hooks:React 16.8 引入的 Hooks 允許函數組件管理狀態和副作用,簡化了類組件中復雜的生命周期管理。
  7. React Router 和 Context:React 通過 React Router 實現單頁面應用的路由功能,通過 React Context 提供跨組件的數據傳遞。

這些特性使得 React 在構建高效、可維護的用戶界面時非常強大,特別是在構建大型應用時,可以大大提升開發效率和應用性能。

4.說說你對React的state和props有什么區別

突出 state 和 props 的區別


面試官stateprops 都是 React 中用于管理和傳遞數據的方式,但它們有一些重要的區別:

  1. state(狀態)

    • state 是組件內部管理的數據,它決定了組件的可變狀態。
    • 組件可以通過 this.setState(類組件)或者 useState(函數組件)來更新 state,從而觸發組件重新渲染。
    • 每個組件有自己的 state,它是可變的,因此 state 主要用于存儲需要隨時間變化的數據,如用戶輸入、交互狀態等。
  2. props(屬性)

    • props 是父組件傳遞給子組件的數據,它是只讀的,子組件不能修改自己的 props
    • props 用于組件間的數據傳遞和共享,是組件之間的通信方式。
    • props 是不可變的,父組件通過更新 props 來控制子組件的數據。

關鍵區別:

  • 來源state 來自組件內部,props 來自父組件。
  • 可變性state 是可變的,props 是只讀的。
  • 用途state 用于組件內部的數據管理,props 用于組件間的數據傳遞。

5.說說你對React的super和super(props)有什么區別


在 React 中,supersuper(props) 都是與類組件的構造函數相關的,但是它們有細微的區別。

  1. super

    • super 是調用父類的構造函數。在 React 中,所有的組件類都繼承自 React.ComponentReact.PureComponent,因此在定義構造函數時,我們需要調用 super() 來初始化父類。
    • 如果沒有調用 super(),子類的構造函數就無法正確執行,會導致錯誤。
    class MyComponent extends React.Component {constructor() {super(); // 調用父類構造函數this.state = { count: 0 };}
    }
    
  2. super(props)

    • super(props) 不僅調用父類的構造函數,還將父組件傳遞的 props 傳遞給 React.Component 的構造函數。
    • React 需要通過 props 初始化組件的狀態或其他操作,因此如果我們想在構造函數中使用 this.props,就必須調用 super(props)
    class MyComponent extends React.Component {constructor(props) {super(props); // 調用父類構造函數并傳遞 propsthis.state = { count: 0 };}
    }
    

關鍵區別:

  • super() :僅僅調用父類的構造函數,不傳遞 props
  • super(props) :調用父類的構造函數,并將父組件傳遞的 props 傳遞給父類,這樣子類的構造函數中就可以訪問 this.props

在使用 React.ComponentReact.PureComponent 時,如果希望在構造函數中訪問 this.props,應該使用 super(props)

6.說說你對react中類組件和函數組件的理解,有什么區別?

在React中,類組件和函數組件是兩種主要的組件形式,它們有以下區別:

類組件

  1. 定義方式
  • 類組件是基于ES6的類語法定義的,需要繼承自React.Component
  1. 生命周期方法
  • 類組件可以使用React提供的各種生命周期方法,如componentDidMountcomponentDidUpdatecomponentWillUnmount等。
  1. 狀態管理
  • 類組件有自己的狀態(this.state),可以通過this.setState()方法來更新狀態。
  1. this關鍵字
  • 類組件中需要使用this關鍵字來訪問組件的屬性和方法。
  1. 性能優化
  • 可以使用shouldComponentUpdate生命周期方法來進行性能優化,避免不必要的渲染。
  1. 代碼復雜性
  • 類組件的代碼通常比函數組件更復雜,尤其是在處理多個生命周期方法和狀態更新時。

函數組件

  1. 定義方式
  • 函數組件是一個簡單的JavaScript函數,接收props作為參數并返回React元素。
  1. Hooks支持
  • 自React 16.8起,函數組件可以使用Hooks(如useStateuseEffect等)來管理狀態和副作用。
  1. 狀態管理
  • 使用useState Hook可以在函數組件中添加和管理狀態。
  1. 簡潔性
  • 函數組件通常更簡潔,易于理解和維護。
  1. 性能優化
  • React團隊為函數組件引入了React.memo高階組件來進行性能優化,避免不必要的渲染。
  1. 代碼簡潔性
  • 函數組件的代碼通常更加簡潔,尤其是在使用Hooks之后,可以避免類組件中的一些樣板代碼。

總結

  • 類組件適合那些需要使用復雜生命周期方法或者需要在多個生命周期方法中維護狀態的場景。
  • 函數組件隨著Hooks的引入,已經變得非常強大,可以處理大多數場景,包括狀態管理和副作用處理。函數組件通常更簡潔、易于測試和維護。

隨著React的發展,函數組件和Hooks已經成為主流,許多新的特性和優化都是圍繞它們展開的。因此,現代React開發中,推薦優先使用函數組件和Hooks。

7.說說你對react中受控組件和非受控組件的理解?應用場景


面試官,在 React 中,受控組件和非受控組件主要的區別在于數據的控制和管理方式。

1. 受控組件(Controlled Components)

  • 定義:受控組件是指那些通過 React 的 state 來管理其值的組件。組件的值由父組件的狀態來控制,用戶的輸入通過事件處理程序更新組件的 state,從而觸發重新渲染。

  • 實現:在受控組件中,表單元素(如 <input><textarea><select> 等)的值由組件的 state 控制,onChange 事件用來更新 state,確保 React 控制表單元素的值。

    function ControlledInput() {const [value, setValue] = useState('');const handleChange = (e) => {setValue(e.target.value);};return (<input type="text" value={value} onChange={handleChange} />);
    }
    
  • 特點

    • React 完全控制組件的狀態和行為。
    • 可以方便地進行表單驗證、動態顯示錯誤信息等。
    • 更易于調試,因其數據是受控的。

2. 非受控組件(Uncontrolled Components)

  • 定義:非受控組件是指那些不直接通過 React 的 state 來控制其值的組件。相反,組件的值由 DOM 本身管理,而 React 通過 ref 來訪問表單元素的值。

  • 實現:在非受控組件中,表單元素的值并不由 React 的狀態管理,而是依賴于 DOM 自身的狀態。你可以通過 ref 獲取該值。

    function UncontrolledInput() {const inputRef = useRef();const handleSubmit = () => {alert('Input value: ' + inputRef.current.value);};return (<div><input type="text" ref={inputRef} /><button onClick={handleSubmit}>Submit</button></div>);
    }
    
  • 特點

    • 組件的狀態不由 React 管理,而是由 DOM 自身維護。
    • 使用 ref 來直接訪問 DOM 元素。
    • 在某些簡單的場景中使用非受控組件可以減少額外的狀態管理,代碼更簡潔。

3. 受控組件與非受控組件的區別

  • 數據來源

    • 受控組件:表單元素的值由 React 的 state 控制。
    • 非受控組件:表單元素的值由 DOM 控制,React 通過 ref 來訪問它。
  • 渲染方式

    • 受控組件的每次用戶輸入都會更新 React 的 state,并觸發組件重新渲染。
    • 非受控組件不會每次輸入都觸發渲染,只有在調用 ref 獲取值時才訪問 DOM。
  • 靈活性

    • 受控組件能夠實現更多的功能(如表單驗證、動態更新等),更適合復雜的交互。
    • 非受控組件適合那些沒有復雜交互邏輯的簡單表單,減少了不必要的狀態管理。

4. 應用場景

  • 受控組件

    • 適用于需要實時跟蹤用戶輸入、進行表單驗證、動態更新 UI 或處理表單數據提交的場景。
    • 例如,復雜表單、表單驗證、交互式表單(例如,根據用戶選擇動態渲染其他輸入字段)。
  • 非受控組件

    • 適用于簡單的場景,不需要頻繁跟蹤輸入值的變化。例如,簡單的表單或是只在表單提交時才獲取值的場景。
    • 例如,表單只需要在提交時獲取數據,或是需要快速開發一個簡單的表單,不關心輸入的實時變化。

總結

  • 受控組件通過 React 的 state 來管理表單元素的值,適合需要高控制和實時反饋的場景。
  • 非受控組件使用 ref 直接訪問 DOM 元素的值,適合簡單表單或需要簡化代碼的場景。

8.說說你對react事件綁定的方式有哪些?區別?

如果這是一個面試題,我會簡潔明了地回答 React 中事件綁定的方式,并突出每種方式的區別。以下是我可能的回答:


面試官,在 React 中,事件綁定主要有兩種方式:方法綁定(普通函數)和箭頭函數綁定。它們的區別在于上下文(this)的綁定方式。以下是詳細解釋:

1. 使用 bind 方法綁定事件

  • 定義:使用 JavaScript 的 bind() 方法在構造函數中顯式地綁定事件處理函數的 this 上下文。
  • 實現:在構造函數中通過 bind 方法將事件處理函數的 this 綁定到當前實例。
class MyComponent extends React.Component {constructor(props) {super(props);this.state = { count: 0 };// 在構造函數中綁定事件處理函數this.handleClick = this.handleClick.bind(this);}handleClick() {this.setState({ count: this.state.count + 1 });}render() {return <button onClick={this.handleClick}>Click</button>;}
}
  • 優點:事件處理函數中的 this 指向當前組件實例。
  • 缺點:每次組件實例化時,bind 會創建一個新的函數,可能導致性能問題,尤其是在渲染大量組件時。

2. 使用箭頭函數綁定事件

  • 定義:在事件處理函數內部使用箭頭函數來自動綁定 this
  • 實現:箭頭函數不需要顯式綁定 this,因為箭頭函數會從定義位置(類組件)繼承 this
class MyComponent extends React.Component {constructor(props) {super(props);this.state = { count: 0 };}handleClick = () => {this.setState({ count: this.state.count + 1 });}render() {return <button onClick={this.handleClick}>Click</button>;}
}
  • 優點:代碼簡潔,this 自動綁定,不需要顯式調用 bind
  • 缺點:每次渲染時都會創建一個新的箭頭函數,可能導致性能問題,尤其是在大量渲染時。

3. 直接傳遞事件處理函數(函數式組件)

  • 定義:對于函數組件,直接傳遞事件處理函數即可,this 不存在,事件處理函數直接引用即可。
  • 實現
function MyComponent() {const [count, setCount] = useState(0);const handleClick = () => {setCount(count + 1);};return <button onClick={handleClick}>Click</button>;
}
  • 優點:沒有 this,代碼更加簡潔和易于理解,且性能更好。
  • 缺點:適用于函數組件,對于類組件來說不適用。

4. 直接調用事件處理函數

  • 定義:直接在 JSX 中調用事件處理函數,不進行綁定。
  • 實現
class MyComponent extends React.Component {handleClick() {alert('Button clicked!');}render() {return <button onClick={() => this.handleClick()}>Click</button>;}
}
  • 優點:代碼簡潔。
  • 缺點:每次渲染都會創建一個新的函數,可能會影響性能,特別是在大量渲染時。

5. 傳遞參數給事件處理函數

  • 定義:如果需要在事件處理函數中傳遞額外的參數,可以通過箭頭函數或 bind 來傳遞。
class MyComponent extends React.Component {handleClick = (param) => {alert(param);};render() {return <button onClick={() => this.handleClick('Hello!')}>Click</button>;}
}
  • 優點:可以靈活地傳遞額外參數。
  • 缺點:和直接調用一樣,每次渲染都會創建新的函數。

6. 事件處理函數的優化

  • React.memouseCallback:對于性能要求較高的組件,可以使用 React.memo(函數組件)和 useCallback(函數組件)來避免不必要的渲染和重新綁定函數。這樣做可以確保在相同的輸入下,事件處理函數保持一致,避免每次渲染都創建新的函數。

總結:可直接回答總結部分

  • bind:適用于類組件,在構造函數中綁定 this,但可能引發性能問題。
  • 箭頭函數:簡潔的寫法,自動綁定 this,但也可能在渲染時創建新的函數,影響性能。
  • 函數式組件:沒有 this,直接傳遞事件處理函數,性能好且代碼簡潔。但不適用于類組件
  • 直接調用:雖然簡潔,但每次渲染都會創建新的函數,性能較差。
  • 傳遞參數:通過箭頭函數或 bind,可以靈活傳遞額外參數,但要注意性能影響。

在實際開發中,通常推薦使用箭頭函數或者函數組件來簡化代碼,盡量避免不必要的性能開銷,尤其是在頻繁渲染的組件中。

9.說說react事件機制?

在React中,事件機制是一個重要的核心概念,它通過合成事件(SyntheticEvent)事件委托(Event Delegation) 實現了跨瀏覽器一致性和性能優化。以下是詳細解析:


1. 合成事件(SyntheticEvent)

React的事件對象是對原生瀏覽器事件的跨瀏覽器封裝,提供了統一的API接口,確保在不同瀏覽器中行為一致。

  • 特點

    • 跨瀏覽器兼容:例如,event.preventDefault()event.stopPropagation() 在所有瀏覽器中行為一致。
    • 性能優化:事件對象會被復用(事件池機制),在事件回調執行后,事件對象的屬性會被重置為null。若需異步訪問事件屬性,需調用 event.persist()
    • 事件類型:支持常見的DOM事件(如onClickonChange),也支持React特有的合成事件(如onDoubleClick)。
  • 示例

    function handleClick(event) {event.preventDefault(); // 阻止默認行為event.stopPropagation(); // 阻止冒泡console.log(event.target.value); // 訪問事件屬性
    }
    

2. 事件委托(Event Delegation)

React將所有事件委托到根節點(React 17之前是document,17+是ReactDOM.render的容器節點),而非直接綁定到具體元素。

  • 優勢

    • 內存優化:減少事件監聽器的數量,避免為每個元素單獨綁定事件。
    • 動態元素支持:動態添加的子元素無需重新綁定事件。
  • 示例

    // React內部自動處理事件委托,開發者只需編寫事件處理函數
    <button onClick={handleClick}>Click Me</button>
    

3. 與原生事件的區別

  • 命名方式:React事件使用駝峰命名(如onClick),而非原生的小寫(如onclick)。
  • 事件綁定:React通過JSX屬性綁定事件,而非addEventListener
  • 默認行為:React中需顯式調用event.preventDefault(),而原生事件可通過return false阻止默認行為。

4. 事件處理中的this綁定

在類組件中,事件處理函數需注意this指向問題:

  • 解決方法

    • 構造函數中綁定:this.handleClick = this.handleClick.bind(this)
    • 使用箭頭函數:handleClick = () => { ... }
    • 在JSX中直接綁定:onClick={() => this.handleClick()}(可能引起性能問題)

5. 事件池(Event Pooling)

  • 機制:React會復用合成事件對象以提升性能,事件回調執行后,事件對象的屬性會被置為null

  • 異步訪問:若需在異步操作(如setTimeoutPromise)中訪問事件屬性,需調用event.persist()

    function handleClick(event) {event.persist(); // 保留事件對象setTimeout(() => {console.log(event.target.value); // 正常訪問}, 1000);
    }
    

6. React 17+ 的變化

  • 事件委托容器:事件不再委托到document,而是綁定到ReactDOM.render的根容器節點,避免與外部DOM樹沖突。
  • 移除事件池:React 17+ 移除了事件池優化,合成事件對象不再被復用,無需event.persist()即可異步訪問屬性。

7. 應用場景與最佳實踐

  • 受控組件:使用onChangestate管理表單輸入(實時驗證、提交)。
  • 性能敏感場景:非受控組件結合ref直接操作DOM,減少渲染次數。
  • 阻止冒泡:在嵌套組件中,通過event.stopPropagation()控制事件傳播。

總結

React的事件機制通過合成事件事件委托,在簡化開發的同時保證了性能和跨瀏覽器一致性。理解其核心原理(如this綁定、事件池、委托策略)能幫助開發者更高效地處理交互邏輯,避免常見陷阱(如異步訪問事件屬性)。隨著React 17+的更新,事件機制進一步簡化,更貼近原生行為。

10.說說react構建組件的方式有哪些?區別是?

在 React 中,構建組件的方式主要有以下幾種,它們各有特點并適用于不同的場景:


1. 類組件(Class Components)

  • 定義方式:通過 ES6 的 class 語法定義,繼承自 React.Component

  • 核心特性

    • 使用 this.state 管理狀態。
    • 通過生命周期方法(如 componentDidMountcomponentDidUpdate)處理副作用。
    • 需要手動綁定事件處理函數的 this 指向。
  • 適用場景

    • 需要復雜生命周期控制的場景(如精確管理組件掛載、更新、卸載時的邏輯)。
    • 舊代碼庫或需要兼容 React 16.8 之前的版本。
  • 示例

    class MyComponent extends React.Component {state = { count: 0 };handleClick = () => {this.setState({ count: this.state.count + 1 });};render() {return <button onClick={this.handleClick}>{this.state.count}</button>;}
    }
    

2. 函數組件(Function Components)

  • 定義方式:通過普通 JavaScript 函數定義,接受 props 參數并返回 JSX。

  • 核心特性

    • 使用 Hooks(如 useStateuseEffect)管理狀態和副作用。
    • 無生命周期方法,但可通過 useEffect 模擬生命周期行為。
    • 代碼更簡潔,避免 this 綁定問題。
  • 適用場景

    • 新項目或需要簡化代碼結構的場景。
    • 需要邏輯復用(通過自定義 Hooks)。
  • 示例

    function MyComponent() {const [count, setCount] = useState(0);const handleClick = () => setCount(count + 1);return <button onClick={handleClick}>{count}</button>;
    }
    

3. 高階組件(HOC, Higher-Order Components)

  • 定義方式:接收一個組件并返回一個新組件的函數。

  • 核心特性

    • 用于邏輯復用(如權限校驗、數據獲取)。
    • 通過包裝組件注入額外 props 或行為。
  • 缺點

    • 嵌套過多可能導致“包裝地獄”(類似 withA(withB(Component)))。
    • 可能引入命名沖突。
  • 示例

    function withLogger(WrappedComponent) {return function(props) {useEffect(() => {console.log('Component rendered!');}, []);return <WrappedComponent {...props} />;};
    }
    const EnhancedComponent = withLogger(MyComponent);
    

4. Render Props 模式

  • 定義方式:通過 props 傳遞一個函數,由子組件決定如何渲染內容。

  • 核心特性

    • 解決邏輯復用問題,避免 HOC 的嵌套問題。
    • 更靈活地共享組件間的邏輯。
  • 示例

    <DataProvider render={data => <ChildComponent data={data} />} />
    

5. 自定義 Hooks

  • 定義方式:通過 useXxx 命名的函數封裝可復用邏輯。

  • 核心特性

    • 替代 HOC 和 Render Props,更簡潔地實現邏輯復用。
    • 可以在函數組件中直接調用。
  • 示例

    function useCounter(initialValue) {const [count, setCount] = useState(initialValue);const increment = () => setCount(count + 1);return { count, increment };
    }
    // 使用
    function MyComponent() {const { count, increment } = useCounter(0);return <button onClick={increment}>{count}</button>;
    }
    

6. 復合組件(Compound Components)

  • 定義方式:通過多個關聯組件共同工作,共享隱式狀態(如 <Select><Option>)。

  • 核心特性

    • 通過 React.ContextReact.Children 實現狀態共享。
    • 提供更直觀的 API 設計。
  • 示例

    const Tabs = ({ children }) => {const [activeTab, setActiveTab] = useState(0);return (<div>{React.Children.map(children, (child, index) =>React.cloneElement(child, {isActive: index === activeTab,onClick: () => setActiveTab(index),}))}</div>);
    };
    

各方式的核心區別

方式狀態管理邏輯復用代碼簡潔性適用場景
類組件this.state繼承、HOC較復雜舊項目、復雜生命周期控制
函數組件 + HooksuseState自定義 Hooks簡潔新項目、邏輯復用
HOC通過 props 注入包裝組件中等橫切關注點(如鑒權、日志)
Render Props通過函數參數傳遞動態渲染靈活但稍顯冗長需要高度定制的邏輯復用
復合組件Context 或 Children隱式狀態共享直觀關聯組件的組合(如表單)

總結

  • 類組件:適合需要精細控制生命周期的場景,但逐漸被函數組件取代。
  • 函數組件 + Hooks:現代 React 的主流方式,代碼簡潔且邏輯復用能力強。
  • HOC/Render Props:在 Hooks 出現前用于邏輯復用,現可結合 Hooks 使用。
  • 復合組件:適合構建復雜但 API 友好的組件庫(如 Ant Design)。

推薦選擇

  • 新項目優先使用 函數組件 + Hooks
  • 需要兼容舊代碼時,可混合使用類組件和 Hooks。
  • 邏輯復用優先用 自定義 Hooks,其次考慮 HOC 或 Render Props。

11.說說react引入css的方式有哪幾種?區別

在 React 中,引入 CSS 的方式多樣,每種方法都有其適用場景和優缺點。以下是常見方案及其核心區別:


1. 內聯樣式(Inline Styles)

  • 定義:直接在 JSX 元素中通過 style 屬性編寫樣式,使用 JavaScript 對象表示。

  • 特點

    • 作用域:僅作用于當前元素,無全局污染。
    • 動態樣式:方便根據 props/state 動態修改樣式。
    • 局限性:不支持偽類(如 :hover)、媒體查詢、動畫等。
  • 示例

    const divStyle = { color: 'red', fontSize: '20px' };
    function Component() {return <div style={divStyle}>Hello</div>;
    }
    

2. 普通 CSS 文件(Plain CSS)

  • 定義:通過 import './styles.css' 引入全局 CSS 文件。

  • 特點

    • 作用域:全局生效,易引發樣式沖突。
    • 維護性:適合傳統項目,但缺乏模塊化。
    • 功能支持:完整支持所有 CSS 特性。
  • 示例

    /* styles.css */
    .my-class { color: red; }
    
    import './styles.css';
    function Component() {return <div className="my-class">Hello</div>;
    }
    

3. CSS Modules

  • 定義:通過構建工具(如 Webpack)將 CSS 文件轉換為局部作用域的模塊。

  • 特點

    • 作用域:類名被哈希化,避免全局沖突(如 .my-class_1x2y3)。
    • 維護性:模塊化清晰,適合組件化開發。
    • 兼容性:需配置構建工具支持(如 css-loader)。
  • 示例

    /* styles.module.css */
    .myClass { color: red; }
    
    import styles from './styles.module.css';
    function Component() {return <div className={styles.myClass}>Hello</div>;
    }
    

4. CSS-in-JS

  • 定義:使用 JavaScript 編寫 CSS,常見庫包括 styled-componentsEmotionJSS

  • 特點

    • 作用域:樣式與組件綁定,無全局污染。
    • 動態樣式:支持基于 props/state 的動態樣式。
    • 功能支持:完整 CSS 功能(包括偽類、動畫)。
    • 性能:運行時生成樣式,可能影響性能(但通常可優化)。
  • 示例(styled-components)

    import styled from 'styled-components';
    const StyledDiv = styled.div`color: ${props => props.primary ? 'red' : 'blue'};&:hover { font-size: 20px; }
    `;
    function Component() {return <StyledDiv primary>Hello</StyledDiv>;
    }
    

5. CSS 預處理器(Sass/Less/Stylus)

  • 定義:通過 Sass/Less 等預處理器增強 CSS 功能(變量、嵌套、混合等)。

  • 特點

    • 功能增強:支持變量、嵌套、函數等高級特性。
    • 結合方式:可與 CSS Modules 或普通 CSS 結合使用。
    • 構建依賴:需配置預處理器(如 sass-loader)。
  • 示例(Sass + CSS Modules)

    /* styles.module.scss */
    $primary-color: red;
    .myClass { color: $primary-color; }
    
    import styles from './styles.module.scss';
    function Component() {return <div className={styles.myClass}>Hello</div>;
    }
    

6. Utility-First CSS(Tailwind CSS)

  • 定義:通過預定義的實用類(utility classes)快速組合樣式。

  • 特點

    • 開發速度:無需手寫 CSS,通過類名組合實現樣式。
    • 定制性:支持通過配置文件擴展主題。
    • 學習成本:需記憶大量類名,但 IDE 插件可輔助。
  • 示例

    function Component() {return (<div className="text-red-500 hover:text-blue-500">Hello</div>);
    }
    

7. CSS 框架(如 Bootstrap)

  • 定義:使用現成的 UI 框架(如 Bootstrap、Ant Design)提供的樣式。

  • 特點

    • 快速開發:直接使用預定義的組件和樣式。
    • 定制性:通常支持主題覆蓋,但可能需覆蓋框架默認樣式。
  • 示例

    import 'bootstrap/dist/css/bootstrap.min.css';
    function Component() {return <button className="btn btn-primary">Submit</button>;
    }
    

各方案對比

方式作用域動態樣式功能支持維護性適用場景
內聯樣式組件內??(無偽類/媒體查詢)簡單動態樣式
普通 CSS全局??傳統項目、小型應用
CSS Modules局部??組件化開發、避免沖突
CSS-in-JS局部??復雜動態樣式、主題系統
預處理器依賴引入方式??(增強功能)需要高級 CSS 功能
Utility-First全局/局部?(通過類名)?快速開發、減少自定義 CSS
CSS 框架全局??快速搭建標準化 UI

總結

  • 簡單場景:內聯樣式或普通 CSS。
  • 組件化開發:優先選擇 CSS ModulesCSS-in-JS(如 styled-components)。
  • 動態主題/復雜樣式CSS-in-JS 是最佳選擇。
  • 快速開發Tailwind CSS 或現成的 CSS 框架
  • 大型項目:結合 CSS Modules + 預處理器(如 Sass)提升可維護性。

根據項目規模、團隊習慣和樣式復雜度靈活選擇,也可混合使用多種方案(如用 Tailwind 處理布局,CSS-in-JS 處理動態主題)。

12.React生命周期有哪些不同的階段?每個階段對應的方法是?

初始化掛載(Mounting)更新(Updating) ?和?卸載(Unmounting)

1. 生命周期概述

1.1 React 16.3 之前的生命周期

  • 初始化階段
    • constructor
    • componentWillMount
    • render
    • componentDidMount
  • 更新階段
    • componentWillReceiveProps
    • shouldComponentUpdate
    • componentWillUpdate
    • render
    • componentDidUpdate
  • 卸載階段
    • componentWillUnmount

1.2 React 16.3 之后的生命周期

  • 初始化階段
    • constructor
    • getDerivedStateFromProps
    • render
    • componentDidMount
  • 更新階段
    • getDerivedStateFromProps
    • shouldComponentUpdate
    • render
    • getSnapshotBeforeUpdate
    • componentDidUpdate
  • 卸載階段
    • componentWillUnmount

    5.2 生命周期方法與 Hooks 對照表

生命周期方法Hooks 實現
constructoruseState
componentDidMountuseEffect(() => {}, [])
componentDidUpdateuseEffect(() => {}, [deps])
componentWillUnmountuseEffect(() => { return () => {} }, [])
shouldComponentUpdateuseMemo, useCallback
getDerivedStateFromPropsuseState + useEffect
詳細請看鏈接https://tutudev.blog.csdn.net/article/details/144718978

13.React組件之間如何進行通信?

在 React 中,組件間的通信方式根據組件關系的不同有多種解決方案。以下是常見場景及對應方法:


一、父子組件通信

1. 父 → 子:通過 props 傳遞數據
  • 實現:父組件通過屬性(props)將數據傳遞給子組件。

  • 示例

    // 父組件
    function Parent() {const data = "Hello";return <Child message={data} />;
    }
    // 子組件
    function Child({ message }) {return <div>{message}</div>; // 輸出:Hello
    }
    
2. 子 → 父:通過回調函數
  • 實現:父組件傳遞一個回調函數給子組件,子組件調用該函數傳回數據。

  • 示例

    // 父組件
    function Parent() {const handleData = (data) => console.log(data);return <Child onSend={handleData} />;
    }
    // 子組件
    function Child({ onSend }) {return <button onClick={() => onSend("Data from child")}>Send</button>;
    }
    

二、兄弟組件通信

1. 通過共同的父組件(狀態提升)
  • 實現:將共享狀態提升到父組件,通過 props 和回調函數傳遞。

  • 示例

    function Parent() {const [sharedData, setSharedData] = useState("");return (<><ChildA onUpdate={setSharedData} /><ChildB data={sharedData} /></>);
    }
    

三、跨層級組件通信

1. Context API
  • 實現:通過 React.createContext 創建上下文,Provider 提供數據,useContextConsumer 消費數據。

  • 示例

    // 創建 Context
    const MyContext = React.createContext();
    // 父組件(Provider)
    function App() {return (<MyContext.Provider value="Hello"><Grandchild /></MyContext.Provider>);
    }
    // 子組件(Consumer)
    function Grandchild() {const value = useContext(MyContext);return <div>{value}</div>; // 輸出:Hello
    }
    
2. 狀態管理庫(Redux、MobX、Zustand)
  • 實現:通過全局 Store 管理狀態,組件通過 useSelectorconnect 訂閱狀態。

  • Redux 示例

    // 定義 Action 和 Reducer
    const increment = () => ({ type: 'INCREMENT' });
    const counterReducer = (state = 0, action) => {if (action.type === 'INCREMENT') return state + 1;return state;
    };
    // 組件中派發 Action
    function Component() {const count = useSelector(state => state);const dispatch = useDispatch();return <button onClick={() => dispatch(increment())}>{count}</button>;
    }
    

四、任意組件通信

1. 事件總線(Event Emitter)
  • 實現:使用第三方庫(如 events)或自定義事件系統。

  • 示例

    // 創建事件總線
    const eventEmitter = new EventEmitter();
    // 組件 A:發布事件
    function ComponentA() {return <button onClick={() => eventEmitter.emit("event", "Data")}>Send</button>;
    }
    // 組件 B:訂閱事件
    function ComponentB() {const [data, setData] = useState("");useEffect(() => {eventEmitter.on("event", setData);return () => eventEmitter.off("event", setData);}, []);return <div>{data}</div>;
    }
    
2. Refs 和命令式方法
  • 實現:父組件通過 ref 調用子組件的方法。

  • 示例

    // 子組件(類組件)
    class Child extends React.Component {method() { console.log("Child method called"); }render() { return <div>Child</div>; }
    }
    // 父組件
    function Parent() {const childRef = useRef();return (<><Child ref={childRef} /><button onClick={() => childRef.current.method()}>Call Method</button></>);
    }
    

五、路由參數傳遞

1. React Router 的 useParamsstate
  • 實現:通過 URL 參數或路由狀態傳遞數據。

  • 示例

    // 路由配置
    <Route path="/user/:id" component={User} />
    // 組件獲取參數
    function User() {const { id } = useParams();const location = useLocation();const data = location.state?.data; // 通過 state 傳遞return <div>User ID: {id}, Data: {data}</div>;
    }
    

六、Hooks 共享邏輯

1. 自定義 Hooks
  • 實現:封裝共享邏輯,多個組件復用同一狀態。

  • 示例

    function useCounter(initialValue) {const [count, setCount] = useState(initialValue);const increment = () => setCount(count + 1);return { count, increment };
    }
    // 組件 A 和 B 共享計數器邏輯
    function ComponentA() {const { count, increment } = useCounter(0);return <button onClick={increment}>A: {count}</button>;
    }
    

總結

場景解決方案適用場景
父子組件Props + 回調函數簡單數據傳遞
兄弟組件狀態提升 + 共同父組件少量兄弟組件
跨層級組件Context API、Redux主題、用戶信息等全局數據
任意組件事件總線、狀態管理庫、消息訂閱發布復雜應用狀態共享
路由跳轉傳參React Router 參數頁面間數據傳遞
邏輯復用自定義 Hooks跨組件共享業務邏輯

選擇建議

  • 簡單場景優先使用 Props 和 Context
  • 中大型項目使用 Redux/Zustand 管理全局狀態。
  • 避免過度使用事件總線,以保持數據流清晰。
    詳細請看鏈接https://tutudev.blog.csdn.net/article/details/144770984

14.React中組件過渡動畫如何實現

在 React 中實現組件過渡動畫,通常需要結合 CSS 和 React 的生命周期控制。以下是 5 種主流實現方案,從基礎到進階,附帶代碼示例:

React 提供了多種方式來實現組件的過渡動畫:

  • React Transition Group:提供 CSSTransitionTransitionGroup,用于實現組件的生命周期過渡動畫。
  • CSS 動畫:對于簡單的動畫,直接使用 CSS 的 transitionanimation 即可。
  • react-spring:適用于需要更加復雜、物理驅動的動畫效果。

方案對比

方案復雜度控制粒度適用場景學習成本
CSS 類名切換簡單顯示/隱藏
react-transition-group通用組件過渡
framer-motion精細復雜交互動畫
自定義 Hooks靈活需要定制邏輯
react-spring精細物理動畫/列表動畫

方案 1:純 CSS 類名切換

適用場景:簡單的顯示/隱藏過渡
原理:通過狀態切換 CSS 類名觸發動畫

// CSS
.fade-enter {opacity: 0;
}
.fade-enter-active {opacity: 1;transition: opacity 300ms;
}
.fade-exit {opacity: 1;
}
.fade-exit-active {opacity: 0;transition: opacity 300ms;
}// React 組件
function App() {const [show, setShow] = useState(false);return (<div><button onClick={() => setShow(!show)}>Toggle</button>{show && (<div className="fade-enter-active">Content with Fade Animation</div>)}</div>);
}

缺點:無法處理卸載動畫(元素會立即消失)


方案 2:react-transition-group 庫

適用場景:完整的進入/離開動畫控制
安裝npm install react-transition-group

import { CSSTransition } from 'react-transition-group';function App() {const [show, setShow] = useState(false);return (<div><button onClick={() => setShow(!show)}>Toggle</button><CSSTransitionin={show}timeout={300}classNames="fade"unmountOnExit><div className="box">Content with Transition</div></CSSTransition></div>);
}
/* 對應 CSS */
.fade-enter {opacity: 0;
}
.fade-enter-active {opacity: 1;transition: opacity 300ms;
}
.fade-exit {opacity: 1;
}
.fade-exit-active {opacity: 0;transition: opacity 300ms;
}

優勢:自動處理動畫生命周期,支持卸載動畫


方案 3:使用動畫庫(如 framer-motion)

適用場景:復雜動畫序列,物理效果動畫
安裝npm install framer-motion

import { motion, AnimatePresence } from 'framer-motion';function App() {const [show, setShow] = useState(false);return (<div><button onClick={() => setShow(!show)}>Toggle</button><AnimatePresence>{show && (<motion.divinitial={{ opacity: 0, y: -20 }}animate={{ opacity: 1, y: 0 }}exit={{ opacity: 0, y: 20 }}transition={{ duration: 0.3 }}>Animated Content</motion.div>)}</AnimatePresence></div>);
}

特點:聲明式 API,支持彈簧物理動畫、手勢交互


方案 4:結合 Hooks 的自定義動畫

適用場景:需要精細控制動畫過程

function useFadeAnimation(duration = 300) {const [isVisible, setIsVisible] = useState(false);const [isAnimating, setIsAnimating] = useState(false);const fadeIn = () => {setIsAnimating(true);setIsVisible(true);};const fadeOut = () => {setIsAnimating(true);setTimeout(() => {setIsVisible(false);setIsAnimating(false);}, duration);};return {isVisible,isAnimating,fadeIn,fadeOut,animationStyle: {opacity: isVisible ? 1 : 0,transition: `opacity ${duration}ms ease-out`}};
}// 使用示例
function Component() {const { isVisible, fadeIn, fadeOut, animationStyle } = useFadeAnimation();return (<div><button onClick={isVisible ? fadeOut : fadeIn}>Toggle</button><div style={animationStyle}>Animated Content</div></div>);
}

方案 5:列表動畫(react-spring)

適用場景:動態列表項的添加/刪除動畫
安裝npm install @react-spring/web

import { useTransition, animated } from '@react-spring/web';function List() {const [items, setItems] = useState([]);const transitions = useTransition(items, {from: { opacity: 0, height: 0 },enter: { opacity: 1, height: 40 },leave: { opacity: 0, height: 0 },});return (<div><button onClick={() => setItems([...items, Date.now()])}>Add Item</button><div className="list">{transitions((style, item) => (<animated.div style={style} onClick={() => setItems(items.filter(i => i !== item))}>Item {item}</animated.div>))}</div></div>);
}

性能優化技巧

  1. 優先使用 transform 和 opacity
    這些屬性不會觸發重排(reflow)

    // 好
    { transform: 'translateX(100px)' }
    // 避免
    { left: '100px' }
    
  2. 啟用 GPU 加速

    .animated-element {transform: translateZ(0);will-change: transform;
    }
    
  3. 合理使用 requestAnimationFrame

    const animate = () => {requestAnimationFrame(() => {// 更新動畫狀態});
    };
    

15.說說你在React項目如何捕獲錯誤的?


在 React 中,我們通常使用錯誤邊界(Error Boundaries)來捕獲運行時的錯誤,并做出相應的處理。錯誤邊界是一個組件,它可以捕獲其子組件樹中的 JavaScript 錯誤、記錄錯誤信息,并展示一個備用 UI。

1. 錯誤邊界(Error Boundaries)

React 提供了一種名為 錯誤邊界 的機制,允許我們在應用中捕獲并處理渲染過程中發生的錯誤。錯誤邊界是一個類組件,必須實現 **componentDidCatch** 生命周期方法,或者在更現代的版本中實現 static **getDerivedStateFromError**

  • getDerivedStateFromError捕獲錯誤降級渲染頁面UI
  • componentDidCatch捕獲錯誤上傳服務器日志
使用錯誤邊界:
  1. 創建一個錯誤邊界組件
import React, { Component } from 'react';class ErrorBoundary extends Component {constructor(props) {super(props);this.state = { hasError: false, errorInfo: null };}static getDerivedStateFromError(error) {// 更新狀態以便下次渲染可以顯示備選UIreturn { hasError: true };}componentDidCatch(error, info) {// 可以將錯誤日志上報到服務器console.error("Error caught by Error Boundary:", error, info);this.setState({ errorInfo: info });}render() {if (this.state.hasError) {// 渲染備選的 UIreturn (<div><h1>Something went wrong.</h1><details style={{ whiteSpace: 'pre-wrap' }}>{this.state.errorInfo && this.state.errorInfo.componentStack}</details></div>);}return this.props.children;}
}export default ErrorBoundary;
  1. 使用錯誤邊界包裹子組件
import React from 'react';
import ErrorBoundary from './ErrorBoundary';
import MyComponent from './MyComponent';function App() {return (<ErrorBoundary><MyComponent /></ErrorBoundary>);
}export default App;
  • getDerivedStateFromError:這個靜態方法會在子組件拋出錯誤時被調用,用來更新組件的狀態并決定是否渲染備用 UI。
  • componentDidCatch:這個生命周期方法用于捕獲錯誤和錯誤的調試信息,可以用來記錄錯誤日志或發送錯誤信息到后臺。

2. 錯誤邊界的應用場景

  • 捕獲子組件的運行時錯誤:錯誤邊界主要用于捕獲子組件的渲染過程中、生命周期方法、構造函數等發生的錯誤。它能保證父組件不會受到影響,從而防止整個應用崩潰。
  • 展示備用 UI:當捕獲到錯誤時,錯誤邊界會展示一個備選的 UI,可以是一個簡單的提示信息,或是一個錯誤日志供用戶或開發人員參考。
  • 避免應用崩潰:如果沒有錯誤邊界,React 會默認讓整個應用崩潰。而使用錯誤邊界后,其他不受影響的部分可以繼續正常渲染,提升用戶體驗。

3. 限制與注意事項

  • 只能捕獲渲染階段的錯誤:錯誤邊界僅能捕獲組件樹中渲染、生命周期方法和構造函數中的錯誤,不能捕獲事件處理、異步代碼(如 setTimeoutfetch)、服務端渲染等地方的錯誤。

  • 事件處理:如果在事件處理程序中發生錯誤,React 并不會自動捕獲,需手動進行錯誤處理(例如使用 try/catch)。

    const handleClick = () => {try {// 執行可能出錯的代碼} catch (error) {console.error('Error occurred in event handler:', error);}
    };
    
  • 不適用于異步代碼:如果你在 componentDidMount 或其他生命周期方法中使用了異步代碼,需要確保對異步操作中的錯誤進行處理。可以使用 try/catch.catch() 來捕獲異常。

    async componentDidMount() {try {const data = await fetchData();this.setState({ data });} catch (error) {console.error('Error fetching data:', error);}
    }
    

4. 結合 Error Boundaries 和日志記錄

通常,錯誤邊界會與日志服務(如 Sentry、LogRocket)配合使用,以便在捕獲到錯誤時,將錯誤信息發送到服務器進行日志記錄和分析。

componentDidCatch(error, info) {// 假設我們用 Sentry 來捕獲錯誤日志Sentry.captureException(error, { extra: info });this.setState({ errorInfo: info });
}

總結:

  • 錯誤邊界 是 React 中專門用于捕獲和處理運行時錯誤的機制。它能夠捕獲子組件的錯誤,避免整個應用崩潰,并顯示一個備用 UI。
  • getDerivedStateFromErrorcomponentDidCatch 是錯誤邊界的核心方法,用來處理錯誤、更新組件狀態和記錄錯誤信息。
  • 錯誤邊界只能捕獲渲染過程中的錯誤,對于事件處理和異步代碼中的錯誤,開發者仍然需要手動處理。
    詳細見鏈接

16.說說對React Refs的理解?應用場景

解釋 refs 的概念、如何使用它以及常見的應用場景。以下是我的回答:


React Refs(引用)是 React 用于直接訪問組件實例或 DOM 元素的一種方式。通過 refs,開發者可以繞過 React 的聲明式數據流,直接與 DOM 元素或 React 組件實例進行交互。

1. Refs 的基本概念

在 React 中,ref 是用來引用 DOM 元素或 React 組件實例的一個特殊對象。通常情況下,React 的數據流是單向的,通過狀態(state)來控制視圖,但有些情況下我們需要直接操作 DOM 或調用組件的方法,這時就可以使用 ref

使用方式:
  1. 訪問 DOM 元素: 使用 React.createRef() 創建一個 ref 對象,并將其賦值給 React 組件中的元素或組件實例。

    import React, { Component } from 'react';class MyComponent extends Component {constructor(props) {super(props);// 創建一個 ref 對象this.myInput = React.createRef();}focusInput = () => {// 直接操作 DOM 元素,調用 focus 方法this.myInput.current.focus();};render() {return (<div><input ref={this.myInput} type="text" /><button onClick={this.focusInput}>Focus the input</button></div>);}
    }export default MyComponent;
    

    這里的 this.myInput 是一個 ref 對象,通過 this.myInput.current 來訪問對應的 DOM 元素。

  2. 訪問類組件實例: 如果 ref 被用來引用一個類組件實例,可以通過 ref.current 來訪問該組件的實例,并調用其方法。

    class ChildComponent extends React.Component {sayHello() {console.log('Hello from Child');}render() {return <div>Child Component</div>;}
    }class ParentComponent extends React.Component {constructor(props) {super(props);this.childRef = React.createRef();}callChildMethod = () => {// 調用子組件的方法this.childRef.current.sayHello();};render() {return (<div><button onClick={this.callChildMethod}>Call Child Method</button><ChildComponent ref={this.childRef} /></div>);}
    }export default ParentComponent;
    

    在這個例子中,this.childRef.currentChildComponent 的實例,我們可以通過它來調用 sayHello 方法。

2. ref 的應用場景

(1) 訪問和操作 DOM 元素

ref 最常見的應用場景是直接訪問 DOM 元素,尤其是在以下情況下:

  • 需要聚焦輸入框(input)。
  • 需要獲取元素的尺寸或位置(例如,測量一個元素的寬高)。
  • 需要實現自定義的滾動行為。

例如,我們可以使用 ref 來聚焦一個輸入框,或是控制動畫時直接訪問 DOM 元素。

(2) 控制子組件的行為

通過 ref,父組件可以訪問子組件的實例,并調用子組件暴露的方法。這對于處理一些業務邏輯(如觸發子組件的生命周期方法、重置子組件狀態等)非常有用。

(3) 觸發動畫

在 React 中,如果需要直接操作 DOM 元素來觸發動畫(例如使用第三方動畫庫或自定義的動畫),ref 可以幫助我們繞過 React 的渲染機制,直接訪問 DOM 元素進行操作。

(4) 表單驗證與焦點管理

在表單中,可以使用 ref 來獲取對輸入框的引用,從而控制焦點的跳轉或驗證某些輸入項的內容。例如,當用戶提交表單時,可以使用 ref 來自動聚焦到第一個錯誤的輸入框,提供更好的用戶體驗。

(5) 集成第三方庫

在一些情況下,React 組件需要與第三方庫集成,尤其是一些需要直接訪問 DOM 或依賴 DOM 操作的庫。ref 可以讓我們將 React 與這些庫進行有效的連接。例如,集成圖表庫、地圖庫、視頻播放器等。

3. useRef 在函數組件中的使用

在函數組件中,useRefref 的鉤子版本。useRef 返回一個可以在整個組件生命周期內持久化的對象,這個對象的 current 屬性指向 DOM 元素或組件實例。

示例:使用 useRef 訪問 DOM 元素
import React, { useRef } from 'react';function MyComponent() {const inputRef = useRef(null);const focusInput = () => {inputRef.current.focus();};return (<div><input ref={inputRef} type="text" /><button onClick={focusInput}>Focus the input</button></div>);
}export default MyComponent;
示例:useRef 訪問組件實例
import React, { useRef } from 'react';function ChildComponent() {const sayHello = () => {console.log('Hello from Child');};return <div>Child Component</div>;
}function ParentComponent() {const childRef = useRef();const callChildMethod = () => {childRef.current.sayHello();};return (<div><button onClick={callChildMethod}>Call Child Method</button><ChildComponent ref={childRef} /></div>);
}export default ParentComponent;

4. ref 的限制與注意事項

  • 不應過度使用 refref 是 React 提供的對 DOM 直接操作的功能,但它打破了 React 的聲明式編程方式。通常應該盡量通過 React 的狀態(state)來驅動組件的行為,避免過度依賴 ref
  • 無法觸發重新渲染:改變 ref 的值不會導致組件重新渲染,因此不應該將其用來存儲和管理 React 中的狀態。

總結:

  • React Refs 提供了一種訪問 DOM 元素或組件實例的方式。

  • ref 的常見應用場景

    • 操作 DOM 元素,如聚焦輸入框、滾動控制。
    • 訪問子組件實例,調用其方法。
    • 與第三方庫集成,特別是需要直接操作 DOM 的庫。
    • 表單驗證和焦點管理。
    • 執行動畫和效果。
  • 在函數組件中,我們使用 useRef 鉤子來創建 ref

17.談談React中的setState的執行機制

在 React 中,setState 是一個用于更新組件狀態的函數。主要在react中是 1.異步更新(如改變狀態后立馬打印值是不會改變的),在原生事件中是同步更新(在原生點擊事件中使用是同步更新的),2.批量更新,React 會將多個 setState 調用合并成一個更新操作,避免了每次狀態改變都導致一次重新渲染。 3.setState 接受一個回調函數作為第二個參數,這個回調函數會在狀態更新并重新渲染完成后調用

1. 異步性

  • setState 通常是異步的,尤其在事件處理器和生命周期方法中。React 并不會立即更新狀態,而是將其加入到一個更新隊列中,并在下一個渲染周期中批量處理這些更新。
  • 這種異步執行的機制可以有效減少不必要的重新渲染,提高性能。

2. 批量更新(Batching)

  • React 會將多個 setState 調用合并成一個更新操作,避免了每次狀態改變都導致一次重新渲染。例如,如果在同一個事件處理函數內調用了多次 setState,React 會合并這些調用,并在渲染過程中只執行一次更新。
  • 批量更新的實現方式依賴于 React 的更新隊列,React 會在事件循環的末尾處理這些更新,并觸發一次新的渲染。

3. 回調函數

  • setState 接受一個回調函數作為第二個參數,這個回調函數會在狀態更新并重新渲染完成后調用。該回調函數的執行是同步的。
  • 回調函數的使用場景通常是需要在狀態更新后執行一些額外的操作,如操作 DOM 或執行網絡請求。
this.setState({ count: this.state.count + 1 }, () => {console.log("State updated and component re-rendered");
});

4. 合并狀態(State Merging)

  • setState 會對更新進行合并。當調用 setState 更新狀態時,它并不會完全覆蓋當前狀態,而是對指定的屬性進行合并。
  • 例如,如果當前狀態是 { count: 0, name: "John" },調用 this.setState({ count: 1 }) 后,最終的狀態會變成 { count: 1, name: "John" },而不是 { count: 1 }

5. 函數式更新

  • 如果你需要基于前一個狀態來更新狀態,可以傳遞一個函數給 setState。這個函數接收當前狀態作為參數,并返回更新后的新狀態。這樣做可以確保在多個 setState 調用中正確計算狀態。
this.setState((prevState) => ({count: prevState.count + 1
}));

6. 優化

  • 在一些場景下,可以通過 shouldComponentUpdateReact.memo 等機制,避免不必要的渲染。
  • 通過 setState 來觸發渲染時,React 會檢查狀態是否真的發生了變化,如果沒有變化,React 會跳過渲染步驟。

總結

setState 在 React 中是一個異步的、批量更新的機制。它通過合并狀態和優化渲染,盡量減少了不必要的 DOM 更新。理解其異步和批量更新的行為,有助于提高 React 應用的性能和正確性。

18.說說React render方法的原理 ?在什么時候會觸發?

React 的 render 方法是其核心機制之一,負責將組件狀態和屬性轉換為用戶界面。它的原理和觸發時機如下:


一、render 方法的原理

  1. 虛擬 DOM(Virtual DOM)

    • render 方法生成的是虛擬 DOM(一個輕量級的 JavaScript 對象),而不是直接操作真實 DOM。
    • 虛擬 DOM 是真實 DOM 的抽象表示,通過 React.createElement 或 JSX 語法生成。
  2. 協調(Reconciliation)

    • 當組件狀態或屬性變化時,React 會調用 render 生成新的虛擬 DOM。
    • React 通過 Diff 算法對比新舊虛擬 DOM 的差異,找出需要更新的部分(最小化 DOM 操作)。
  3. 批量更新與異步性

    • React 會將多個狀態更新合并(批處理),避免頻繁觸發渲染,提升性能。
    • 虛擬 DOM 的對比和真實 DOM 的更新是異步的(React 18 默認啟用并發模式)。

二、render 方法的觸發時機

  1. 初始渲染(Mounting)

    • 組件首次掛載到 DOM 時,會觸發 render 方法。
  2. 狀態更新(State Change)

    • 通過 setState 更新組件狀態時,會觸發重新渲染(除非被 shouldComponentUpdate 阻止)。
  3. 屬性變化(Props Change)

    • 父組件重新渲染導致子組件的 props 變化時,子組件會重新渲染。
  4. Context 更新

    • 如果組件訂閱了 React Context,當 Context 的值變化時,相關組件會重新渲染。
  5. 強制更新(Force Update)

    • 調用 forceUpdate() 方法會跳過 shouldComponentUpdate,強制觸發 render

三、優化渲染的關鍵點

  1. 避免不必要的渲染

    • 類組件:通過 shouldComponentUpdate 或繼承 PureComponent 實現淺比較。
    • 函數組件:使用 React.memo 包裹組件,或通過 useMemo/useCallback 緩存值和函數。
  2. 不可變數據

    • 直接修改狀態(如 this.state.obj.key = 1)不會觸發渲染,必須通過 setState 或更新函數(如 useState 的 setter)。
  3. Key 的合理使用

    • 列表渲染時,為元素分配唯一且穩定的 key,幫助 React 高效識別變化。

四、常見問題

  • 為什么修改了 state,但頁面沒更新?
    可能直接修改了狀態對象(未通過 setState),或未正確觸發渲染(如異步操作未正確處理)。
  • 函數組件如何觸發渲染?
    函數組件通過 useState 的 setter 或 useReducer 的 dispatch 觸發更新。
  • React 18 并發模式下的渲染
    React 會優先處理高優先級更新,可能中斷并重新開始渲染,提升用戶體驗。

總結

即回答以下即可

render 方法是 React 類組件中負責渲染 UI 的核心方法,它在組件的 stateprops 發生變化,通過虛擬 DOM 和 Diff 算法實現高效更新。觸發條件包括狀態/屬性變化、Context 更新等,合理優化可避免性能瓶頸。

  • React 會調用?render?生成新的虛擬 DOM。
  • React 通過?Diff 算法對比新舊虛擬 DOM 的差異,找出需要更新的部分(最小化 DOM 操作)。

19.說說React 中Real DOM 和 Virtual DOM 的區別?優缺點?

React 中的 Real DOM(真實 DOM)和 Virtual DOM(虛擬 DOM)是兩種不同的 DOM 管理機制,它們的核心區別在于操作方式和性能優化策略。以下是它們的區別、優缺點及適用場景:


一、核心區別

特性Real DOMVirtual DOM
本質瀏覽器提供的原生 DOM 對象輕量級的 JavaScript 對象(虛擬表示)
更新方式直接操作 DOM 節點通過 Diff 算法對比差異后批量更新真實 DOM
性能開銷直接操作成本高(重排、重繪)計算差異的 JS 開銷,但減少真實 DOM 操作
更新粒度每次修改觸發完整更新批量合并更新,最小化 DOM 操作
跨平臺能力依賴瀏覽器環境可脫離瀏覽器(如 React Native、SSR)
開發體驗手動管理 DOM,易出錯聲明式編程,自動管理 DOM 更新

二、Real DOM 的優缺點

優點
  1. 直接控制:可直接操作 DOM,適合需要精細控制 DOM 的場景(如復雜動畫)。
  2. 無中間層:無需維護虛擬 DOM 結構,減少內存占用(適用于極簡單頁面)。
缺點
  1. 性能瓶頸:頻繁操作 DOM 會導致重排(Reflow)和重繪(Repaint),性能開銷大。
  2. 開發復雜度:手動管理 DOM 狀態容易出錯(如內存泄漏、事件綁定殘留)。
  3. 跨平臺限制:依賴瀏覽器環境,難以復用邏輯到其他平臺(如移動端)。

三、Virtual DOM 的優缺點

優點
  1. 性能優化

    • 通過 Diff 算法對比差異,僅更新必要的 DOM 節點。
    • 批量合并更新,減少真實 DOM 操作次數(如 React 的自動批處理)。
  2. 聲明式編程

    • 開發者只需關注數據狀態(State/Props),無需手動操作 DOM。
    • 代碼更易維護,邏輯更清晰。
  3. 跨平臺能力

    • 虛擬 DOM 是純 JS 對象,可適配不同渲染目標(瀏覽器、移動端、服務端等)。
缺點
  1. 額外開銷

    • 維護虛擬 DOM 需要內存和計算資源(生成虛擬 DOM、Diff 對比)。
    • 在極簡單場景下,可能不如直接操作 DOM 高效。
  2. 無法完全避免 DOM 操作

    • 最終仍需操作真實 DOM,只是通過中間層優化了流程。

四、為什么 React 選擇 Virtual DOM?

  1. 平衡性能與開發體驗

    • 在大多數應用場景中,虛擬 DOM 的 Diff 算法能顯著減少 DOM 操作,提升性能。
    • 開發者無需手動優化,專注于業務邏輯。
  2. 跨平臺統一

    • 虛擬 DOM 抽象了渲染層,使 React 可同時支持 Web、Native、SSR 等場景。
  3. 聲明式 UI 的優勢

    • 通過狀態驅動 UI,簡化復雜交互的實現(如條件渲染、動態列表)。

五、適用場景

  • Virtual DOM 適用場景

    • 中大型應用,頻繁狀態更新(如社交網絡、儀表盤)。
    • 需要跨平臺復用邏輯(如 React Native)。
    • 團隊協作項目,需統一開發范式。
  • Real DOM 適用場景

    • 極簡單靜態頁面,無需復雜交互。
    • 對性能要求極高的局部操作(如 Canvas 動畫、游戲渲染)。

六、性能對比示例

假設更新 10 個 DOM 節點:

  • Real DOM:直接修改 10 次,觸發 10 次重排/重繪。

  • Virtual DOM

    1. 生成新虛擬 DOM,對比差異(Diff 算法)。
    2. 計算最小修改路徑(如僅更新 2 個節點)。
    3. 批量操作真實 DOM,觸發 1 次重排/重繪。

七、總結

  • Virtual DOM 是 React 的核心優化策略,通過犧牲少量 JS 計算時間,換取真實 DOM 操作的大幅減少,從而提升整體性能。
  • Real DOM 直接操作更底層,但在復雜場景下難以維護,適合特殊需求。
  • 現代前端框架(如 React、Vue)均采用虛擬 DOM 或類似機制,平衡性能與開發效率。

20.React JSX轉換成真實DOM的過程

React 將 JSX 轉換為真實 DOM 的過程是一個分層的、高效的工作流程,涉及多個階段的處理。以下是詳細的轉換過程:


一、JSX 的本質

JSX 是 JavaScript 的語法擴展,本質上是 React.createElement() 的語法糖。它允許開發者以類似 HTML 的語法描述 UI,但最終會被編譯為 JavaScript 對象(即 虛擬 DOM 節點)。

示例:JSX → React.createElement
// JSX 代碼
const element = <div className="title">Hello React</div>;// 編譯后的 JavaScript 代碼
const element = React.createElement("div",{ className: "title" },"Hello React"
);

二、轉換過程的核心步驟

1. JSX 編譯階段(Babel 或 TypeScript)
  • 工具:通過 Babel(@babel/preset-react)或 TypeScript 將 JSX 轉換為 React.createElement() 調用。

  • 輸出:生成 React 元素對象(即虛擬 DOM 節點),結構如下:

    {type: "div",props: {className: "title",children: "Hello React"},// ...其他內部屬性(如 key、ref)
    }
    
2. 構建虛擬 DOM 樹
  • 組件渲染:當組件調用 render()(類組件)或執行函數組件時,遞歸生成嵌套的 React 元素對象樹。

  • 示例

    function App() {return (<div><Header /><Content /></div>);
    }
    

    轉換為:

    React.createElement("div", null, React.createElement(Header, null),React.createElement(Content, null)
    );
    
3. 協調(Reconciliation)與 Diff 算法
  • 觸發時機:當狀態(State)或屬性(Props)變化時,重新生成新的虛擬 DOM 樹。

  • Diff 過程

    • React 對比新舊虛擬 DOM 樹,找出需要更新的部分。
    • 使用 高效 Diff 策略(如按層級比較、Key 優化列表更新)。
4. 生成真實 DOM(提交階段)
  • 首次渲染(Mounting)

    • React 根據虛擬 DOM 樹創建真實 DOM 節點。
    • 通過 ReactDOM.render() 或根組件的 createRoot(React 18+)將 DOM 插入頁面。
  • 更新階段(Updating)

    • 根據 Diff 結果,通過 最小化 DOM 操作(如 appendChildremoveChildupdateAttribute)更新真實 DOM。

三、詳細流程示意圖

JSX 代碼 → Babel 編譯為 React.createElement() → 生成虛擬 DOM 樹(React 元素對象) → 協調(Diff 算法對比新舊樹) → 生成 DOM 更新指令 → 批量操作真實 DOM

四、關鍵角色與 API

  1. React.createElement(type, props, children)

    • 創建 React 元素對象,描述 UI 結構。
  2. ReactDOM.render(element, container)

    • 將虛擬 DOM 轉換為真實 DOM 并掛載到容器(如 document.getElementById('root'))。
  3. 協調器(Reconciler)

    • React 16+ 引入 Fiber 架構,支持可中斷的異步渲染,優化性能。

五、性能優化機制

  1. 批量更新(Batching)

    • 將多次狀態更新合并為一次渲染,減少 DOM 操作次數。
  2. Diff 算法優化

    • 同級節點比較、唯一 Key 標識列表項,避免不必要的節點重建。
  3. 惰性加載與按需渲染

    • 通過 React.lazySuspense 延遲加載組件,減少初始渲染壓力。

六、示例:從 JSX 到真實 DOM 的完整過程

// 1. JSX 代碼
function App() {return <button onClick={() => alert("Clicked!")}>Click Me</button>;
}// 2. 編譯為 React.createElement
React.createElement("button", { onClick: () => alert("Clicked!") }, "Click Me");// 3. 生成 React 元素對象
const buttonElement = {type: "button",props: {onClick: () => alert("Clicked!"),children: "Click Me"}
};// 4. 轉換為真實 DOM(簡化版邏輯)
const domNode = document.createElement(buttonElement.type);
domNode.textContent = buttonElement.props.children;
domNode.addEventListener("click", buttonElement.props.onClick);// 5. 插入頁面
document.getElementById("root").appendChild(domNode);

七、總結

  • JSX 是聲明式 UI 的抽象層,通過編譯轉換為虛擬 DOM 對象。
  • 虛擬 DOM 是中間表示,幫助 React 高效計算最小化 DOM 操作。
  • 協調與 Diff 算法 是 React 性能的核心保障,避免直接操作真實 DOM 的開銷。
  • 開發者只需關注數據(State/Props)的變化,React 自動處理 UI 更新,實現高效開發。

21.React中用戶如何根據不同的權限查看不同的頁面

在 React 中實現基于權限的頁面訪問控制,通常需要結合 路由管理全局狀態權限驗證邏輯。以下是完整的實現方案,包含代碼示例和最佳實踐:


一、核心思路

  1. 權限數據管理:從后端獲取用戶權限信息,存儲到全局狀態(如 Redux、Context API)。
  2. 路由動態配置:根據權限生成可訪問的路由列表。
  3. 路由守衛:在導航前校驗權限,攔截未授權訪問。
  4. 組件級控制:頁面內根據權限隱藏/顯示元素。

二、關鍵優化點

  1. 路由懶加載:使用 React.lazy 提升性能

    const AdminPage = React.lazy(() => import("./pages/AdminPage"));
    
  2. 權限持久化:通過 localStorage 或 Cookie 保存登錄狀態

  3. 接口級鑒權:在 API 請求頭中攜帶 Token,后端驗證權限

  4. 動態菜單渲染:根據權限生成側邊欄菜單項

    const filteredRoutes = routes.filter(route => route.isPublic || (user && route.requiredRoles?.includes(user.role))
    );
    

四、安全注意事項

  1. 前端權限非絕對安全:必須與后端接口權限校驗結合
  2. 敏感數據保護:即使隱藏 UI,也要確保 API 返回數據經過過濾
  3. Token 有效期管理:使用 refreshToken 機制更新訪問令牌

五、完整工作流程

用戶登錄 → 獲取角色權限 → 存儲到全局狀態 → 動態生成路由 → 導航時校驗權限 → 有權限則渲染頁面,否則跳轉登錄/無權限頁

22.React.createClass 與 extends component 的區別是什么

在 React 的發展過程中,React.createClassextends React.Component 是兩種不同的組件定義方式,它們的區別主要體現在語法、功能特性和歷史背景上。以下是詳細對比:


一、核心區別對比表

特性React.createClass (已廢棄)extends React.Component (ES6 類組件)
語法React 專屬 APIES6 類語法
this 綁定自動綁定方法中的 this需手動綁定(或使用箭頭函數/類屬性)
狀態初始化getInitialState() 方法constructor 中通過 this.state 初始化
默認 PropsgetDefaultProps() 方法通過靜態屬性 static defaultProps 定義
PropTypes內部屬性 propTypes靜態屬性 static propTypes
Mixins 支持支持不支持(改用高階組件/Hooks)
生命周期方法早期方法(如 componentWillMount相同方法,但需結合 ES6 類語法
React 版本支持React <15.5,已廢棄React 15.5+ 推薦寫法

二、詳細區別解析

1. 語法與定義方式
  • React.createClass
    通過工廠函數創建組件,是 React 早期 API:

    const MyComponent = React.createClass({render() {return <div>{this.props.text}</div>;}
    });
    
  • extends React.Component
    使用 ES6 類繼承語法:

    class MyComponent extends React.Component {render() {return <div>{this.props.text}</div>;}
    }
    

2. this 綁定問題
  • React.createClass
    自動綁定方法中的 this,無需手動處理:

    const Component = React.createClass({handleClick() {console.log(this); // 正確指向組件實例},render() {return <button onClick={this.handleClick}>Click</button>;}
    });
    
  • extends React.Component
    方法中的 this 默認不綁定,需手動處理(常見方案):

    • 構造函數中綁定

      class Component extends React.Component {constructor(props) {super(props);this.handleClick = this.handleClick.bind(this);}handleClick() { /* ... */ }
      }
      
    • 箭頭函數(類屬性):

      class Component extends React.Component {handleClick = () => { /* ... */ };
      }
      

3. 狀態與 Props 初始化
  • React.createClass
    使用特定方法定義初始狀態和默認 Props:

    const Component = React.createClass({getInitialState() {return { count: 0 };},getDefaultProps() {return { text: "Hello" };}
    });
    
  • extends React.Component
    在構造函數中初始化狀態,通過靜態屬性定義默認 Props:

    class Component extends React.Component {constructor(props) {super(props);this.state = { count: 0 };}static defaultProps = { text: "Hello" };
    }
    

4. Mixins 支持
  • React.createClass
    支持 mixins 實現代碼復用:

    const LoggerMixin = {componentDidMount() {console.log("Component mounted");}
    };const Component = React.createClass({mixins: [LoggerMixin],// ...
    });
    
  • extends React.Component
    不支持 Mixins(ES6 類不支持多重繼承),改用高階組件(HOC)或 Hooks:

    // 高階組件替代方案
    function withLogger(Component) {return class extends React.Component {componentDidMount() {console.log("Component mounted");}render() {return <Component {...this.props} />;}};
    }
    

三、為什么 React.createClass 被廢棄?

  1. ES6 類語法的普及:ES6 類更符合 JavaScript 標準,減少 React 專屬 API 的學習成本。
  2. this 綁定靈活性:手動綁定讓開發者更清楚代碼行為,避免隱式綁定帶來的困惑。
  3. 性能優化:類組件與現代 React 特性(如 Fiber 架構)更兼容。
  4. Mixins 的替代方案:Mixins 易導致命名沖突和代碼耦合,HOC 和 Hooks 提供了更清晰的復用模式。

四、遷移建議

  • 舊項目遷移:使用 create-react-class 包過渡,或手動改為類組件/函數組件。

  • 新項目:直接使用 函數組件 + Hooks(React 16.8+ 推薦):

    function MyComponent({ text }) {const [count, setCount] = useState(0);return <div>{text}</div>;
    }
    

五、總結

  • React.createClass 是 React 早期的組件定義方式,已逐漸被淘汰。
  • extends React.Component 是 ES6 類組件的標準寫法,更符合現代 JavaScript 規范。
  • 函數組件 + Hooks 已成為 React 的主流開發模式,兼具簡潔性和功能性。

23.React事件與普通的html事件有什么區別

React 事件與普通 HTML 事件在實現機制和使用方式上有顯著區別,以下是主要差異及詳細說明:


一、核心區別對比表

特性React 事件普通 HTML 事件
事件命名駝峰命名(如 onClick全小寫(如 onclick
事件綁定JSX 中直接綁定函數(如 onClick={fn}HTML 屬性或 addEventListener
事件對象合成事件(SyntheticEvent原生 DOM 事件對象
事件委托自動委托到根容器(React 17+)需手動委托(如 addEventListener
默認行為阻止必須顯式調用 e.preventDefault()可通過 return falsee.preventDefault()
this 綁定需手動綁定(類組件)或使用箭頭函數默認指向觸發事件的元素(非嚴格模式)
性能優化事件池復用,異步需 e.persist()無復用機制
跨瀏覽器兼容性統一封裝,無需處理瀏覽器差異需手動處理(如 IE 兼容性)

二、詳細區別解析

1. 事件命名與綁定方式
  • React 事件

    • 使用駝峰命名(如 onClickonChange)。

    • 在 JSX 中直接綁定函數,無需字符串:

      <button onClick={handleClick}>Click</button>
      
  • HTML 事件

    • 使用全小寫屬性名(如 onclick)。

    • 通過字符串或 addEventListener 綁定:

      <button onclick="handleClick()">Click</button>
      <!-- 或 -->
      <script>document.querySelector("button").addEventListener("click", handleClick);
      </script>
      

2. 事件對象(Event Object)
  • React 事件

    • 使用 合成事件(SyntheticEvent) ,是對原生事件的跨瀏覽器包裝。
    • 通過事件池復用,提升性能(異步訪問需調用 e.persist())。
    • 統一接口,無需處理瀏覽器差異(如 e.stopPropagation() 在所有瀏覽器中一致)。
    const handleClick = (e) => {e.preventDefault(); // 阻止默認行為e.persist();        // 保留事件對象供異步使用
    };
    
  • HTML 事件

    • 直接使用原生 DOM 事件對象。
    • 需處理瀏覽器兼容性(如 IE 的 window.event)。
    element.onclick = function(e) {e = e || window.event; // 處理 IE 兼容e.stopPropagation();
    };
    

3. 事件委托機制
  • React 事件

    • React 17+ 將事件委托到根容器(如 ReactDOM.createRoot() 掛載的節點),而非 document
    • 減少內存占用,支持多 React 版本共存。
  • HTML 事件

    • 需手動實現事件委托:

      document.getElementById("parent").addEventListener("click", (e) => {if (e.target.matches("button")) {// 處理子元素點擊}
      });
      

4. this 綁定問題
  • React 事件(類組件)

    • 類組件中需手動綁定 this,或使用箭頭函數:

      class Button extends React.Component {handleClick() { console.log(this); } // this 默認未綁定render() {return (<button onClick={this.handleClick.bind(this)}>Bind</button>// 或<button onClick={() => this.handleClick()}>Arrow</button>);}
      }
      
  • HTML 事件

    • 內聯事件處理函數中 this 默認指向元素:

      <button onclick="console.log(this)">Click</button> <!-- 輸出按鈕元素 -->
      
    • 通過 addEventListener 綁定時,this 默認指向元素:

      button.addEventListener("click", function() {console.log(this); // 輸出按鈕元素
      });
      

5. 默認行為阻止
  • React 事件

    • 必須顯式調用 e.preventDefault()
    const handleSubmit = (e) => {e.preventDefault(); // 阻止表單提交
    };
    
  • HTML 事件

    • 可通過 return false(僅限屬性綁定)或 e.preventDefault()

      <form onsubmit="return false"> <!-- 阻止提交 -->
      <!-- 或 -->
      <form onsubmit="handleSubmit(event)">
      <script>function handleSubmit(e) {e.preventDefault();}
      </script>
      

三、React 事件系統的優勢

  1. 跨瀏覽器一致性
    合成事件屏蔽了瀏覽器差異(如 event.target 的兼容性處理)。
  2. 性能優化
    事件池機制減少對象創建開銷,提升性能。
  3. 聲明式綁定
    JSX 中直接綁定函數,代碼更簡潔。
  4. 自動清理
    組件卸載時自動解綁事件,避免內存泄漏。

四、示例對比

React 事件處理
function App() {const handleClick = (e) => {e.preventDefault();console.log("React event:", e.nativeEvent);};return <a href="#" onClick={handleClick}>React Link</a>;
}
HTML 事件處理
<a href="#" onclick="handleClick(event)">HTML Link</a>
<script>function handleClick(e) {e.preventDefault();console.log("HTML event:", e);}
</script>

五、總結

  • React 事件 是跨瀏覽器封裝的高層抽象,提供一致的開發體驗。
  • HTML 事件 直接操作原生 DOM,需處理瀏覽器差異和手動管理。
  • 掌握兩者區別有助于避免常見陷阱(如 this 綁定、默認行為處理),并寫出更健壯的 React 代碼。

24.為什么useState要使用數組而非對象?

在 React 的 useState 中,返回數組 [state, setState] 而非對象 { state, setState },是出于設計上的靈活性和開發便利性考慮。以下是具體原因和解析:


一、核心原因:解構賦值的靈活性

1. 自由命名變量
  • 使用數組解構時,開發者可以自定義變量名,避免對象屬性名的強制約束:

    // 數組解構:自由命名
    const [count, setCount] = useState(0);
    const [user, setUser] = useState({ name: "John" });// 對象解構(假設):必須固定屬性名
    const { state: count, setState: setCount } = useState(0); // 冗長且不直觀
    
2. 多狀態場景更簡潔
  • 當組件需要多個 useState 時,數組解構更清晰:

    // 數組解構:直接且無命名沖突
    const [count, setCount] = useState(0);
    const [text, setText] = useState("");// 對象解構(假設):需要重復屬性名
    const { state: count, setState: setCount } = useState(0);
    const { state: text, setState: setText } = useState(""); // 命名沖突!
    

二、對比:數組 vs 對象返回值的差異

特性數組返回值對象返回值(假設)
變量命名自由命名(如 [count, setCount]固定屬性名(如 { state, setState }
多狀態聲明無命名沖突需重復解構或別名,易沖突
代碼簡潔性更簡潔更冗長
一致性所有 Hook 統一返回數組(如 useReducer無統一標準

四、示例:數組解構的實際優勢

1. 自定義命名
// 狀態1:計數器
const [count, setCount] = useState(0); // 狀態2:輸入框文本
const [text, setText] = useState("");// 狀態3:用戶數據
const [user, setUser] = useState({ name: "John" });
2. 忽略不需要的值
  • 若 Hook 返回對象,無法跳過中間值;數組解構允許占位符:

    // 假設 useSomeHook 返回 [a, b, c]
    const [a, , c] = useSomeHook(); // 忽略第二個值// 若返回對象,需解構所有屬性
    const { a, b, c } = useSomeHook(); // 必須處理 b
    

總結

  • 返回數組:為了提供命名自由多狀態聲明的簡潔性
  • 參數類型自由useState 的初始值可以是任意類型(數字、對象、數組等),與返回值形式無關。
  • 設計一致性:所有 React Hook 遵循相似的返回值約定,降低學習成本。

因此,const [xxx, setXxx] = useState(0) 中的數組解構是 React 的刻意設計,旨在提升開發體驗和代碼靈活性。

25.React為什么要使用hooks

React 引入 Hooks 的核心目標是解決類組件的設計缺陷,提升代碼的可維護性、邏輯復用性和開發體驗。以下是具體原因和 Hooks 的核心優勢:

總結:為什么使用 Hooks?

  • 代碼更簡潔:消除類組件的冗余代碼。
  • 邏輯更復用:自定義 Hook 實現高效邏輯共享。
  • 開發更高效:函數式編程減少心智負擔。
  • 未來更友好:適配 React 新特性,代表未來方向。

一、解決類組件的痛點

1. 邏輯復用困難
  • 類組件時代:通過高階組件(HOC)、Render Props 等模式復用邏輯,導致嵌套地獄(Wrapper Hell)和代碼冗余。

  • Hooks 方案:通過自定義 Hook(如 useFetchuseAuth)直接復用狀態邏輯,無嵌套、更簡潔。

    // 自定義 Hook:復用數據請求邏輯
    function useFetch(url) {const [data, setData] = useState(null);useEffect(() => {fetch(url).then(res => res.json()).then(setData);}, [url]);return data;
    }// 在組件中使用
    function UserProfile({ userId }) {const user = useFetch(`/api/users/${userId}`);return <div>{user?.name}</div>;
    }
    
2. 生命周期方法分散邏輯
  • 類組件問題:相關邏輯被拆分到不同生命周期(如 componentDidMountcomponentDidUpdate),代碼難以維護。

    class Timer extends React.Component {componentDidMount() {this.timer = setInterval(() => {/* 更新狀態 */}, 1000);}componentWillUnmount() {clearInterval(this.timer);}// 相關邏輯被拆分到多個方法
    }
    
  • Hooks 方案:用 useEffect 合并生命周期邏輯,按功能組織代碼。

    function Timer() {useEffect(() => {const timer = setInterval(() => {/* 更新狀態 */}, 1000);return () => clearInterval(timer); // 清理邏輯}, []);
    }
    
3. this 綁定問題
  • 類組件問題:需要手動綁定 this,或使用箭頭函數,易出錯且代碼冗余。

    class Button extends React.Component {handleClick() { /* 需要綁定 this */ }render() {return <button onClick={this.handleClick.bind(this)}>Click</button>;}
    }
    
  • Hooks 方案:函數組件無 this,直接使用閉包變量,避免綁定問題。

    function Button() {const handleClick = () => {/* 直接訪問 props/state */};return <button onClick={handleClick}>Click</button>;
    }
    

二、Hooks 的核心優勢

1. 函數式編程范式
  • 更簡潔的代碼:函數組件比類組件代碼量更少,結構更清晰。
  • 更少的樣板代碼:無需定義 classconstructorrender 方法。
2. 邏輯與 UI 解耦
  • 關注點分離:通過自定義 Hook 將業務邏輯抽離,UI 組件只負責渲染。

    // 邏輯復用層:useCounter
    function useCounter(initialValue) {const [count, setCount] = useState(initialValue);const increment = () => setCount(c => c + 1);return { count, increment };
    }// UI 組件:只負責渲染
    function Counter() {const { count, increment } = useCounter(0);return <button onClick={increment}>{count}</button>;
    }
    
3. 更好的 TypeScript 支持
  • 函數組件 + Hooks 的類型推斷更直觀,避免類組件中 this 類型問題。
4. 性能優化更精細
  • 通過 useMemouseCallback 精確控制重渲染,避免不必要的計算。

三、Hooks 如何推動 React 生態

  1. 漸進式遷移:支持類組件與函數組件共存,項目可逐步遷移。
  2. 社區創新:自定義 Hooks 催生了大量開源庫(如 react-queryahooks)。
  3. 未來兼容性:React 新特性(如并發模式)優先支持 Hooks。

四、Hooks 的局限性

  • 學習曲線:需理解閉包、依賴數組等概念。
  • 規則限制:必須遵守 Hooks 的調用順序(不能在條件或循環中使用)。

Hooks 是 React 對“關注點分離”和“代碼復用”的終極答案,讓開發者以更優雅的方式構建可維護的現代應用。

26.在React中如何實現代碼分割

在 React 中實現代碼分割(Code Splitting)是優化應用性能的關鍵手段,能夠減少初始加載時間,提升用戶體驗。以下是 5 種核心方法及詳細實現步驟:


一、使用 React.lazy + Suspense(組件級分割)

適用場景:按需加載非首屏組件(如彈窗、復雜模塊)。
實現步驟

  1. React.lazy 動態導入組件。
  2. Suspense 包裹組件,提供加載狀態。
import React, { Suspense } from 'react';// 動態導入組件(Webpack 自動分割代碼)
const LazyComponent = React.lazy(() => import('./LazyComponent'));function App() {return (<div><Suspense fallback={<div>Loading...</div>}><LazyComponent /> {/* 使用時才加載 */}</Suspense></div>);
}

二、基于路由的代碼分割(路由級分割)

適用場景:SPA 中按路由拆分代碼(如不同頁面)。
實現步驟(以 React Router v6 為例):

import { BrowserRouter, Routes, Route } from 'react-router-dom';
import React, { Suspense } from 'react';const Home = React.lazy(() => import('./routes/Home'));
const About = React.lazy(() => import('./routes/About'));function App() {return (<BrowserRouter><Suspense fallback={<div>Loading Page...</div>}><Routes><Route path="/" element={<Home />} /><Route path="/about" element={<About />} /></Routes></Suspense></BrowserRouter>);
}

三、動態 import() 語法(手動控制加載時機)

適用場景:在事件觸發時加載代碼(如點擊按鈕后加載模塊)。
實現步驟

function App() {const [module, setModule] = useState(null);const loadModule = async () => {const { default: DynamicModule } = await import('./DynamicModule');setModule(<DynamicModule />);};return (<div><button onClick={loadModule}>Load Module</button>{module}</div>);
}

四、使用第三方庫 @loadable/component

適用場景:更靈活的代碼分割(支持服務端渲染、預加載等)。
實現步驟

  1. 安裝庫:

    npm install @loadable/component
    
  2. 使用 loadable 包裝組件:

    import loadable from '@loadable/component';const LoadableComponent = loadable(() => import('./Component'), {fallback: <div>Loading...</div>,
    });function App() {return <LoadableComponent />;
    }
    

五、Webpack 配置優化(文件名哈希、分組)

適用場景:精細化控制代碼塊(chunk)生成策略。
配置示例webpack.config.js):

module.exports = {output: {filename: '[name].[contenthash].js',chunkFilename: '[name].[contenthash].chunk.js',},optimization: {splitChunks: {chunks: 'all',cacheGroups: {vendors: {test: /[\/]node_modules[\/]/,name: 'vendors',},},},},
};

六、最佳實踐與注意事項

  1. 優先分割大組件/路由:對性能提升最明顯的部分優先處理。

  2. 預加載關鍵資源:使用 webpackPrefetch 提前加載未來可能需要的模塊。

    const Component = React.lazy(() => import(/* webpackPrefetch: true */ './Component'
    ));
    
  3. 錯誤邊界處理:用 ErrorBoundary 捕獲加載失敗。

    class ErrorBoundary extends React.Component {state = { hasError: false };static getDerivedStateFromError() { return { hasError: true }; }render() {return this.state.hasError ? <div>Error!</div> : this.props.children;}
    }// 使用
    <ErrorBoundary><Suspense fallback="Loading..."><LazyComponent /></Suspense>
    </ErrorBoundary>
    
  4. 避免過度分割:每個分割塊會增加 HTTP 請求,平衡數量與體積。


總結

  • 輕量級組件React.lazy + Suspense
  • 路由級分割 → 結合 React Router
  • 精細控制@loadable/component 或動態 import()
  • 打包優化 → Webpack 配置

通過合理應用代碼分割,可顯著提升 React 應用的加載速度與運行時性能。

27.談談你對React中Fragment的理解

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/diannao/70898.shtml
繁體地址,請注明出處:http://hk.pswp.cn/diannao/70898.shtml
英文地址,請注明出處:http://en.pswp.cn/diannao/70898.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

數據安全管理的AI工具有哪些?

數據安全管理的AI工具在不斷演進&#xff0c;它們憑借強大的算法和學習能力&#xff0c;為企業筑起了一道堅不可摧的數字防線。 在數據安全管理領域&#xff0c;AI工具的應用日益廣泛&#xff0c;以下是一些常見的工具及其功能&#xff1a; AI驅動的數據分類與標記 工具: Micr…

Matplotlib 繪圖標記

Matplotlib 繪圖標記 引言 Matplotlib 是一個功能強大的 Python 繪圖庫&#xff0c;廣泛用于數據可視化。在 Matplotlib 中&#xff0c;繪圖標記&#xff08;markers&#xff09;是數據點在圖表中顯示的方式。正確的使用繪圖標記可以增強圖表的可讀性和美觀性。本文將詳細介紹…

【DeepSeek問答】QProcess::start是異步的嗎?會使UI卡頓嗎?

好的&#xff0c;我現在需要回答用戶關于QProcess::start是否是異步的&#xff0c;以及它是否會導致UI卡頓的問題。用戶之前已經詢問過如何開發一個調用7-Zip的Qt應用&#xff0c;并且我給出了示例代碼。現在用戶可能在使用過程中遇到了問題&#xff0c;或者想更深入了解QProce…

Android 常用命令和工具解析之存儲相關

1 基本概念 2 命令解讀 2.1 adb shell df df 命令主要用于需要檢查文件系統上已使用和可用的磁盤空間的數量。如果沒有指定文件名&#xff0c;則顯示在當前所有掛載的文件系統上可用的空間。其原理是從proc/mounts 或 /etc/mtab 中檢索磁盤信息。 注意&#xff1a;df命令并…

使用ZFile打造屬于自己的私有云系統結合內網穿透實現安全遠程訪問

文章目錄 前言1.關于ZFile2.本地部署ZFile3.ZFile本地訪問測試4.ZFile的配置5.cpolar內網穿透工具安裝6.創建遠程連接公網地址7.固定ZFile公網地址 前言 在數字化的今天&#xff0c;我們每個人都是信息的小能手。無論是職場高手、攝影達人還是學習狂人&#xff0c;每天都在創造…

HarmonyOS 5.0應用開發——鴻蒙接入高德地圖實現POI搜索

【高心星出品】 文章目錄 鴻蒙接入高德地圖實現POI搜索運行結果&#xff1a;準備地圖編寫ArkUI布局來加載HTML地圖 鴻蒙接入高德地圖實現POI搜索 在當今數字化時代&#xff0c;地圖應用已成為移動設備中不可或缺的一部分。隨著鴻蒙系統的日益普及&#xff0c;如何在鴻蒙應用中…

idea + Docker + 阿里鏡像服務打包部署

一、下載docker desktop軟件 官網下載docker desktop&#xff0c;需要結合wsl使用 啟動成功的畫面(如果不是這個畫面例如一直處理start或者是stop需要重新啟動&#xff0c;不行就重啟電腦) 打包成功的鏡像在這里&#xff0c;如果頻繁打包會導致磁盤空間被占滿&#xff0c;需…

IP---網絡類型

這只是IP的其中一塊內容-網絡類型&#xff0c;IP還有更多內容可以查看IP專欄&#xff0c;前一章內容為訪問服務器流程&#xff0c;可通過以下路徑查看IP----訪問服務器流程-CSDN博客&#xff0c;歡迎指正 2.網絡類型 網絡類型---根據二層&#xff08;數據鏈路層&#xff09;所…

【監督學習】ARIMA預測模型步驟及matlab實現

ARIMA預測模型 ARIMA預測模型1.算法步驟2.參數選擇(1)拖尾截尾判斷法(2) AIC 準則(3) BIC 準則 3.MATLAB 實現參考資料 ARIMA預測模型 #mermaid-svg-mDhjwpnuA0YcEGnE {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg…

使用git管理uniapp項目

1.本地管理 1. 在項目根目錄中新建 .gitignore 忽略文件&#xff0c;并配置如下&#xff1a; # 忽略 node_modules 目錄 /node_modules /unpackage/dist 2. 打開終端&#xff0c;切換到項目根目錄中&#xff0c;運行如下的命令&#xff0c;初始化本地 Git 倉庫&#xff1…

Unity中動態切換光照貼圖的方法

關鍵代碼&#xff1a;LightmapSettings.lightmaps lightmapDatas; LightmapData中操作三張圖&#xff1a;lightmapColor,lightmapDir,以及一張ShadowMap 這里只操作前兩張&#xff1a; using UnityEngine; using UnityEngine.EventSystems; using UnityEngine.UI;public cl…

C# 運算符

C# 運算符 在C#編程語言中,運算符是用于執行數學或邏輯運算的符號。它們是構建程序邏輯和表達式的基石。C#支持多種類型的運算符,包括算術運算符、關系運算符、邏輯運算符、位運算符、賦值運算符、自增自減運算符以及一些特殊運算符。 算術運算符 算術運算符用于執行基本的…

1.2 Kaggle大白話:Eedi競賽Transformer框架解決方案02-GPT_4o生成訓練集缺失數據

目錄 0. 本欄目競賽匯總表1. 本文主旨2. AI工程架構3. 數據預處理模塊3.1 配置數據路徑和處理參數3.2 配置API參數3.3 配置輸出路徑 4. AI并行處理模塊4.1 定義LLM客戶端類4.2 定義數據處理函數4.3 定義JSON保存函數4.4 定義數據分片函數4.5 定義分片處理函數4.5 定義文件名排序…

pycharm遠程連接服務器運行pytorch

Linux部署pytorch 背景介紹 不同的開源代碼可能需要不同的實驗環境和版本&#xff0c;這時候的確體現出Anaconda管理環境的好處了&#xff0c;分別搞一個獨立環境方便管理。 有的教程建議選擇較舊的版本&#xff0c;但筆者建議在條件允許的情況下安裝最新版&#xff0c;本次…

Python開發 Flask框架面試題及參考答案

目錄 Flask 的核心設計理念是什么?與 Django 相比有哪些顯著差異? 解釋 Flask 框架的核心理念及其作為 “微框架” 的優缺點 Flask 的依賴庫有哪些?簡述 Werkzeug 和 Jinja2 的作用 什么是 WSGI?Flask 如何基于 WSGI 實現服務端與應用的交互 解釋 RESTful API 的設計原…

從“Switch-case“到“智能模式“:C#模式匹配的終極進化指南

當代碼開始"思考" 你是否厭倦了層層嵌套的if-else地獄&#xff1f;是否想過讓代碼像偵探推理一樣優雅地解構數據&#xff1f;C#的模式匹配正是這樣一把瑞士軍刀&#xff0c;從C# 7.0到C# 12&#xff0c;它已悄然進化成改變編程范式的利器。 一、模式匹配的三重境界…

組件注冊方式、傳遞數據

組件注冊 一個vue組件要先被注冊&#xff0c;這樣vue才能在渲染模版時找到其對應的實現。有兩種注冊方式&#xff1a;全局注冊和局部注冊。&#xff08;組件的引入方式&#xff09; 以下這種屬于局部引用。 組件傳遞數據 注意&#xff1a;props傳遞數據&#xff0c;只能從父…

ROS的action通信——實現階乘運算(三)

在ROS中除了常見的話題(topic&#xff09;通信、服務(server)通信等方式&#xff0c;還有action通信這一方式&#xff0c;由于可以實時反饋任務完成情況&#xff0c;該通信方式被廣泛運用于機器人導航等任務中。本文將通過三個小節的分享&#xff0c;實現基于action通信的階乘運…

四款 AI 協作辦公工具,AI工具庫革新辦公效率

在數字化辦公時代&#xff0c;AI 技術正深刻改變著我們的工作方式。AIDH.NETAI工具庫匯聚了眾多先進的工具&#xff0c;今天我們來了解 AI協作辦公工具&#xff0c;探索它們如何助力企業和團隊在辦公場景中脫穎而出。 Taskade&#xff1a;智能工作流的領航者 Taskade 是一款將…

vue2 h5 畫高德地圖電子圍欄

使用前請先申請高德地圖key JavaScript API | 騰訊位置服務 npm install lodash-es效果圖 子組件代碼 <template><div class"fence-container"><div v-if"loading" class"map-loading"><div class"loader">…