答案見(jiàn)最下方:
一、請(qǐng)簡(jiǎn)單介紹一下React18有哪些更新。
二、JSX是什么,它和JS有什么區(qū)別?
三、為什么在文件中沒(méi)有使用React ,也要在文件頂部import React from “react”
四、請(qǐng)說(shuō)一下React 事件機(jī)制和JS原生事件機(jī)制的區(qū)別?
五、React 事件與原生事件的執(zhí)行順序?
六、請(qǐng)說(shuō)說(shuō)React Hooks解決了什么問(wèn)題? 函數(shù)組件與類組件的區(qū)別
七、為什么 useState 要使用數(shù)組而不是對(duì)象
八、說(shuō)一下常用的Hook
九、Hook 的使用限制有哪些?
十、useEffect 與 useLayoutEffect 的區(qū)別
十一、如何自定義Hook
十二、React 高階組件是什么,和普通組件有什么區(qū)別,適用什么場(chǎng)景
十三、React 高階組件、Render.props、Hooks 有什么區(qū)別,為什么要不斷迭代
十四、React組件通信有哪些方式
十五、React中props和state有什么區(qū)別?React中的props為什么是只讀的?
十六、React 16.X 中 props 改變后在哪個(gè)生命周期中處理
十七、React 性能優(yōu)化在哪個(gè)生命周期?它優(yōu)化的原理是什么
十八、React 中 keys 的作用是什么?
十九、React 中 refs 的作用是什么?
二十、說(shuō)說(shuō)React diff 算法
二十一、React 與 Vue 的 diff 算法有何不同?
二十二、React setState 調(diào)用的原理
二十三、setState 第一個(gè)參數(shù)有兩種傳遞方式 1.一個(gè)對(duì)象 2. 一個(gè)函數(shù) 這兩種寫(xiě)法有什么區(qū)別呢?useState呢
二十四、React Hooks 工作機(jī)制深度解析
二十五、React中setState的第二個(gè)參數(shù)作用是什么?
二十六、React中的setState和replaceState的區(qū)別是什么?
二十七、React中的setState批量更新的過(guò)程是什么?
二十八、React 中 setState 什么時(shí)候是同步的,什么時(shí)候是異步的?
二十九、React Hooks 設(shè)計(jì)動(dòng)機(jī)與設(shè)計(jì)模式解析
三十、React實(shí)現(xiàn)緩存的方式有哪些?他們有什么區(qū)別?
三十一、對(duì)React中Fragment的理解,它的使用場(chǎng)景是什么?
三十二、什么是受控組件和非控組件
三十三、React.Component 和 React.PureComponent 的區(qū)別
三十四、對(duì)有狀態(tài)組件和無(wú)狀態(tài)組件的理解及使用場(chǎng)景
三十五、useffect 模擬生命周期
三十六、哪些方法會(huì)觸發(fā) React 重新渲染?重新渲染 render 會(huì)做些什么?
三十七、hooks父組件怎么調(diào)用子組件的方法?
三十八、React的嚴(yán)格模式如何使用,有什么用處?
三十九、React的設(shè)計(jì)思想是什么
四十、為什么React自定義組件首字母要大寫(xiě)
四十一、React組件為什么不能返回多個(gè)元素
四十二、簡(jiǎn)述React的生命周期
四十三、對(duì)React的插槽(Portals)的理解,如何使用,有哪些使用場(chǎng)景
四十四、父組件和子組件的生命周期執(zhí)行順序
四十五、React聲明組件有哪幾種方法,有什么不同?
四十六、React組件的構(gòu)造函數(shù)有什么作用?它是必須的嗎?
四十七、React性能優(yōu)化手段
四十八、在React中如何避免不必要的render?
四十九、React-Router工作原理
五十、對(duì) Redux 的理解,主要解決什么問(wèn)題
五十一、Redux 原理及工作流程
五十二、Redux 中異步的請(qǐng)求怎么處理
五十三、Fiber架構(gòu)
五十四、React錯(cuò)誤處理?
五十五、數(shù)據(jù)雙向綁定的原理
五十六、怎么動(dòng)態(tài)導(dǎo)入組件,按需加載,代碼分割?
五十七、React的狀態(tài)提升是什么?使用場(chǎng)景有哪些?
五十八、對(duì) React 和 Vue 的理解,它們的異同
五十九、對(duì)React SSR的理解
六十、react-redux中的connect怎么實(shí)現(xiàn)?
六十一、diff的時(shí)間復(fù)雜度是多少,為什么?
一、請(qǐng)簡(jiǎn)單介紹一下React18有哪些更新。
1. 并發(fā)渲染
并發(fā)渲染實(shí)際上不是一個(gè)功能,而是React的新的底層渲染機(jī)制,使得React可以同時(shí)準(zhǔn)備多套UI。
并發(fā)模式的一個(gè)最重要的特性渲染可中斷。React18之前的頁(yè)面渲染是一個(gè)一個(gè)同步事務(wù)進(jìn)行處理,是一旦開(kāi)始就無(wú)法中斷。
并發(fā)模式意味著,所有的UI顯示任務(wù)都是可以被中斷的,它可以先執(zhí)行某個(gè)渲染更新然后掛起等待。這意味著渲染必須在DOM樹(shù)計(jì)算完畢以后再執(zhí)行。React就可以在這期間準(zhǔn)備新的更新,而不影響操作的執(zhí)行。
另一個(gè)特性則是可重用性,React可以在渲染某個(gè)更新時(shí)移除一部分UI,然后在稍后的更新中把它再加回來(lái)。
2. 過(guò)渡更新
通常為了區(qū)分緊急更新和非緊急更新,一般來(lái)說(shuō)緊急更新主要是比如輸入、按壓、點(diǎn)擊等需要馬上表現(xiàn)出來(lái)的更新,過(guò)渡更新則是用于查詢結(jié)果之類的不那么需要緊急表現(xiàn)得更新操作,通常情況下一般的更新操作都是緊急的,如果需要開(kāi)啟過(guò)渡更新,則需要調(diào)用例如useStransition和startStransition,緊急更新會(huì)中斷過(guò)渡更新,多個(gè)過(guò)渡更新被中斷,react會(huì)拋棄之前的更新內(nèi)容,僅僅渲染最新的內(nèi)容。
3. 自動(dòng)批處理
React18之前,react不會(huì)對(duì)promise、setTimeout、事件處理之內(nèi)的操作進(jìn)行批處理,只會(huì)對(duì)promise、setTimeout、事件處理以外的操作進(jìn)行批處理,react18則優(yōu)化了這一部分,使得在React的任何地方都可以進(jìn)行自動(dòng)批處理,當(dāng)然你也可以使用flushSync退出批處理.
flushSync(()=>setState(val=>val+1))
對(duì)于class有影響,因?yàn)樵赾lass中可以在18之前在setTimeout和promise中是不會(huì)自動(dòng)批處理的,則在setTimeout中setState之后是可以拿到this.state最新的值的,但是在react18因?yàn)槭亲詣?dòng)批處理則現(xiàn)在是拿不到this.state的最新的值的,如果需要獲取的話則需要用flushSync.
setTimeout(()=>{
this.setState({count}=>count+1);
console.log(this.state.count) //1
this.setState({age}=>{age:20})
},100) //17
setTimeout(()=>{
this.setState({count}=>count+1);
console.log(this.state.count) //0
this.setState({age}=>{age:20})
},100) //18
setTimeout(()=>{
flushSync(()=> {
this.setState({count}=>count+1)}
}
console.log(this.state.count) //1
this.setState({age}=>{age:20})
},100) //18
4. useId Hook
用于客戶端和服務(wù)端渲染生成唯一的Id。
5. startTransition Api / useStransitionHook
開(kāi)啟過(guò)渡渲染
6. useDeferredValue Hook
用于延遲更新某些UI部分。比如結(jié)合Suspense做異步加載.
你可以將useDeferredValue作為性能優(yōu)化的手段,當(dāng)你的某個(gè)UI渲染很慢,你又希望其避免阻塞其他渲染,則你就可以用useDeferredVaue。
7. Suspense 支持SSR
底層實(shí)現(xiàn)依賴錯(cuò)誤邊界組件,當(dāng)Suspense捕獲到子組件的promise,會(huì)優(yōu)先展示fallback的ui,直到返回resolved狀態(tài)再重新渲染。
二、JSX是什么,它和JS有什么區(qū)別?
1、語(yǔ)法:JSX的語(yǔ)法類似于HTML; JS是一門編程語(yǔ)言。
2、功能:JSX是定義React組件的UI;JS是用于邏輯的控制和操作。
3、處理:JSX會(huì)被轉(zhuǎn)換為JS,不可直接執(zhí)行;JS是可直接執(zhí)行;
4、表達(dá)式:JSX中{}中可以嵌入JS表達(dá)式;
5、不用JSX怎么生成React組件呢?
React.createElement('div','組件')
三、為什么在文件中沒(méi)有使用React ,也要在文件頂部import React from “react”
因?yàn)槭褂昧薐SX;
四、請(qǐng)說(shuō)一下React 事件機(jī)制和JS原生事件機(jī)制的區(qū)別?
1、綁定方式不一樣:一個(gè)是綁定到JSX上面,一個(gè)是綁定到HTML上、動(dòng)態(tài)綁定或者adeventListener;
2、事件對(duì)象不一樣:React做了兼容處理,可以兼容所有的瀏覽器;原生需要自己做兼容處理;
3、事件傳播不一樣:React事件默認(rèn)只支持冒泡,捕獲階段通過(guò) onClickCapture 監(jiān)聽(tīng);JS原生支持冒泡和捕獲;
4、事件處理不一樣:React是把所以事件集中在一起,更加節(jié)約性能;JS原生是每個(gè)事件綁定到對(duì)應(yīng)的節(jié)點(diǎn)上面;
5、解綁不一樣:React不需要解綁操作;JS原生需要解綁操作;
五、React 事件與原生事件的執(zhí)行順序?
原生事件優(yōu)于React事件,首先執(zhí)行捕獲階段、然后是冒泡階段,先執(zhí)行原生的捕獲,然后是React的捕獲,然后是原生的冒泡,再是React的冒泡。無(wú)論是原生還是React,捕獲都是優(yōu)于冒泡;React的事件是委托到根節(jié)點(diǎn)的而不是節(jié)點(diǎn)本身。
六、請(qǐng)說(shuō)說(shuō)React Hooks解決了什么問(wèn)題? 函數(shù)組件與類組件的區(qū)別
1、復(fù)雜性 當(dāng)class類組件過(guò)于龐大難于理解,Hooks提供了更加簡(jiǎn)潔易于理解的代碼來(lái)管理狀態(tài)和生命周期;
2、邏輯復(fù)用,方便你在任何地方使用;
3、冗余代碼,可以減少大量冗余代碼;
函數(shù)組件和類組件的區(qū)別:
1、定義方式:使用js函數(shù)來(lái)定義;使用ES6的class類來(lái)定義
2、狀態(tài)管理:使用useState管理;使用this.state,setState管理
3、生命周期:使用useEffect管理;使用componeDidMount等;
4、性能優(yōu)化:使用useMemo等;使用React.memo,shouldComponentUpdate等
5、代碼復(fù)用:邏輯交互可以在任何地方復(fù)用;代碼復(fù)用性差;
七、為什么 useState 要使用數(shù)組而不是對(duì)象
主要原因是可以自定義命名;
八、說(shuō)一下常用的Hook
1、useState
2、useEffect
3、useMemo
4、useCallback
5、useRef
6、useReducer
7、useLayoutEffect
8、useContext
九、Hook 的使用限制有哪些?
只能在函數(shù)組件的頂部使用;不可在for循環(huán)、條件語(yǔ)句中使用;不可在JS文件、class組件中使用;
十、useEffect 與 useLayoutEffect 的區(qū)別
1、執(zhí)行時(shí)機(jī)不同:useEffect是在頁(yè)面加載和渲染完畢以后異步執(zhí)行;useLayoutEffect是在頁(yè)面更新和渲染之前執(zhí)行,是同步執(zhí)行
2、性能不同:useEffect因?yàn)槭钱惒降?,是不?huì)阻塞頁(yè)面加載;useLayoutEffect可能會(huì)導(dǎo)致頁(yè)面阻塞
3、使用場(chǎng)景:一般用于ajax請(qǐng)求、事件監(jiān)聽(tīng)等不影響頁(yè)面布局的操作;一般用于需要獲取DOM節(jié)點(diǎn)的大小位置等操作;
十一、如何自定義Hook
const useFetch = (url)=>{
const [loading,setLoading] = useState(false);
const [error,setError] = useState('');
const [result,setResult] = useState(null);
useEffect(()=>{
try{
setLoading(true)
axios.get(url).then((res)=>{
setResult(res);
setLoading(false)
}).catch((err)=>{
setError(err);
setLoading(false)
})
catch(error){
setError(error);
setLoading(false)
}
},[url])
return {loading,error,result}
}
const [loading,error,result] = useFetch('http:172.27.1.1/api/test/')
十二、React 高階組件是什么,和普通組件有什么區(qū)別,適用什么場(chǎng)景
是一種高級(jí)的組件設(shè)計(jì)模式,而不是ReactApi的一部分,接受一個(gè)組件然后返回一個(gè)新的組件,缺點(diǎn)是會(huì)污染傳入組件的props,難以訪問(wèn)原組件的狀態(tài)和生命周期
特點(diǎn):
1、高復(fù)用性:HOC使得組件之間的邏輯代碼可以復(fù)用
2、狀態(tài)抽象:HOC可以封裝和管理組件狀態(tài)
3、增強(qiáng)功能:HOC可以輕松的給組件增強(qiáng)功能;
適用于:
權(quán)限管理、數(shù)據(jù)處理、狀態(tài)管理
十三、React 高階組件、Render.props、Hooks 有什么區(qū)別,為什么要不斷迭代
Render.props是一種把React作為元素返回的組件,會(huì)導(dǎo)致嵌套地獄;
class MouseTracker extends React.Component {
constructor(props) {
super(props);
this.state = { x: 0, y: 0 };
}
handleMouseMove = (event) => {
this.setState({
x: event.clientX,
y: event.clientY
});
};
render() {
return (
<div style={{ height: '100vh' }} onMouseMove={this.handleMouseMove}>
{this.props.render(this.state)}
</div>
);
}
}
<MouseTracker render={({ x, y }) => (
<MouseTracker render={({ x, y }) => (
<h1 style={{ color: 'red' }}>鼠標(biāo)位置: ({x}, {y})</h1>
)}/>
)}/>
Hooks是在函數(shù)組件中使用,并且解決了Hoc和Render.props的缺點(diǎn)。
十四、React組件通信有哪些方式
1、父?jìng)髯?/strong>
props
2、子傳父
props傳函數(shù)進(jìn)來(lái),refs獲取子組件實(shí)例
// 父組件
function Parent() {
const handleChildEvent = (data) => {
console.log("Received data from child:", data);
};
return <Child onEvent={handleChildEvent} />;
}
// 子組件
function Child({ onEvent }) {
return <button onClick={() => onEvent("Child Data")}>Send Data</button>;
}
// 父組件
function Parent() {
const childRef = useRef(null);
return (
<div>
<Child ref={childRef} />
<button onClick={() => childRef.current.focusInput()}>
聚焦子組件輸入框
</button>
</div>
);
}
// 子組件(需使用 forwardRef 轉(zhuǎn)發(fā) ref)
const Child = forwardRef((props, ref) => {
const inputRef = useRef();
useImperativeHandle(ref, () => ({
focusInput: () => inputRef.current.focus(),
}));
return <input ref={inputRef} />;
});
3、跨層級(jí)
ContextApi
//context.js
import React, { createContext, useContext, useState } from 'react';
// 創(chuàng)建Context
export default const MessageContext = createContext();
// 父組件
import MessageContext from './context.js'
const ParentComponent = () => {
const [message, setMessage] = useState("Hello from Context!");
return (
<MessageContext.Provider value={{ message, setMessage }}>
<ChildOne />
<ChildTwo />
</MessageContext.Provider>
);
};
// 子組件1
import MessageContext from './context.js'
const ChildOne = () => {
const { setMessage } = useContext(MessageContext);
const updateMessage = () => {
setMessage("Updated message from Child One!");
}
return (
<div>
<button onClick={updateMessage}>Update Message</button>
</div>
);
};
// 子組件2
import MessageContext from './context.js'
const ChildTwo = () => {
const { message } = useContext(MessageContext);
return (
<div>
<h2>Message: {message}</h2>
</div>
);
};
export default ParentComponent;
4、兄弟層級(jí)
通過(guò)公用的父組件來(lái)傳遞參數(shù)
5、全局狀態(tài)
Redux、Zustand等
十五、React中props和state有什么區(qū)別?React中的props為什么是只讀的?
props是只讀的不可編輯,state可以編輯
props是父組件傳入的屬性數(shù)據(jù),state是組件內(nèi)部的屬性數(shù)據(jù),在constructor中初始化;
props在組件初始化后就不可變,state是可以在組件內(nèi)不同生命周期改變
保證React單向數(shù)據(jù)流的設(shè)計(jì)模式,父組件可以向不同的組件傳遞props。
十六、React 16.X 中 props 改變后在哪個(gè)生命周期中處理
1、componentReceviedProps()(16.3以后已經(jīng)廢棄);
2、componentDidUpdate()
3、getDerivedStateFromProps()
十七、React 性能優(yōu)化在哪個(gè)生命周期?它優(yōu)化的原理是什么
在shouldComponentUpdate ;此外在16.3以后還加入了React.PureComponent和React.memo
shouldComponentUpdate 可以對(duì)state、props的更新做處理,如果不需要?jiǎng)t返回false
React.PureComponent的功能是對(duì)state、props做淺比較,如果淺比較都不需要更新,那么就不更新
React.memo和React.PureComponent的功能類似,只是是用于函數(shù)組件
優(yōu)化的原理:
主要原理就是就是為了減少渲染以及減少diff算法,因?yàn)閐iff算法是非常寶貴的,減少diff算法和算法就可以優(yōu)化極大的性能。
十八、React 中 keys 的作用是什么?
幫助React高效的識(shí)別和精準(zhǔn)的跟蹤DOM中的變化,比如刪除、添加、修改等操作,特別是動(dòng)態(tài)列表和動(dòng)態(tài)排序的時(shí)候,幫助React判斷哪些節(jié)點(diǎn)需要移除或者重新排序,減少DOM操作。
十九、React 中 refs 的作用是什么?
用于直接訪問(wèn)DOM元素或者組件實(shí)例。
二十、說(shuō)說(shuō)React diff 算法
React的diff算法是基于索引的比較,是新舊虛擬DOM樹(shù)之間進(jìn)行比較,盡可能的減少真實(shí)的DOM操作,提高性能,React會(huì)先比較組件的類型和屬性,只有在必要的時(shí)候才更新其內(nèi)部元素;
Diff算法的關(guān)鍵策略:
1、分層比較:將整個(gè)UI拆分成樹(shù)狀結(jié)構(gòu),比較每一個(gè)層級(jí),只會(huì)比較不同類型的組件,如果是不同類型的組件會(huì)直接刪除替換;
2、統(tǒng)計(jì)比較:統(tǒng)計(jì)層級(jí)的節(jié)點(diǎn)會(huì)根據(jù)key值進(jìn)行比較,key幫助react精確識(shí)別每個(gè)元素的位置,優(yōu)化重排;
3、元素更新與刪除:如果兩個(gè)節(jié)點(diǎn)具有相同的key,react會(huì)更新他們,否則會(huì)刪除并創(chuàng)建新的節(jié)點(diǎn);
4、避免不必要的更新:如果state和props沒(méi)有變化,則不會(huì)重新渲染組件。
具體步驟:
1、先比較類型:如果類型不同則直接銷毀舊節(jié)點(diǎn),創(chuàng)建新節(jié)點(diǎn)。
2、同類型比較:如果類型是相同的,那么react會(huì)根據(jù)其屬性、子節(jié)點(diǎn),比較他們的差異并更新;
3、遞歸對(duì)比子節(jié)點(diǎn):React會(huì)遞歸的對(duì)比所有的子節(jié)點(diǎn),重復(fù)以上的步驟,并且盡量重用已有的key;
key的作用:
用于標(biāo)識(shí)哪個(gè)節(jié)點(diǎn)發(fā)生了變化,幫助React判斷哪些節(jié)點(diǎn)需要移除或者重新排序,減少DOM操作;
二十一、React 與 Vue 的 diff 算法有何不同?
React的diff算法是基于索引的比較,主要是對(duì)新舊虛擬DOM樹(shù)進(jìn)行比較,盡可能的減少DOM操作,提高性能,React會(huì)優(yōu)先比較組建的類型和屬性,只有在必要的時(shí)候才會(huì)更新其內(nèi)部元素;
Vue的diff算法是基于節(jié)點(diǎn)的比較,而且是采用雙端的算法,從前后兩端先進(jìn)行比較,然后再向中間進(jìn)行比較;Vue對(duì)于Key的使用較少,在沒(méi)有Key的時(shí)候,Vue會(huì)默認(rèn)按照位置進(jìn)行節(jié)點(diǎn)重排,所以會(huì)有些性能問(wèn)題。
二十二、React setState 調(diào)用的原理
1、觸發(fā)更新
當(dāng)調(diào)用setState時(shí)React會(huì)將這個(gè)狀態(tài)更新表記為待處理。
2、合并狀態(tài)
在ReactsetState是異步的,所以當(dāng)有多個(gè)setState操作時(shí),這些調(diào)用會(huì)被合并成為一個(gè)。
3、調(diào)度更新
React使用了調(diào)度來(lái)更新這些狀態(tài),在16和之后的版本中因?yàn)橐肓?code>Fiber架構(gòu)。當(dāng)調(diào)用setState的時(shí)候,React會(huì)將該更新加入到Fiber的更新隊(duì)列中。
4、執(zhí)行渲染
在瀏覽器的下一個(gè)事件循環(huán)中,React會(huì)從Fiber的更新隊(duì)列中取出需要更新的組件,并根據(jù)新的狀態(tài)和屬性進(jìn)行渲染邏輯。這一步會(huì)調(diào)用組件的render方法,同時(shí)會(huì)比較新舊的虛擬DOM的差異,最終只對(duì)差異的部分進(jìn)行實(shí)際的DOM更新。
二十三、setState 第一個(gè)參數(shù)有兩種傳遞方式 1.一個(gè)對(duì)象 2. 一個(gè)函數(shù) 這兩種寫(xiě)法有什么區(qū)別呢?useState呢
// 連續(xù)調(diào)用兩次,結(jié)果可能只加 1(而非預(yù)期的加 2)
handleClick = () => {
this.setState({ count: this.state.count + 1 });
this.setState({ count: this.state.count + 1 });
};
// 連續(xù)調(diào)用兩次,結(jié)果會(huì)正確加 2
handleClick = () => {
this.setState((state) => ({ count: state.count + 1 }));
this.setState((state) => ({ count: state.count + 1 }));
};
useState道理也是一樣的
二十四、React Hooks 工作機(jī)制深度解析
1、Hooks和Fiber架構(gòu)的綁定機(jī)制;
狀態(tài)存儲(chǔ)位置:
每個(gè)Hook創(chuàng)建時(shí),會(huì)在組件的Fiber節(jié)點(diǎn)的memoizedState屬性中以鏈表行書(shū)存儲(chǔ)。
每個(gè)Hook節(jié)點(diǎn)包含memoizedState(上一次渲染的狀態(tài)值)、quene(更新隊(duì)列)和next(指向下一個(gè)Hook)字段。
執(zhí)行順序的強(qiáng)制性:
Hooks必須保證每次渲染的調(diào)用順序一致,否則會(huì)因鏈表節(jié)點(diǎn)錯(cuò)位導(dǎo)致?tīng)顟B(tài)錯(cuò)亂給,所以不能再if、for循環(huán)中使用Hook
if (condition) {
const [count] = useState(0); // 破壞 Hook 鏈表順序
}
2、核心Hooks實(shí)現(xiàn)原理
useState狀態(tài)管理機(jī)制
首次渲染:初始化Hook節(jié)點(diǎn),將初始值存入memoizedState;
更新階段:通過(guò)dispatch函數(shù)將更新操作加入隊(duì)列,觸發(fā)重新渲染并計(jì)算新?tīng)顟B(tài)。
閉包陷阱:異步回調(diào)如定時(shí)器、事件監(jiān)聽(tīng),直接使用state可能引用舊值,可能導(dǎo)致更新丟失(需通過(guò)函數(shù)式更新解決或者用useRef同步最新的值)
setCount(prev => prev + 1); // ? 正確方式
setCount(count + 1); // ? 可能依賴過(guò)期閉包值
useEffect副作用調(diào)度
副作用函數(shù)在提交階段異步執(zhí)行,避免阻塞渲染流程
依賴數(shù)組對(duì)比,空數(shù)組只執(zhí)行一次
清理機(jī)制:返回的清理函數(shù)會(huì)在組件卸載或依賴變化前執(zhí)行。
3、Hooks渲染流程
掛載階段:
創(chuàng)建Fiber節(jié)點(diǎn)并創(chuàng)建Hook鏈表;
執(zhí)行組件函數(shù),觸發(fā)Hooks初始化邏輯;
更新階段:
遍歷Hook鏈表,依次應(yīng)用更新隊(duì)列中的操作;
生成新虛擬DOM,通過(guò)Diff算法更新視圖;
卸載階段:
執(zhí)行useEffect返回的清理函數(shù);
移除Fiber節(jié)點(diǎn)及關(guān)聯(lián)的Hook鏈表
二十五、React中setState的第二個(gè)參數(shù)作用是什么?
回調(diào)函數(shù),可以用來(lái)做執(zhí)行完setState后需要的操作,比如打印日志、獲取最新的狀態(tài)之類的。
二十六、React中的setState和replaceState的區(qū)別是什么?
setState只是做淺合并,不會(huì)影響到其他屬性,提高性能。
replaceState是完全覆蓋,可能會(huì)刪除某些屬性(不推薦使用)。
二十七、React中的setState批量更新的過(guò)程是什么?
調(diào)用setState,組件的state不會(huì)立即更新,setState只是把需要修改的state放到一個(gè)隊(duì)列里面,并且是只要還有同步任務(wù),就會(huì)一直把需要更新的state放到隊(duì)列里面去,同時(shí)會(huì)把多次setState的狀態(tài)最終合并到一起,這就是批量更新。
二十八、React 中 setState 什么時(shí)候是同步的,什么時(shí)候是異步的?
react18之前在setTimeOut、promise、setInterval、原生adeventListener中是同步的
合成事件中、生命周期中是異步的。
在React18之后都是異步的
二十九、React Hooks 設(shè)計(jì)動(dòng)機(jī)與設(shè)計(jì)模式解析
1、設(shè)計(jì)動(dòng)機(jī)
邏輯復(fù)用困境:
類組件通過(guò)HOC或者Render props實(shí)現(xiàn)邏輯復(fù)用,導(dǎo)致組件嵌套層級(jí)過(guò)深,代碼耦合度增加
共享狀態(tài)邏輯需要依賴復(fù)雜的設(shè)計(jì)模式,難以跨組件高效復(fù)用
生命周期分散性
關(guān)聯(lián)邏輯被拆分到不同的生命周期比如componentDidMount和componentDidUpdate,代碼冗余易遺漏清洗操作。
類組件中的this綁定問(wèn)題與實(shí)例方法管理復(fù)雜度高
函數(shù)組件的增強(qiáng)需求
函數(shù)組件無(wú)法管理狀態(tài)和副作用,導(dǎo)致其能力不足,所以用useState和useEffect來(lái)補(bǔ)齊短板
2、設(shè)計(jì)模式
狀態(tài)和UI分離模式,拆分組件樣式和組件邏輯代碼成為自定義的hooks,減少代碼耦合。
副作用統(tǒng)一管理
useEffect整合類組件的componentDidMount、componentDidUpdate和componentWillUnmount生命周期,通過(guò)依賴數(shù)組控制執(zhí)行時(shí)機(jī);
清理機(jī)制:返回清理函數(shù)避免內(nèi)存泄漏(如取消網(wǎng)絡(luò)請(qǐng)求、移除事件監(jiān)聽(tīng))
性能優(yōu)化模式
緩存策略:useMemo緩存計(jì)算結(jié)果,useCallback緩存函數(shù)引用,減少不必要的渲染
惰性初始化:傳遞函數(shù)給useState初始化復(fù)雜狀態(tài),避免重復(fù)計(jì)算
三十、React實(shí)現(xiàn)緩存的方式有哪些?他們有什么區(qū)別?
React Query、SWR
localStorage、sessionStorage
React.memo、React.PureComponent
useMemo、useCallback
持久化:只有localStorage、sessionStorage是長(zhǎng)期持久化的,sessionStorage在關(guān)閉窗口后也會(huì)消失,但是刷新不會(huì)消失,其他的都會(huì)消失。
使用場(chǎng)景:
localStorage、sessionStorage存放需要持久化存放的數(shù)據(jù)
React.memo、React.PureComponent主要用于class組件的緩存或者復(fù)雜計(jì)算結(jié)果的緩存。
React Query、SWR主要用于異步存儲(chǔ)數(shù)據(jù)和緩存。
useMemo、useCallback主要是hooks中使用對(duì)函數(shù)組件的緩存
三十一、對(duì)React中Fragment的理解,它的使用場(chǎng)景是什么?
Fragment組件實(shí)際上是因?yàn)镽eact不能直接返回多個(gè)節(jié)點(diǎn),如果需要返回多個(gè)節(jié)點(diǎn)又不想同時(shí)新增其他實(shí)際的節(jié)點(diǎn)去包裹他們,那么就可以用Fragment節(jié)點(diǎn)包裹需要返回的子節(jié)點(diǎn),它還可以寫(xiě)為<></>;
三十二、什么是受控組件和非控組件
使用React來(lái)管理表單狀態(tài)數(shù)據(jù)的組件稱為受控組件
不需要React來(lái)管理表單狀態(tài)數(shù)據(jù)的組件成為非受控組件,用ref來(lái)獲取表單的內(nèi)容
三十三、React.Component 和 React.PureComponent 的區(qū)別
PureComponent組件會(huì)對(duì)props和state進(jìn)行淺比較,避免不必要的更新,實(shí)際上是實(shí)現(xiàn)了shouldComponetUpdate的功能;
三十四、對(duì)有狀態(tài)組件和無(wú)狀態(tài)組件的理解及使用場(chǎng)景
有狀態(tài)組件:
有生命周期、class組件、有this、有繼承、內(nèi)部使用state管理狀態(tài)
無(wú)狀態(tài)組件:
可以是calss組件也可以是函數(shù)組件、無(wú)this、不使用生命周期、無(wú)state管理,完全依賴外部的props做展示作用
三十五、useffect 模擬生命周期
componentDidMount:
useEffect(()=>{
//加載
},[])
componentWillUnmount:
useEffect(()=>{
return ()=>{
//卸載
}
})
componentDidUpdate:
useEffect(()=>{
//沒(méi)有任何等于監(jiān)聽(tīng)所有的更新
})
三十六、哪些方法會(huì)觸發(fā) React 重新渲染?重新渲染 render 會(huì)做些什么?
1.setState()方法被調(diào)用
2.父組件重新渲染
3.props更新
4.ContextApi
5.全局狀態(tài)管理
重新渲染render首先會(huì)比較新舊DOM樹(shù),根據(jù)比較出來(lái)的結(jié)果計(jì)算出需要更新的差異,然后動(dòng)態(tài)更新需要更新的部分。
三十七、hooks父組件怎么調(diào)用子組件的方法?
首先需要forwardRef包裹子組件
const child = forwardRef((ref)=>{
//然后需要使用useImperativeHandle暴露對(duì)應(yīng)的方法給父組件
useImperativeHandle(ref,()=>({
someMethod:()=>{
}
})
})
const parent = ()=>{
const ref = useRef(null)
const childMethd = ()=>{
if(ref.current){
ref.current.someMethod();
}
}
return (
...
<Child ref={ref} />
)
}
三十八、React的嚴(yán)格模式如何使用,有什么用處?
通過(guò)React.strictMode啟用,幫助檢驗(yàn)不規(guī)范的代碼,提升代碼質(zhì)量,只在開(kāi)發(fā)環(huán)境中生效。
三十九、React的設(shè)計(jì)思想是什么
組件化:把用戶界面分解成可復(fù)用的組件
虛擬DOM:避免直接的DOM操作,虛擬DOM是真實(shí)DOM的映射關(guān)系,使用虛擬DOM優(yōu)化更新渲染的性能,避免大量且頻繁的DOM操作
數(shù)據(jù)驅(qū)動(dòng)視圖:數(shù)據(jù)更新就重新渲染頁(yè)面,不需要額外的DOM操作
四十、為什么React自定義組件首字母要大寫(xiě)、
因?yàn)槿绻切?xiě)的會(huì)被當(dāng)做HTML來(lái)處理,所以需要首字母大寫(xiě)。
四十一、React組件為什么不能返回多個(gè)元素
因?yàn)檫@是為了虛擬DOM,虛擬DOM只有一個(gè)根節(jié)點(diǎn)時(shí),才能夠更加高效容易的計(jì)算出差異并進(jìn)行最小化的DOM更新。
四十二、簡(jiǎn)述React的生命周期
分為三個(gè)階段:
組件裝載階段:掛載階段組件會(huì)被創(chuàng)建,然后被插入到DOM中,完成組件的渲染,該過(guò)程只會(huì)發(fā)生一次;
該過(guò)程中會(huì)執(zhí)行constructor、getDerivedStateFormProps、render、componentDidMount;
組件更新階段:當(dāng)組件的props、state等觸發(fā)更新時(shí),會(huì)重新渲染,這個(gè)過(guò)程可能會(huì)執(zhí)行很多次
該過(guò)程中會(huì)執(zhí)行:
getDerivedStateFromProps、shouldComponentUpdate、render、getSnapShotBeforeUpdate、componentDidUpdate
getSnapShotBeforeUpdate:這個(gè)是在render之后執(zhí)行,在componentDidUpdate之前調(diào)用必須和componentDidUpdate一起使用,有兩個(gè)參數(shù)一個(gè)是prePorps和propsState表示更新之前的state、props;典型場(chǎng)景包括:
記錄滾動(dòng)位置,以便更新后恢復(fù)
獲取元素尺寸或位置等瞬時(shí)狀態(tài)
與 componentDidUpdate 配合使用
該方法返回的值會(huì)作為第三個(gè)參數(shù)傳遞給 componentDidUpdate,形成數(shù)據(jù)傳遞鏈路:
getSnapshotBeforeUpdate(prevProps, prevState) {
return { scrollTop: document.getElementById('list').scrollTop };
}
componentDidUpdate(prevProps, prevState, snapshot) {
if (snapshot) {
// 根據(jù)快照恢復(fù)滾動(dòng)位置
}
}
組件卸載階段:
只有一個(gè)componentWillUnmount會(huì)卸載和銷毀組件的所有內(nèi)容,比如定時(shí)器和網(wǎng)絡(luò)請(qǐng)求等。
四十三、對(duì)React的插槽(Portals)的理解,如何使用,有哪些使用場(chǎng)景
插槽指的是可以將子組件渲染到除父組件之外的DOM節(jié)點(diǎn)中,主要用于模態(tài)框、提示框等。
const Modal = ()=>{
return ReactDOM.createPortal(
<div>提示框</div>,
document.getElementById('root')
)
}
四十四、父組件和子組件的生命周期執(zhí)行順序
父conctructor
子conctructor
父render
子render
父componentDidMount
子componentDidMount
四十五、React聲明組件有哪幾種方法,有什么不同?
1.React.createClass、React.Component、函數(shù)組件
React.createClass、React.Component
React.createClass:是ES5的寫(xiě)法現(xiàn)在已經(jīng)不推薦了
React.Component:是ES6的寫(xiě)法他和函數(shù)組件的區(qū)別主要是,擁有生命周期,擁有this,擁有構(gòu)造函數(shù),可以繼承,不支持hooks,擁有狀態(tài)管理。
四十六、React組件的構(gòu)造函數(shù)有什么作用?它是必須的嗎?
是用來(lái)初始化組件和綁定事件處理的方法,不是必須的。
四十七、React性能優(yōu)化手段
緩存比如React.memo、useMemo、useCallback等來(lái)做緩存
避免不必要的重新渲染,可以使用React.fragment、shouldComponentUpdate、React.PureComponent
列表加key:渲染列表提供一個(gè)唯一的key,幫助React在Diff算法的時(shí)候更加精準(zhǔn)高效的找到對(duì)應(yīng)的差異,并精確更新。
懶加載和代碼分割:使用懶加載來(lái)加載頁(yè)面,可以減少加載的時(shí)間,提高性能。
計(jì)算密集型任務(wù):可以使用Web Workers,可以避免阻塞UI頁(yè)面。
服務(wù)端渲染SSR
四十八、在React中如何避免不必要的render?
緩存比如React.memo、useMemo、useCallback等來(lái)做緩存
避免不必要的重新渲染,可以使用React.fragment、shouldComponentUpdate、React.PureComponent
四十九、React-Router工作原理
原理:監(jiān)聽(tīng)URL變化,動(dòng)態(tài)渲染組件,從而實(shí)現(xiàn)頁(yè)面的跳轉(zhuǎn)和切換。
兩種路由模式:
hash:使用window.location.hash 監(jiān)聽(tīng)hash的變化 路徑中帶有#;通過(guò)window.onhashchange監(jiān)聽(tīng)
history:使用window.history.pushState和window.history.replaceState修改URl的變化,路徑中沒(méi)有#;通過(guò)window.onpopstate監(jiān)聽(tīng);
使用Link和useNavigate更改路由地址;
五十、對(duì) Redux 的理解,主要解決什么問(wèn)題
是一個(gè)用于javascript的應(yīng)用的狀態(tài)管理庫(kù),主要是解決了復(fù)雜單頁(yè)面應(yīng)用開(kāi)發(fā)中狀態(tài)管理非常困難的問(wèn)題,Redux通過(guò)提供一個(gè)集中式的狀態(tài)管理,將整個(gè)應(yīng)用的狀態(tài)保存在一個(gè)地方,便于管理和維護(hù)。
Redux的核心概念和設(shè)計(jì)理念
1、單一數(shù)據(jù)源:所有數(shù)據(jù)都存儲(chǔ)在單一的store中
2、狀態(tài)是只讀的:唯一可以改變狀態(tài)的方法是通過(guò)發(fā)送一個(gè)action,意味著每次操作都是可記錄和追蹤的
3、使用純函數(shù)來(lái)執(zhí)行狀態(tài)的變化:通過(guò)reducers純函數(shù)來(lái)描述如何根據(jù)action更新?tīng)顟B(tài),確保狀態(tài)更新的可預(yù)測(cè)性。
主要解決的問(wèn)題:
集中式管理狀態(tài):集中的管理所有狀態(tài),便于管理與維護(hù)
狀態(tài)可預(yù)測(cè)性:狀態(tài)更改需要action操作,就可以追溯所有的操作
方便的狀態(tài)共享:所有組件都可以訪問(wèn)Redux里面的狀態(tài)
時(shí)間旅行調(diào)試:Redux結(jié)合了不可變的數(shù)據(jù)和純函數(shù)的概念,使得所有的操作都是可記錄。
中間件的支持: 可以方便的擴(kuò)展功能
五十一、Redux 原理及工作流程
Redux的工作流程:
1、派發(fā)Action:用戶操作會(huì)派發(fā)一個(gè)action,這個(gè)action是一個(gè)描述事件的對(duì)象
2、處理Action: Reducers是一個(gè)純函數(shù)沒(méi)接收當(dāng)前的state和action,返回新的state
3、更新Store:store接受到新的state,更新其內(nèi)部狀態(tài),所有組件都可以從store獲取到最新的狀態(tài)。
一、三大核心要素
Store(單一數(shù)據(jù)源)
全局唯一的狀態(tài)容器,存儲(chǔ)整個(gè)應(yīng)用的所有共享狀態(tài)
通過(guò) store.getState() 獲取當(dāng)前狀態(tài),通過(guò) store.dispatch(action) 觸發(fā)狀態(tài)更新
示例結(jié)構(gòu):
{
user: { name: 'Alice' },
todos: ['Task 1', 'Task 2']
}
Action(狀態(tài)變化的描述)
一個(gè)普通 JavaScript 對(duì)象,必須包含 type 字段描述操作類型
可選 payload 字段攜帶數(shù)據(jù),例如:
{
type: 'ADD_TODO',
payload: 'Learn Redux'
}
Reducer(狀態(tài)變化的純函數(shù))
接收當(dāng)前 state 和 action,返回新?tīng)顟B(tài)(不可直接修改原狀態(tài))
示例:
const todoReducer = (state = [], action) => {
switch (action.type) {
case 'ADD_TODO':
return [...state, action.payload];
default:
return state;
}
};
二、核心原則
單一數(shù)據(jù)源
整個(gè)應(yīng)用狀態(tài)僅存儲(chǔ)在一個(gè) Store 中,便于調(diào)試和管理
狀態(tài)只讀
唯一修改狀態(tài)的方式是派發(fā) Action,禁止直接修改 Store 數(shù)據(jù)
純函數(shù)修改
Reducer 必須是純函數(shù)(相同輸入必定得到相同輸出,無(wú)副作用)
五十二、Redux 中異步的請(qǐng)求怎么處理
可以使用中間件,比如redux-thunk、redux-saga
五十三、Fiber架構(gòu)
Fiber是一種基于鏈表的數(shù)據(jù)結(jié)構(gòu),他將渲染過(guò)程拆分成多個(gè)小的任務(wù)單元。這種機(jī)制允許React在執(zhí)行這些任務(wù)單元時(shí),根據(jù)瀏覽器空閑的時(shí)間暫停、恢復(fù)和重新安排任務(wù)的執(zhí)行順序。這樣當(dāng)瀏覽器有需要緊急處理的交互或者更新時(shí),React可以暫停當(dāng)前的渲染任務(wù),將控制權(quán)交給瀏覽器,等瀏覽器空閑的時(shí)候再繼續(xù)執(zhí)行渲染任務(wù),從而避免長(zhǎng)時(shí)間占用主線程,提高頁(yè)面的響應(yīng)性。
Fiber解決的問(wèn)題:
1、可中斷和恢復(fù)的渲染:Fiber允許渲染過(guò)程在需要時(shí)暫停和恢復(fù),避免長(zhǎng)時(shí)間占用主線程,提高頁(yè)面的響應(yīng)性。
2、優(yōu)先級(jí)調(diào)度:Fiber引入了優(yōu)先級(jí)調(diào)度的概念,不同類型的更新可以被賦予不同的優(yōu)先級(jí)。高優(yōu)先級(jí)的更新如用戶的交互,會(huì)被優(yōu)先處理,確保用戶交互能夠及時(shí)響應(yīng),同時(shí)合理安排低優(yōu)先級(jí)的任務(wù)的執(zhí)行。
3、更細(xì)粒度的渲染控制:通過(guò)Fiber,react可以對(duì)渲染過(guò)程進(jìn)行更細(xì)粒度的控制,開(kāi)發(fā)者可以根據(jù)具體需求設(shè)置不同的有優(yōu)先級(jí)和調(diào)度策略。
Fiber的優(yōu)勢(shì):
可中斷和恢復(fù)的機(jī)制:根據(jù)瀏覽器的空閑情況,可以暫停和恢復(fù)任務(wù)。
增量渲染:拆分任務(wù)為多個(gè)任務(wù),根據(jù)任務(wù)的優(yōu)先級(jí)等進(jìn)行調(diào)度。
更好的用戶體驗(yàn):優(yōu)先處理用戶的交互事件
五十四、React錯(cuò)誤處理?
try-catch
ErrorBoundary
class ErrorBoundary extends React.Component {
state = { hasError: false };
componentDidCatch(error, info) {
this.setState({ hasError: true });
logErrorToService(error, info.componentStack);
}
render() {
return this.state.hasError
? <FallbackUI />
: this.props.children;
}
}
<ErrorBoundary>
<UnstableComponent />
</ErrorBoundary>
Susponse
五十五、數(shù)據(jù)雙向綁定的原理
1、React是通過(guò)props和state來(lái)實(shí)現(xiàn)狀態(tài)和單項(xiàng)數(shù)據(jù)流的,當(dāng)props和state改變都會(huì)觸發(fā)頁(yè)面的重新渲染,
2、受控的組件是根據(jù)state和表單中的value和onChange事件來(lái)實(shí)現(xiàn)數(shù)據(jù)雙向綁定的效果。
五十六、怎么動(dòng)態(tài)導(dǎo)入組件,按需加載,代碼分割?
React.lazy動(dòng)態(tài)加載組件,并且配合Suspense來(lái)動(dòng)態(tài)展示組件的內(nèi)容。
也可以在React router中使用React.lazy和Suspense來(lái)處理。
代碼分割則可以使用Webpack來(lái)把應(yīng)用分解為更小、更易管理的塊。
一、動(dòng)態(tài)導(dǎo)入基礎(chǔ)方案
使用 React.lazy + Suspense
適用于組件級(jí)懶加載,需配合 fallback 占位
const LazyComponent = React.lazy(() => import('./LazyComponent'));
function App() {
return (
<Suspense fallback={<div>Loading...</div>}>
<LazyComponent />
</Suspense>
);
}
動(dòng)態(tài)導(dǎo)入函數(shù)模塊
通過(guò) import() 語(yǔ)法實(shí)現(xiàn)非組件代碼分割
import('./math').then(math => {
console.log(math.add(2, 3));
});
二、路由級(jí)按需加載
React Router 集成
結(jié)合路由配置實(shí)現(xiàn)頁(yè)面級(jí)懶加載
const Home = React.lazy(() => import('./routes/Home'));
const About = React.lazy(() => import('./routes/About'));
function RouterConfig() {
return (
<Routes>
<Route path="/" element={<Suspense fallback={...}><Home /></Suspense>} />
<Route path="/about" element={<Suspense fallback={...}><About /></Suspense>} />
</Routes>
);
}
代碼分割:
SplitChunks 配置
分離第三方庫(kù)和公共代碼
// webpack.config.js
optimization: {
splitChunks: {
chunks: 'all',
cacheGroups: {
vendors: {
test: /[\\/]node_modules[\\/]/,
name: 'vendors',
priority: 10
}
}
}
}
五十七、React的狀態(tài)提升是什么?使用場(chǎng)景有哪些?
就是通過(guò)在父組件中保存狀態(tài)和修改狀態(tài)的方法,通過(guò)props把狀態(tài)值和更改狀態(tài)的方法一起傳給子組件,子組件然后再調(diào)用父組件的的方法,從而修改負(fù)組件中狀態(tài),并共享給其他組件,這個(gè)就是狀態(tài)提升。
場(chǎng)景:
表單聯(lián)動(dòng)
全局狀態(tài)管理
五十八、對(duì) React 和 Vue 的理解,它們的異同
相同點(diǎn):
組件化開(kāi)發(fā)
響應(yīng)式數(shù)據(jù)綁定
虛擬DOM
不同點(diǎn):
創(chuàng)建方式:JSX HTML
數(shù)據(jù)流 單向數(shù)據(jù)流 數(shù)據(jù)雙向綁定
五十九、對(duì)React SSR的理解
服務(wù)端渲染時(shí)把數(shù)據(jù)和模板組成的HTML一起返回。
優(yōu)點(diǎn):
對(duì)SEO友好
所有模板圖片資源都存儲(chǔ)到服務(wù)器
一個(gè)html返回所有的數(shù)據(jù)
減少了http請(qǐng)求
響應(yīng)快、用戶體驗(yàn)好
缺點(diǎn):
服務(wù)器壓力大
生命周期只會(huì)執(zhí)行到componentDidMount之前
六十、react-redux中的connect怎么實(shí)現(xiàn)?
connect 是 react-redux 的核心 API,用于將 React 組件與 Redux store 連接起來(lái)。它的實(shí)現(xiàn)主要基于高階組件(HOC)模式,通過(guò)訂閱 store 的狀態(tài)變化并映射到組件的 props
從 Context 獲取 Store
connect 通過(guò) React 的 context 獲取由 Provider 注入的 Redux store。
映射 State 和 Dispatch
mapStateToProps(state, ownProps?): 將 store 的 state 映射到組件的 props。
mapDispatchToProps(dispatch, ownProps?): 將 action creators 綁定到 dispatch,并作為 props 傳遞給組件。
合并 Props
將 mapStateToProps、mapDispatchToProps 和組件自身的 ownProps 合并為最終傳遞給子組件的 props。
訂閱 Store 變化
在組件的生命周期(如 componentDidMount)中訂閱 store 的變化,并在狀態(tài)更新時(shí)觸發(fā)重新渲染。
返回高階組件
connect 返回一個(gè)函數(shù),該函數(shù)接收原始組件并返回一個(gè)包裝后的新組件
function connect(mapStateToProps, mapDispatchToProps) {
return function(WrappedComponent) {
return class ConnectedComponent extends React.Component {
static contextTypes = {
store: PropTypes.object
};
componentDidMount() {
this.unsubscribe = this.context.store.subscribe(() => {
this.forceUpdate();
});
}
componentWillUnmount() {
this.unsubscribe();
}
render() {
const { store } = this.context;
const stateProps = mapStateToProps(store.getState(), this.props);
const dispatchProps = mapDispatchToProps(store.dispatch, this.props);
return (
<WrappedComponent
{...this.props}
{...stateProps}
{...dispatchProps}
/>
);
}
};
};
}
六十一、diff的時(shí)間復(fù)雜度是多少,為什么?
傳統(tǒng)Diff算法因全局對(duì)比和復(fù)雜編輯操作導(dǎo)致O(n3)復(fù)雜度,而React、Vue等框架通過(guò)層級(jí)限制與節(jié)點(diǎn)復(fù)用策略將復(fù)雜度降至O(n),同時(shí)兼顧性能與開(kāi)發(fā)效率