1、redux中間件
中間件提供第三方插件的模式惹挟,自定義攔截 action -> reducer 的過(guò)程。變?yōu)?action -> middlewares -> reducer 。這種機(jī)制可以讓我們改變數(shù)據(jù)流廷区,實(shí)現(xiàn)如異步 action 买窟,action 過(guò)濾丰泊,日志輸出,異常報(bào)告等功能始绍。
常見(jiàn)的中間件:
redux-logger:提供日志輸出
redux-thunk:處理異步操作
redux-promise:處理異步操作瞳购,actionCreator的返回值是promise
2、redux有什么缺點(diǎn)
1.一個(gè)組件所需要的數(shù)據(jù)亏推,必須由父組件傳過(guò)來(lái)学赛,而不能像flux中直接從store取。
2.當(dāng)一個(gè)組件相關(guān)數(shù)據(jù)更新時(shí)吞杭,即使父組件不需要用到這個(gè)組件盏浇,父組件還是會(huì)重新render,可能會(huì)有效率影響芽狗,或者需要寫(xiě)復(fù)雜的shouldComponentUpdate進(jìn)行判斷绢掰。
3、react組件的劃分業(yè)務(wù)組件技術(shù)組件童擎?
根據(jù)組件的職責(zé)通常把組件分為UI組件和容器組件滴劲。
UI 組件負(fù)責(zé) UI 的呈現(xiàn),容器組件負(fù)責(zé)管理數(shù)據(jù)和邏輯顾复。
兩者通過(guò)React-Redux 提供connect方法聯(lián)系起來(lái)哑芹。
具體使用可以參照如下鏈接:http://www.ruanyifeng.com/blog/2016/09/redux_tutorial_part_three_react-redux.html
4、react生命周期函數(shù)
這個(gè)問(wèn)題要考察的是組件的生命周期
一捕透、初始化階段:
getDefaultProps:獲取實(shí)例的默認(rèn)屬性
getInitialState:獲取每個(gè)實(shí)例的初始化狀態(tài)
componentWillMount:組件即將被裝載聪姿、渲染到頁(yè)面上
render:組件在這里生成虛擬的DOM節(jié)點(diǎn)
componentDidMount:組件真正在被裝載之后
二、運(yùn)行中狀態(tài):
componentWillReceiveProps:組件將要接收到屬性的時(shí)候調(diào)用
shouldComponentUpdate:組件接受到新屬性或者新?tīng)顟B(tài)的時(shí)候(可以返回false乙嘀,接收數(shù)據(jù)后不更新末购,阻止render調(diào)用,后面的函數(shù)不會(huì)被繼續(xù)執(zhí)行了)
componentWillUpdate:組件即將更新不能修改屬性和狀態(tài)
render:組件重新描繪
componentDidUpdate:組件已經(jīng)更新
三虎谢、銷(xiāo)毀階段:
componentWillUnmount:組件即將銷(xiāo)毀
5盟榴、react性能優(yōu)化是哪個(gè)周期函數(shù)?
shouldComponentUpdate 這個(gè)方法用來(lái)判斷是否需要調(diào)用render方法重新描繪dom婴噩。因?yàn)閐om的描繪非常消耗性能擎场,如果我們能在shouldComponentUpdate方法中能夠?qū)懗龈鼉?yōu)化的dom diff算法,可以極大的提高性能几莽。
詳細(xì)參考:
https//segmentfault.com/a/1190000006254212
6迅办、為什么虛擬dom會(huì)提高性能?
虛擬dom相當(dāng)于在js和真實(shí)dom中間加了一個(gè)緩存,利用dom diff算法避免了沒(méi)有必要的dom操作章蚣,從而提高性能站欺。
具體實(shí)現(xiàn)步驟如下:
用 JavaScript 對(duì)象結(jié)構(gòu)表示 DOM 樹(shù)的結(jié)構(gòu);然后用這個(gè)樹(shù)構(gòu)建一個(gè)真正的 DOM 樹(shù),插到文檔當(dāng)中
當(dāng)狀態(tài)變更的時(shí)候矾策,重新構(gòu)造一棵新的對(duì)象樹(shù)磷账。然后用新的樹(shù)和舊的樹(shù)進(jìn)行比較,記錄兩棵樹(shù)差異
把2所記錄的差異應(yīng)用到步驟1所構(gòu)建的真正的DOM樹(shù)上贾虽,視圖就更新了逃糟。
參考鏈接:
https://www.zhihu.com/question/29504639?sort=created
7、diff算法?
把樹(shù)形結(jié)構(gòu)按照層級(jí)分解蓬豁,只比較同級(jí)元素履磨。
給列表結(jié)構(gòu)的每個(gè)單元添加唯一的key屬性,方便比較庆尘。
React 只會(huì)匹配相同 class 的 component(這里面的class指的是組件的名字)
合并操作,調(diào)用 component 的 setState 方法的時(shí)候, React 將其標(biāo)記為 dirty.到每一個(gè)事件循環(huán)結(jié)束, React 檢查所有標(biāo)記 dirty 的 component 重新繪制.
選擇性子樹(shù)渲染巷送。開(kāi)發(fā)人員可以重寫(xiě)shouldComponentUpdate提高diff的性能驶忌。
參考鏈接:
https//segmentfault.com/a/1190000000606216
8、react性能優(yōu)化方案
(1)重寫(xiě)shouldComponentUpdate來(lái)避免不必要的dom操作笑跛。
(2)使用 production 版本的react.js付魔。
(3)使用key來(lái)幫助React識(shí)別列表中所有子組件的最小變化。
參考鏈接:
https://segmentfault.com/a/1190000006254212
9飞蹂、簡(jiǎn)述flux 思想
Flux 的最大特點(diǎn)几苍,就是數(shù)據(jù)的"單向流動(dòng)"。
1.用戶訪問(wèn) View
2.View 發(fā)出用戶的 Action
3.Dispatcher 收到 Action陈哑,要求 Store 進(jìn)行相應(yīng)的更新
4.Store 更新后妻坝,發(fā)出一個(gè)"change"事件
5.View 收到"change"事件后,更新頁(yè)面
參考鏈接:
http://www.ruanyifeng.com/blog/2016/01/flux.html
10惊窖、React項(xiàng)目用過(guò)什么腳手架刽宪?Mern? Yeoman?
Mern:MERN是腳手架的工具,它可以很容易地使用Mongo, Express, React and NodeJS生成同構(gòu)JS應(yīng)用界酒。它最大限度地減少安裝時(shí)間圣拄,并得到您使用的成熟技術(shù)來(lái)加速開(kāi)發(fā)。
參考鏈接:http://www.open-open.com/lib/view/open1455953055292.html
11React 中的keys是什么毁欣,為什么它們很重要庇谆?
keys是什么幫助 React 跟蹤哪些項(xiàng)目已更改、添加或從列表中刪除凭疮。
return (
<ul>
{this.state.todoItems.map(({task, uid}) => {
return <li key={uid}>{task}</li>
})}
</ul>
)
- 在開(kāi)發(fā)過(guò)程中饭耳,我們需要保證某個(gè)元素的 key 在其同級(jí)元素中具有唯一性。在 React Diff 算法中 React 會(huì)借助元素的 Key 值來(lái)判斷該元素是新近創(chuàng)建的還是被移動(dòng)而來(lái)的元素执解,從而減少不必要的元素重渲染哥攘。此外,React 還需要借助 Key 值來(lái)判斷元素與本地狀態(tài)的關(guān)聯(lián)關(guān)系,因此我們絕不可忽視轉(zhuǎn)換函數(shù)中 Key 的重要性逝淹。
每個(gè) keys 在兄弟元素之間是獨(dú)一無(wú)二的耕姊。我們已經(jīng)談過(guò)幾次關(guān)于和解(reconciliation)的過(guò)程,而且這個(gè)和解過(guò)程(reconciliation)中的一部分正在執(zhí)行一個(gè)新的元素樹(shù)與最前一個(gè)的差異栅葡。keys 使處理列表時(shí)更加高效茉兰,因?yàn)?React 可以使用子元素上的 keys 快速知道元素是新的還是在比較樹(shù)時(shí)才被移動(dòng)。
而且 keys 不僅使這個(gè)過(guò)程更有效率欣簇,而且沒(méi)有 keys 规脸,React 不知道哪個(gè)本地狀態(tài)對(duì)應(yīng)于移動(dòng)中的哪個(gè)項(xiàng)目。所以當(dāng)你 map 的時(shí)候熊咽,不要忽略了 keys 莫鸭。
12調(diào)用 setState 之后發(fā)生了什么?
在代碼中調(diào)用setState函數(shù)之后横殴,React 會(huì)將傳入的參數(shù)對(duì)象與組件當(dāng)前的狀態(tài)合并被因,然后觸發(fā)所謂的調(diào)和過(guò)程(Reconciliation)。經(jīng)過(guò)調(diào)和過(guò)程衫仑,React 會(huì)以相對(duì)高效的方式根據(jù)新的狀態(tài)構(gòu)建 React 元素樹(shù)并且著手重新渲染整個(gè)UI界面梨与。在 React 得到元素樹(shù)之后,React 會(huì)自動(dòng)計(jì)算出新的樹(shù)與老樹(shù)的節(jié)點(diǎn)差異文狱,然后根據(jù)差異對(duì)界面進(jìn)行最小化重渲染粥鞋。在差異計(jì)算算法中,React 能夠相對(duì)精確地知道哪些位置發(fā)生了改變以及應(yīng)該如何改變瞄崇,這就保證了按需更新呻粹,而不是全部重新渲染。
- React會(huì)將當(dāng)前傳入的參數(shù)對(duì)象與組件當(dāng)前的狀態(tài)合并,然后觸發(fā)調(diào)和過(guò)程,在調(diào)和的過(guò)程中,React會(huì)以相對(duì)高效的方式根據(jù)新的狀態(tài)構(gòu)建React元素樹(shù)并且重新渲染整個(gè)UI界面.
React得到的元素樹(shù)之后,React會(huì)自動(dòng)計(jì)算出新的樹(shù)與老的樹(shù)的節(jié)點(diǎn)的差異,然后根據(jù)差異對(duì)界面進(jìn)行最小化的渲染,在React的差異算法中,React能夠精確的知道在哪些位置發(fā)生看改變以及應(yīng)該如何去改變,這樣就保證了UI是按需更新的而不是重新渲染整個(gè)界面.
13React 中 Element 與 Component 的區(qū)別是苏研?
簡(jiǎn)單而言尚猿,React Element 是描述屏幕上所見(jiàn)內(nèi)容的數(shù)據(jù)結(jié)構(gòu),是對(duì)于 UI 的對(duì)象表述楣富。典型的 React Element 就是利用 JSX 構(gòu)建的聲明式代碼片然后被轉(zhuǎn)化為createElement的調(diào)用組合凿掂。而 React Component 則是可以接收參數(shù)輸入并且返回某個(gè) React Element 的函數(shù)或者類(lèi)。
14在什么情況下你會(huì)優(yōu)先選擇使用 Class Component 而不是 Functional Component纹蝴?
在組件需要包含內(nèi)部狀態(tài)或者使用到生命周期函數(shù)的時(shí)候使用 Class Component 庄萎,否則使用函數(shù)式組件。
React 中 refs 的作用是什么塘安?
- 注意糠涛,根據(jù)React最新文檔,下面這種用法已經(jīng)被棄用了兼犯,統(tǒng)一改為回調(diào)函數(shù)模式
this.refs.textInput
Refs 是 React 提供給我們的安全訪問(wèn) DOM 元素或者某個(gè)組件實(shí)例的句柄忍捡。我們可以為元素添加ref屬性然后在回調(diào)函數(shù)中接受該元素在 DOM 樹(shù)中的句柄集漾,該值會(huì)作為回調(diào)函數(shù)的第一個(gè)參數(shù)返回:
class CustomForm extends Component {
handleSubmit = () => {
console.log("Input Value: ", this.input.value)
}
render () {
return (
<form onSubmit={this.handleSubmit}>
<input
type='text'
ref={(input) => this.input = input} />
<button type='submit'>Submit</button>
</form>
)
}
}
上述代碼中的input域包含了一個(gè)ref屬性,該屬性聲明的回調(diào)函數(shù)會(huì)接收input對(duì)應(yīng)的 DOM 元素砸脊,我們將其綁定到this指針以便在其他的類(lèi)函數(shù)中使用具篇。另外值得一提的是,refs 并不是類(lèi)組件的專(zhuān)屬凌埂,函數(shù)式組件同樣能夠利用閉包暫存其值:
function CustomForm ({handleSubmit}) {
let inputElement
return (
<form onSubmit={() => handleSubmit(inputElement.value)}>
<input
type='text'
ref={(input) => inputElement = input} />
<button type='submit'>Submit</button>
</form>
)
}
16 Controlled Component 與 Uncontrolled Component 之間的區(qū)別是什么驱显?15
React 的核心組成之一就是能夠維持內(nèi)部狀態(tài)的自治組件,不過(guò)當(dāng)我們引入原生的HTML表單元素時(shí)(input,select,textarea 等)瞳抓,我們是否應(yīng)該將所有的數(shù)據(jù)托管到 React 組件中還是將其仍然保留在 DOM 元素中呢埃疫?這個(gè)問(wèn)題的答案就是受控組件與非受控組件的定義分割。受控組件(Controlled Component)代指那些交由 React 控制并且所有的表單數(shù)據(jù)統(tǒng)一存放的組件孩哑。譬如下面這段代碼中username變量值并沒(méi)有存放到DOM元素中栓霜,而是存放在組件狀態(tài)數(shù)據(jù)中。任何時(shí)候我們需要改變username變量值時(shí)横蜒,我們應(yīng)當(dāng)調(diào)用setState函數(shù)進(jìn)行修改胳蛮。
class ControlledForm extends Component {
state = {
username: ''
}
updateUsername = (e) => {
this.setState({
username: e.target.value,
})
}
handleSubmit = () => {}
render () {
return (
<form onSubmit={this.handleSubmit}>
<input
type='text'
value={this.state.username}
onChange={this.updateUsername} />
<button type='submit'>Submit</button>
</form>
)
}
}
而非受控組件(Uncontrolled Component)則是由DOM存放表單數(shù)據(jù),并非存放在 React 組件中愁铺。我們可以使用 refs 來(lái)操控DOM元素:
class UnControlledForm extends Component {
handleSubmit = () => {
console.log("Input Value: ", this.input.value)
}
render () {
return (
<form onSubmit={this.handleSubmit}>
<input
type='text'
ref={(input) => this.input = input} />
<button type='submit'>Submit</button>
</form>
)
}
}
竟然非受控組件看上去更好實(shí)現(xiàn),我們可以直接從 DOM 中抓取數(shù)據(jù)闻鉴,而不需要添加額外的代碼茵乱。不過(guò)實(shí)際開(kāi)發(fā)中我們并不提倡使用非受控組件,因?yàn)閷?shí)際情況下我們需要更多的考慮表單驗(yàn)證孟岛、選擇性的開(kāi)啟或者關(guān)閉按鈕點(diǎn)擊瓶竭、強(qiáng)制輸入格式等功能支持,而此時(shí)我們將數(shù)據(jù)托管到 React 中有助于我們更好地以聲明式的方式完成這些功能渠羞。引入 React 或者其他 MVVM 框架最初的原因就是為了將我們從繁重的直接操作 DOM 中解放出來(lái)斤贰。
在生命周期中的哪一步你應(yīng)該發(fā)起 AJAX 請(qǐng)求?
- 我們應(yīng)當(dāng)將AJAX 請(qǐng)求放到 componentDidMount 函數(shù)中執(zhí)行次询,主要原因有下:
React 下一代調(diào)和算法 Fiber 會(huì)通過(guò)開(kāi)始或停止渲染的方式優(yōu)化應(yīng)用性能荧恍,其會(huì)影響到 componentWillMount 的觸發(fā)次數(shù)。對(duì)于 componentWillMount 這個(gè)生命周期函數(shù)的調(diào)用次數(shù)會(huì)變得不確定屯吊,React 可能會(huì)多次頻繁調(diào)用 componentWillMount送巡。如果我們將 AJAX 請(qǐng)求放到 componentWillMount 函數(shù)中,那么顯而易見(jiàn)其會(huì)被觸發(fā)多次盒卸,自然也就不是好的選擇骗爆。
如果我們將 AJAX 請(qǐng)求放置在生命周期的其他函數(shù)中,我們并不能保證請(qǐng)求僅在組件掛載完畢后才會(huì)要求響應(yīng)蔽介。如果我們的數(shù)據(jù)請(qǐng)求在組件掛載之前就完成摘投,并且調(diào)用了setState函數(shù)將數(shù)據(jù)添加到組件狀態(tài)中煮寡,對(duì)于未掛載的組件則會(huì)報(bào)錯(cuò)。而在 componentDidMount 函數(shù)中進(jìn)行 AJAX 請(qǐng)求則能有效避免這個(gè)問(wèn)題犀呼。
17 shouldComponentUpdate 的作用是啥以及為何它這么重要幸撕?
- shouldComponentUpdate 允許我們手動(dòng)地判斷是否要進(jìn)行組件更新,根據(jù)組件的應(yīng)用場(chǎng)景設(shè)置函數(shù)的合理返回值能夠幫我們避免不必要的更新圆凰。
18 如何告訴 React 它應(yīng)該編譯生產(chǎn)環(huán)境版本杈帐?
通常情況下我們會(huì)使用 Webpack 的 DefinePlugin 方法來(lái)將 NODE_ENV 變量值設(shè)置為 production。編譯版本中 React 會(huì)忽略 propType 驗(yàn)證以及其他的告警信息专钉,同時(shí)還會(huì)降低代碼庫(kù)的大小挑童,React 使用了 Uglify 插件來(lái)移除生產(chǎn)環(huán)境下不必要的注釋等信息。
19 為什么我們需要使用 React 提供的 Children API 而不是 JavaScript 的 map?
React.Children.map(props.children, () => ) instead of props.children.map(() => )
props.children并不一定是數(shù)組類(lèi)型藕各,譬如下面這個(gè)元素:
<Parent>
<h1>Welcome.</h1>
</Parent>
如果我們使用props.children.map函數(shù)來(lái)遍歷時(shí)會(huì)受到異常提示充易,因?yàn)樵谶@種情況下props.children是對(duì)象(object)而不是數(shù)組(array)。React 當(dāng)且僅當(dāng)超過(guò)一個(gè)子元素的情況下會(huì)將props.children設(shè)置為數(shù)組尽楔,就像下面這個(gè)代碼片:
<Parent>
<h1>Welcome.</h1>
<h2>props.children will now be an array</h2>
</Parent>
這也就是我們優(yōu)先選擇使用React.Children.map函數(shù)的原因,其已經(jīng)將props.children不同類(lèi)型的情況考慮在內(nèi)了第练。
20 createElement 與 cloneElement 的區(qū)別是什么阔馋?
createElement 函數(shù)是 JSX 編譯之后使用的創(chuàng)建 React Element 的函數(shù),而 cloneElement 則是用于復(fù)制某個(gè)元素并傳入新的 Props娇掏。
簡(jiǎn)單地說(shuō)呕寝,一個(gè) React element 描述了你想在屏幕上看到什么。
換個(gè)說(shuō)法就是婴梧,一個(gè) React element 是一些 UI 的對(duì)象表示下梢。
一個(gè) React Component 是一個(gè)函數(shù)或一個(gè)類(lèi),
它可以接受輸入并返回一個(gè) React element
(通常是通過(guò) JSX 塞蹭,它被轉(zhuǎn)化成一個(gè) createElement 調(diào)用)孽江。
21 傳入 setState 函數(shù)的第二個(gè)參數(shù)的作用是什么?
該函數(shù)會(huì)在setState函數(shù)調(diào)用完成并且組件開(kāi)始重渲染的時(shí)候被調(diào)用番电,我們可以用該函數(shù)來(lái)監(jiān)聽(tīng)渲染是否完成:
this.setState(
{ username: 'tylermcginnis33' },
() => console.log('setState has finished and the component has re-rendered.')
)
22 reactJS的props.children.map函數(shù)來(lái)遍歷會(huì)收到異常提示岗屏,為什么?應(yīng)該如何遍歷漱办?
this.props.children 的值有三種可能:
1.當(dāng)前組件沒(méi)有子節(jié)點(diǎn)担汤,它就是 undefined;
2.有一個(gè)子節(jié)點(diǎn),數(shù)據(jù)類(lèi)型是 object 洼冻;
3.有多個(gè)子節(jié)點(diǎn)崭歧,數(shù)據(jù)類(lèi)型就是 array 。
系統(tǒng)提供React.Children.map()方法安全的遍歷子節(jié)點(diǎn)對(duì)象
23 面試中的一道題:
constructor() {
super();
this.state = {
val: 0
};
}
componentDidMount() {
this.setState({val: this.state.val + 1});
console.log(this.state.val); // 第 1 次 log
this.setState({val: this.state.val + 1});
console.log(this.state.val); // 第 2 次 log
setTimeout(() => {
this.setState({val: this.state.val + 1});
console.log(this.state.val); // 第 3 次 log
this.setState({val: this.state.val + 1});
console.log(this.state.val); // 第 4 次 log
}, 0);
}
render() {
return null;
}
};
問(wèn)上述代碼中 4 次 console.log 打印出來(lái)的 val 分別是多少撞牢?
答案:4 次 log 的值分別是:0率碾、0叔营、2、3所宰。
(如果想知道到底為什么绒尊,可以看另一篇文章,《React中this.setState到底做了什么仔粥?》)