霍格沃茲學習路線和麵試重點準備
React知識點
1:生命週期
1-1:生命週期階段(v16之前)
-
元件初始化階段
import React , (component) from 'react' class Text extends Component { constructor(props) { super(props); } } 1:元件繼承react component基類,從而擁有render,生命週期等方法。因為函式元件沒有繼承,所以函式元件沒有生命週期等特性。 2:super(props)用來呼叫基類的構造方法。也將父元件的props注入子元件,將元件讀取(props只可讀不可變,state可讀可變)
-
元件掛載階段
此階段分為
componentWillMount
render
componentDidMount
三個階段componentWillMount
在組建掛載前呼叫,而且只會被呼叫一次render
函式只負責返回UI不負責渲染。返回的階段依賴於它的引數。不能在裡面執行this.setState()會有改變元件的副作用。componentDidMount
元件掛載DOM後呼叫,只會被呼叫一次,
-
元件更新階段
首先要明確react元件的更新機制,setState引起的state更新或者是父元件重新render引起的props更新,都會引起子元件的重新render,當進行效能優化時,我們可以通過
shouldComponentUpdate
造成元件更新的情況
-
父元件重新渲染render
a:直接使用,當父元件重新render導致重傳props,子元件將直接跟這個重新渲染,無論props是否變化。可以通過shouldComponentUpdate方法發優化
class Child extends Component { shouldComponentUpdate(nextProps){ // 應該使用這個方法,否則無論props是否有變化都將會導致元件跟著重新渲染 if(nextProps.someThings === this.props.someThings){ return false } } render() { return <div>{this.props.someThings}</div } }
b.在componentWillReceiveProps方法中,將props轉換成自己的state
class Child extends Component { constructor(props) { super(props); this.state = { someThings: props.someThings }; } componentWillReceiveProps(nextProps) { // 父元件重傳props時就會呼叫這個方法 this.setState({someThings: nextProps.someThings}); } render() { return <div>{this.state.someThings}</div> } } //在該函式(componentWillReceiveProps)中呼叫 this.setState() 將不會引起第二次渲染 是因為componentWillReceiveProps中判斷props是否變化了,若變化了,this.setState將引起state變化,從而引起render,此時就沒必要再做第二次因重傳props引起的render了,不然重複做一樣的渲染了。
-
元件本身呼叫setState,無論state有沒有發生變化,可以通過shouldComponentData方法進行優化。
class Child extends Component { constructor(props) { super(props); this.state = { someThings:1 } } shouldComponentUpdate(nextStates){ // 應該使用這個方法,否則無論state是否有變化都將會導致元件重新渲染 if(nextStates.someThings === this.state.someThings){ return false } } handleClick = () => { // 雖然呼叫了setState ,但state並無變化 const preSomeThings = this.state.someThings this.setState({ someThings: preSomeThings }) } render() { return <div onClick = {this.handleClick}>{this.state.someThings}</div> } }
此階段分為
componentWillReceiveProps
shouldComponentUpdata,com
componentWillUpdate
render
componentDidUpdate
componentWillReceiveProps(nextPorps)
此方法只調用與props引起的元件跟新過程中,相應props變化之後進行更新的唯一方式,引數nextProps時父元件傳給當前元件的新props,但父元件render方法發呼叫不能保證重傳給的當前元件的props是有變化的,所以在此方法中根據nextPorps何this.props來查明重傳的props是否有變化,以及如果改變了執行啥,比如根據新this.setState觸發當前元件重新render- shouldComponentUpdate(nextProps, nextState)
此方法通過比較nextProps,nextState及當前元件的this.props,this.state,返回true時當前元件將繼續執行更新過程,返回false則當前元件更新停止,以此可用來減少元件的不必要渲染,優化元件效能。
ps:這邊也可以看出,就算
componentWillReceiveProps()
中執行了this.setState,更新了state,但在render前(如shouldComponentUpdate,componentWillUpdate),this.state依然指向更新前的state,不然nextState及當前元件的this.state的對比就一直是true了。如果
shouldComponentUpdate
返回false,那就一定不用rerender(重新渲染 )這個元件了,元件的React elements(React 元素) 也不用去比對。 但是如果shouldComponentUpdate
返回true,會進行元件的React elements比對,如果相同,則不用rerende
r這個元件,如果不同,會呼叫render函式進行rerender。- componentWillUpdate(nextProps, nextState)
此方法在呼叫render方法前執行,在這邊可執行一些元件更新發生前的工作,一般較少用。
- render
render方法在上文講過,這邊只是重新呼叫。
- componentDidUpdate(prevProps, prevState)
此方法在元件更新後被呼叫,可以操作元件更新的DOM,prevProps和prevState這兩個引數指的是元件更新前的props和state
-
-
解除安裝階段
此階段只有一個生命週期方法:componentWillUnmount
此方法在元件被解除安裝前呼叫,可以在這裡執行一些清理工作,比如清楚元件中使用的定時器,清楚componentDidMount中手動建立的DOM元素等,以避免引起記憶體洩漏。
1-2:生命週期方法
getInitialState
初始化this.state的值,只在元件掛載階段執行一次
getDefaultProps
只在租價你建立時呼叫一次並快取返回對物件(在React.createClass
之後就會呼叫)因為這個方法在例項初始化之前呼叫,所有在這個方法裡面不能依賴this獲取到這個元件的例項,在元件裝載之後這個方法快取的結果會用來保證訪問this.props屬性時,當這個元件沒有在父元件中傳入,也總是有值的
render
組裝生成這個元件的 HTML 結構(使用原生 HTML 標籤或者子元件),也可以返回 null
或者 false
,這時候 ReactDOM.findDOMNode(this)
會返回 null
。
componentWillMount
只會在裝載之前呼叫一次,在 render
之前呼叫,你可以在這個方法裡面呼叫 setState
改變狀態,並且不會導致額外呼叫一次 render
componentDidMount
在render之後,只在客戶端。之後元件已經生成了對應的DOM結構,可以通過this.getDOMNode()來進行訪問。 如果你想和其他JavaScript框架一起使用,可以在這個方法中呼叫setTimeout, setInterval或者傳送AJAX請求等操作(防止非同步操作阻塞UI)。,從這裡開始可以通過 ReactDOM.findDOMNode(this)
獲取到元件的 DOM 節點。
componentWillReceiveProps(更新觸發)
在元件接收到一個新的 prop (更新後)時被呼叫。這個方法在初始化render時不會被呼叫。
shouldComponentUpdate(更新觸發)
返回一個布林值。在元件接收到新的props或者state時被呼叫。在初始化時或者使用forceUpdate時不被呼叫。可以在你確認不需要更新元件時使用。
componentWillUpdate(更新觸發)
在元件接收到新的props或者state但還沒有render時被呼叫。在初始化時不會被呼叫。
componentDidUpdate(更新觸發)
在元件完成更新後立即呼叫。在初始化時不會被呼叫。
componentWillUnmount(解除安裝觸發)
在元件從 DOM 中移除之前立刻被呼叫。
2:React元件
劃分根據:是否擁有自己的state以及建立方式。通過類建立的元件一般是複雜元件,通過函式建立的元件一定是簡單元件。通過類建立的元件繼承了Component,從而擁有了Component的生命週期和特性。所有被稱為複雜元件
2-1:特點:
- 函式元件寫法簡潔,承擔的責任明確,本質是一個函式,接受一個外部傳入的props,返回對應的UI描述
- 類元件功能更加強大,繼承了React.component,擁有自己的生命週期以及一些鉤子函式,方便在不同階段對元件進行操作,對元件進行更多的控制
- 有狀態元件能使用this ,無狀態元件不能使用this
2-2:使用時機
如果想要儲存一些資料並且想要對這些資料進行增刪改查 那麼就應該使用有狀態元件,如果只是單純的處理一些邏輯就用無狀態元件。我們更多的應該是使用無狀態元件(因為如果一個是有狀態元件的話那麼他就會觸發一些生命週期定義的函式。一旦觸發這些函式就會影響當前專案的執行,所以儘可能使用無狀態元件,除非對當前元件不是很清晰是否要儲存資料的時候選擇有狀態元件)狀態的東西都會通過父級去傳遞,比如Persons,Header這些元件如果想用到資料的話我們可以通過傳參的形式給它傳遞過去,即當前的資料能夠進行統一的資料管理,比如說通過父級管理資料,其他元件如果想擁有這個元件的話可以通過傳值的形式給它。
2-3:元件通訊
2-3-1:父向子通訊
父元件通過props向子元件傳遞props,子元件的到props
function Father(props){
let name = "父元件值"
return(
<Son name={name}></Son>
)
}
function Son(props){
return(
<div>{props.name}</div>
)
}
2-3-2:子向父
利用回撥函式,可以實現子元件向父元件通訊,父元件一個函式作為props傳遞給子元件,子元件呼叫該回調函式,便可以向父元件通訊
class Father extends Component{
callback(meg){
console.log(msg)
}
render(){
return(
<Son callback={this.callback.bind(this)}></Son>
)
}
}
function Son(props){
let name = "son name"
return(
<div onclick={props.callback(name)}>{props.name}</div>
)
}
2-3-2:跨級元件通訊
跨級元件通訊:父元件向更深層次元件通訊,,一般有兩種方式中間元件層層傳遞props
使用context物件
對於第一種方式,如果結構層次較深,明顯增加了複雜程度,適用於三層以內的結構,當元件層次較深時,就需要採用context
,設定context,則這個結構樹的所有元件都可以共享這個context,但是如果內部出現context,則就近選擇context,使用方法
2-3-4:非巢狀元件通訊
非巢狀元件,就是沒有任何包含關係的元件,包括兄弟元件以及不在同一個父級中的非兄弟元件。對於非巢狀元件,可以採用下面兩種方式:
- 利用二者共同父元件的context物件通訊
- 使用自定義事件通訊
如果採用元件共同的父級進行中轉,會增加元件之間的耦合(推薦高內聚,低耦合),如果元件層次較深的話,不易找到公共父元件,這個時候我們就需要採用自定義事件的方式來實現非巢狀元件間的通訊
1:我們需要要給events包
npm install events
2:新建一個ev.js引入events包,並向外提供一個事件物件,供通訊使用,自定義事件是典型的釋出/訂閱模式,通過向事件物件上新增監聽器和觸發事件來實現元件間通訊。
//ev.js
import {EventEmitter} from "events"
export default new EventEmitter();
//app.js
import React from 'react'
import Foo form './Foo'
import Boo form './Boo'
class App extends React.component{
constructor(){
this.state = {
msg:""
}
}
render(){
return(
<>
<Foo/>
<Boo/>
</>
)
}
}
//Foo.js
import React,{ Component } from "react";
import emitter from "./ev"
export default class Foo extends Component{
constructor(props) {
super(props);
this.state = {
msg:null,
};
}
componentDidMount(){
// 宣告一個自定義事件,在元件裝載完成以後
this.eventEmitter = emitter.addListener("callMe",(msg)=>{
this.setState({
msg
})
});
}
// 元件銷燬前移除事件監聽
componentWillUnmount(){
emitter.removeListener(this.eventEmitter);
}
render(){
return(
<div>
{ this.state.msg }
我是非巢狀 1 號
</div>
);
}
}
//Boo.js
import emitter from "./ev"
export default class Boo extends Component{
render(){
const cb = (msg) => {
return () => {
// 觸發自定義事件
emitter.emit("callMe","Hello")
}
}
return(
<div>
我是非巢狀 2 號
<button onClick = { cb("blue") }>點選我</button>
</div>
);
}
}
2-3:高階元件
以引數為元件,返回值為新的元件,元件時將props轉化為UI,高階元件是將一個元件轉換為另一個元件
3:React context使用
設計目的是為了共享那些對於一個元件樹而言的全域性資料,比如當前認證使用者,主題,首選語言等。context主要運用場景在於很多不同層級的元件需要訪問同一些資料,需要謹慎使用,因為會使複用性變差
如果只是想避免層層傳遞一些屬性,元件組合有時候是比context更好的解決方案
3-1:context使用條件
- 上級元件需要宣告自己支援的context
- 子元件要宣告自己需要使用的context
3-2:context使用注意點
-
父元件需要宣告自己支援 context,並提供 context 中屬性的 PropTypes
-
子元件需要宣告自己需要使用 context,並提供其需要使用的 context 屬性的 PropTypes
-
父元件需提供一個 getChildContext 函式,以返回一個初始的 context 物件
-
如果元件中使用建構函式(constructor),還需要在建構函式中傳入第二個引數 context,並在 super 呼叫父類建構函式是傳入 context,否則會造成元件中無法使用 context。
constructor(props,context){ super(props,context); }
-
改變 context 物件
我們不應該也不能直接改變 context 物件中的屬性,要想改變 context 物件,只有讓其和父元件的 state 或者 props 進行關聯,在父元件的 state 或 props 變化時,會自動呼叫 getChildContext 方法,返回新的 context 物件,而後子元件進行相應的渲染。修改 App.js,讓 context 物件可變
3-3:context使用方法
/*app.js------------------------------------------------------------*/
import React, { Component } from 'react';
import PropTypes from "prop-types";
import Sub from "./Sub";
export default class App extends Component{
// 父元件宣告自己支援 context
static childContextTypes = {
color:PropTypes.string,
callback:PropTypes.func,
}
// 父元件提供一個函式,用來返回相應的 context 物件
getChildContext(){
return{
color:this.state.color,
callback:this.callback.bind(this)
}
}
callback(msg){
console.log(msg)
}
render(){
return(
<div>
<Sub></Sub>
</div>
);
}
}
/*Father.js --------------------------------------------*/
import React, { Component } from 'react';
import PropTypes from "prop-types";
import Son from "../Son/index";
class Father extends Component {
constructor(props){
super(props)
}
static contextTypes = {
color:PropTypes.string,
callback:PropTypes.func,
}
render() {
return (
<div>
<Son />
</div>
);
}
}
export default Father;
/*無狀態元件--------------------------------------------------------*/
const SubSub = (props,context) => {
const style = { color:context.color }
const cb = (msg) => {
return () => {
context.callback(msg);
}
}
return(
<div style = { style }>
SUBSUB
<button onClick = { cb("我胡漢三又回來了!") }>點選我</button>
</div>
);
}
SubSub.contextTypes = {
color:PropTypes.string,
callback:PropTypes.func,
}
/*Son.js-----------------------------------------------------------*/
import React,{ Component } from "react";
import PropTypes from "prop-types";
export default class Son extends Component{
// 子元件宣告自己需要使用 context
static contextTypes = {
color:PropTypes.string,
callback:PropTypes.func,
}
render(){
const style = { color:this.context.color }
const cb = (msg) => {
return () => {
this.context.callback(msg);
}
}
return(
<div style = { style }>
SUBSUB
<button onClick = { cb("我胡漢三又回來了!") }>點選我</button>
</div>
);
}
}