面試官:說(shuō)說(shuō)你在使用React 過(guò)程中遇到的常見(jiàn)問(wèn)題?解決方案?
本文轉(zhuǎn)載自微信公眾號(hào)「JS每日一題」,作者灰灰。轉(zhuǎn)載本文請(qǐng)聯(lián)系JS每日一題公眾號(hào)。
一、前言
在使用react開(kāi)發(fā)項(xiàng)目過(guò)程中,每個(gè)人或多或少都會(huì)遇到一些"奇怪"的問(wèn)題,本質(zhì)上都是我們對(duì)其理解的不夠透徹
react 系列,33個(gè)工作日,33次凌晨還在亮起的臺(tái)燈,到今天就圓滿畫上句號(hào)了,比心
在系列中我們列出了很多比較經(jīng)典的考題,工作中遇到的問(wèn)題也往往就藏中其中,只是以不同的表現(xiàn)形式存在罷了
今天的題解不算題解,準(zhǔn)確來(lái)說(shuō)是對(duì)整個(gè)系列的一次貫穿,總結(jié)
目錄:
- react 有什么特性
- 生命周期有哪些不同階段?每個(gè)階段對(duì)應(yīng)的方法是?
- state 和 props有什么區(qū)別?
- super()和super(props)有什么區(qū)別?
- setState執(zhí)行機(jī)制?
- React的事件機(jī)制?
- 事件綁定的方式有哪些?
- 構(gòu)建組件的方式有哪些?區(qū)別?
- 組件之間如何通信?
- key有什么作用?
- refs 的理解?應(yīng)用場(chǎng)景?
- Hooks的理解?解決了什么問(wèn)題?
- 如何引入css?
- redux工作原理?
- redux中間件有哪些?
- react-router組件有哪些?
- render觸發(fā)時(shí)機(jī)?
- 如何減少render?
- JSX轉(zhuǎn)化DOM過(guò)程?
- 性能優(yōu)化手段有哪些
- 如何做服務(wù)端渲染?
react 有什么特性
主要的特性分為:
- JSX語(yǔ)法
- 單向數(shù)據(jù)綁定
- 虛擬DOM
- 聲明式編程
- Component
借助這些特性,react整體使用起來(lái)更加簡(jiǎn)單高效,組件式開(kāi)發(fā)提高了代碼的復(fù)用率
- 生命周期有哪些不同階段?每個(gè)階段對(duì)應(yīng)的方法是?
主要分成了新的生命周期和舊的生命周期:
新版生命周期整體流程如下圖所示:
舊的生命周期流程圖如下:
state 和 props有什么區(qū)別?
兩者相同點(diǎn):
- 兩者都是 JavaScript 對(duì)象
- 兩者都是用于保存信息
- props 和 state 都能觸發(fā)渲染更新
區(qū)別:
- props 是外部傳遞給組件的,而 state 是在組件內(nèi)被組件自己管理的,一般在 constructor 中初始化
- props 在組件內(nèi)部是不可修改的,但 state 在組件內(nèi)部可以進(jìn)行修改
- state 是多變的、可以修改
super()和super(props)有什么區(qū)別?
在React中,類組件基于ES6,所以在constructor中必須使用super
在調(diào)用super過(guò)程,無(wú)論是否傳入props,React內(nèi)部都會(huì)將porps賦值給組件實(shí)例porps屬性中
如果只調(diào)用了super(),那么this.props在super()和構(gòu)造函數(shù)結(jié)束之間仍是undefined
setState執(zhí)行機(jī)制?
在react類組件的狀態(tài)需要通過(guò)setState進(jìn)行更改,在不同場(chǎng)景下對(duì)應(yīng)不同的執(zhí)行順序:
在組件生命周期或React合成事件中,setState是異步
在setTimeout或者原生dom事件中,setState是同步
當(dāng)我們批量更改state的值的時(shí)候,react內(nèi)部會(huì)將其進(jìn)行覆蓋,只取最后一次的執(zhí)行結(jié)果
當(dāng)需要下一個(gè)state依賴當(dāng)前state的時(shí)候,則可以在setState中傳遞一個(gè)回調(diào)函數(shù)進(jìn)行下次更新
React的事件機(jī)制?
React基于瀏覽器的事件機(jī)制自身實(shí)現(xiàn)了一套事件機(jī)制,包括事件注冊(cè)、事件的合成、事件冒泡、事件派發(fā)等
組件注冊(cè)的事件最終會(huì)綁定在document這個(gè) DOM上,而不是 React組件對(duì)應(yīng)的 DOM,從而節(jié)省內(nèi)存開(kāi)銷
自身實(shí)現(xiàn)了一套事件冒泡機(jī)制,阻止不同時(shí)間段的冒泡行為,需要對(duì)應(yīng)使用不同的方法
事件綁定的方式有哪些?
react常見(jiàn)的綁定方式有如下:
- render方法中使用bind
- render方法中使用箭頭函數(shù)
- constructor中bind
- 定義階段使用箭頭函數(shù)綁定
前兩種方式在每次組件render的時(shí)候都會(huì)生成新的方法實(shí)例,性能問(wèn)題欠缺
構(gòu)建組件的方式有哪些?區(qū)別?
組件的創(chuàng)建主要分成了三種方式:
- 函數(shù)式創(chuàng)建
- 繼承 React.Component 創(chuàng)建
- 通過(guò) React.createClass 方法創(chuàng)建
如今一般都是前兩種方式,對(duì)于一些無(wú)狀態(tài)的組件創(chuàng)建,建議使用函數(shù)式創(chuàng)建的方式,再比如hooks的機(jī)制下,函數(shù)式組件能做類組件對(duì)應(yīng)的事情,所以建議都使用函數(shù)式的方式來(lái)創(chuàng)建組件
組件之間如何通信?
組件間通信可以通過(guò)props、傳遞回調(diào)函數(shù)、context、redux等形式進(jìn)行組件之間通訊
key有什么作用?
使用key是react性能優(yōu)化的手段,在一系列數(shù)據(jù)最前面插入元素,如果沒(méi)有key的值,則所有的元素都需要進(jìn)行更換,而有key的情況只需要將最新元素插入到前面,不涉及刪除操作
在使用key的時(shí)候應(yīng)保證:
- key 應(yīng)該是唯一的
- key不要使用隨機(jī)值(隨機(jī)數(shù)在下一次 render 時(shí),會(huì)重新生成一個(gè)數(shù)字)
- 避免使用 index 作為 key
refs 的理解?應(yīng)用場(chǎng)景?
Refs允許我們?cè)L問(wèn) DOM節(jié)點(diǎn)或在 render方法中創(chuàng)建的 React元素
下面的場(chǎng)景使用refs非常有用:
- 對(duì)Dom元素的焦點(diǎn)控制、內(nèi)容選擇、控制
- 對(duì)Dom元素的內(nèi)容設(shè)置及媒體播放
- 對(duì)Dom元素的操作和對(duì)組件實(shí)例的操作
- 集成第三方 DOM 庫(kù)
Hooks的理解?解決了什么問(wèn)題?
Hook 是 React 16.8 的新增特性。它可以讓你在不編寫 class 的情況下使用 state 以及其他的 React 特性
解決問(wèn)題如下:
- 難以重用和共享組件中的與狀態(tài)相關(guān)的邏輯
- 邏輯復(fù)雜的組件難以開(kāi)發(fā)與維護(hù),當(dāng)我們的組件需要處理多個(gè)互不相關(guān)的 local state 時(shí),每個(gè)生命周期函數(shù)中可能會(huì)包含著各種互不相關(guān)的邏輯在里面
- 類組件中的this增加學(xué)習(xí)成本,類組件在基于現(xiàn)有工具的優(yōu)化上存在些許問(wèn)題
- 由于業(yè)務(wù)變動(dòng),函數(shù)組件不得不改為類組件等等
如何引入css?
常見(jiàn)的CSS引入方式有以下:
- 在組件內(nèi)直接使用
- 組件中引入 .css 文件
- 組件中引入 .module.css 文件
- CSS in JS
組件內(nèi)直接使用css會(huì)導(dǎo)致大量的代碼,而文件中直接引入css文件是全局作用域,發(fā)生層疊
引入.module.css文件能夠解決局部作用域問(wèn)題,但是不方便動(dòng)態(tài)修改樣式,需要使用內(nèi)聯(lián)的方式進(jìn)行樣式的編寫
css in js這種方法,可以滿足大部分場(chǎng)景的應(yīng)用,可以類似于預(yù)處理器一樣樣式嵌套、定義、修改狀態(tài)等
redux工作原理?
redux要求我們把數(shù)據(jù)都放在 store公共存儲(chǔ)空間
一個(gè)組件改變了 store 里的數(shù)據(jù)內(nèi)容,其他組件就能感知到 store的變化,再來(lái)取數(shù)據(jù),從而間接的實(shí)現(xiàn)了這些數(shù)據(jù)傳遞的功能
工作流程圖如下所示:
redux中間件有哪些?
市面上有很多優(yōu)秀的redux中間件,如:
- redux-thunk:用于異步操作
- redux-logger:用于日志記錄
react-router組件有哪些?
常見(jiàn)的組件有:
- BrowserRouter、HashRouter
- Route
- Link、NavLink
- switch
- redirect
render觸發(fā)時(shí)機(jī)?
在React 中,類組件只要執(zhí)行了 setState 方法,就一定會(huì)觸發(fā) render 函數(shù)執(zhí)行
函數(shù)組件useState 會(huì)判斷當(dāng)前值有無(wú)發(fā)生改變確定是否執(zhí)行render方法,一旦父組件發(fā)生渲染,子組件也會(huì)渲染
如何減少render?
父組件渲染導(dǎo)致子組件渲染,子組件并沒(méi)有發(fā)生任何改變,這時(shí)候就可以從避免無(wú)謂的渲染,具體實(shí)現(xiàn)的方式有如下:
- shouldComponentUpdate
- PureComponent
- React.memo
JSX轉(zhuǎn)化DOM過(guò)程?
jsx首先會(huì)轉(zhuǎn)化成React.createElement這種形式,React.createElement作用是生成一個(gè)虛擬Dom對(duì)象,然后會(huì)通過(guò)ReactDOM.render進(jìn)行渲染成真實(shí)DOM
性能優(yōu)化手段有哪些
除了減少render的渲染之外,還可以通過(guò)以下手段進(jìn)行優(yōu)化:
除此之外, 常見(jiàn)性能優(yōu)化常見(jiàn)的手段有如下:
- 避免使用內(nèi)聯(lián)函數(shù)
- 使用 React Fragments 避免額外標(biāo)記
- 使用 Immutable
- 懶加載組件
- 事件綁定方式
- 服務(wù)端渲染
如何做服務(wù)端渲染?
node server 接收客戶端請(qǐng)求,得到當(dāng)前的請(qǐng)求url 路徑,然后在已有的路由表內(nèi)查找到對(duì)應(yīng)的組件,拿到需要請(qǐng)求的數(shù)據(jù),將數(shù)據(jù)作為 props、context或者store 形式傳入組件
然后基于 react 內(nèi)置的服務(wù)端渲染方法 renderToString()把組件渲染為 html字符串在把最終的 html進(jìn)行輸出前需要將數(shù)據(jù)注入到瀏覽器端
瀏覽器開(kāi)始進(jìn)行渲染和節(jié)點(diǎn)對(duì)比,然后執(zhí)行完成組件內(nèi)事件綁定和一些交互,瀏覽器重用了服務(wù)端輸出的 html 節(jié)點(diǎn),整個(gè)流程結(jié)束
到這里,整個(gè)React系列也就結(jié)束了,如果這個(gè)系列有給你帶來(lái)一些啟發(fā)或者幫助,可以點(diǎn)點(diǎn)下方的贊告訴我們,我們下個(gè)系列再見(jiàn)