徹底理解React組件的生命週期(一)

React組件的生命週期,主要分爲掛載、更新以及移除階段的生命週期。所謂掛載指的是組件從初始化到渲染到頁面的過程。卸載指的是組件生成的DOM元素從頁面中刪除的過程。更新指的是組件更新的過程。那麼在這些過程當中它都經歷了些什麼呢?

首先來一段代碼:

ReactDOM.render(
    <Book/>
    document.querySelector("#wrap")
)

其實以上代碼在經過React.js的編譯後會變成這個樣子:

ReactDOM.render(
    // <Book/>,
    React.createElement(Book),
    document.querySelector("#wrap")
)

也就是說將Book傳給了React.createElement函數,又將該函數的結果返回給了ReactDOM.rende。最後將組件掛載到了id爲wrap的元素中!我們也可以大膽的猜測一下在這個過程中它都做了些什麼事:

// React.createElement 中實例化一個 Book
const book = new Book(props, children)
// React.createElement 中調用 book.render 方法渲染組件的內容
const bookJsxObject = book.render()

// ReactDOM 用渲染後的 JavaScript 對象來來構建真正的 DOM 元素
const bookDOM = createDOMFromObject(bookJsxObject)
// ReactDOM 把 DOM 元素塞到頁面上
document.querySelector("#wrap").innerHTML=bookDOM;

以上代碼也可以簡單理解爲:

// 實例化組件,也就是構造DOM元素
constructor();
// 將構造的元素插入到指定的頁面元素中
render();

不過在掛載的過程當中,其實還有兩個方法,分別爲componentWillMount 和 componentDidMount。

// 實例化組件,也就是構造DOM元素
constructor();
// 當DOM元素載入之前調用
componentWillMount();
// 將構造的元素插入到指定的頁面元素中
render();
// 當DOM元素載入頁面之後調用
componentDidMount();

componentWillMount方法在組件掛載到頁面之前調用。而componentDidMount方法是在掛載到頁面以後調用。我們也可以將這兩個方法放到組件內部:

class Book extends React.Component{
    constructor(){
        super();
        console.log("執行constructor");
    }
    componentWillMount(){
        console.log("執行componentWillmount");
    }
    componentDidMount(){
        console.log("執行componentDidMount");
    }
    render(){
        console.log("執行render");
        return (
            <div>大家好,我叫張培躍</div>
        )
    }
}
ReactDOM.render(
    <Book/>,
    document.querySelector("#wrap")
)

通過以上示例的輸出結果,我們也可以看到方法的執行順序:

->執行constructor
->執行componentWillmount
->執行render
->執行componentDidMount
componentWillmount在render之前,componentDidMount在render之後

接下來咱們再來思考一個問題,我們既然可以將組件掛載到頁面中,自然也可以將組件在頁面中進行卸載。而當組件卸載之前會調用組件的componentWillUnmount方法:

// 實例化組件,也就是構造DOM元素
constructor();
// 當DOM元素載入之前調用
componentWillMount();
// 將構造的元素插入到指定的頁面元素中
render();
// 當DOM元素載入頁面之後調用
componentDidMount();
// 當從頁面中卸載時會調用
componentWillUnmount()

接下來,我們再來定義一個組件,讓該組件控制對Book組件的掛載與卸載:

class My extends React.Component{
    constructor(){
        super();
        this.state={
            isShow:true
        };
        this.changeIsShow=this.changeIsShow.bind(this);
    }
    changeIsShow(){
        this.setState({
            isShow:!this.state.isShow
        })
    }
    render(){
        return (
            <div>
                <input type="button" value="點我啊"
                           onClick={this.changeIsShow}/>
                {/*根據isShow來對Book進行掛載與卸載*/}
                {this.state.isShow?<Book />:null}
            </div>
        )
    }
}
ReactDOM.render(
    <My/>,
    document.querySelector("#wrap")
)

然後在在Book組件中增加componentWillUnmount方法:

class Book extends React.Component{
    constructor(){
        super();
        console.log("執行constructor");
    }
    componentWillMount(){
        console.log("執行componentWillmount");
    }
    componentDidMount(){
        console.log("執行componentDidMount");
    }
    componentWillUnmount(){
        console.log("執行componentWillUnmount");
    }
    render(){
        console.log("執行render");
        return (
            <div>大家好,我叫張培躍</div>
        )
    }
}

當點擊按鈕時控制檯會輸出最後一行,說明componentWillUnmount方法在被卸載時調用了:

->執行constructor
->執行componentWillmount
->執行render
->執行componentDidMount

完整示例代碼:

class Book extends React.Component{
    constructor(){
        super();
        console.log("執行constructor");
    }
    componentWillMount(){
        console.log("執行componentWillmount");
    }
    componentDidMount(){
        console.log("執行componentDidMount");
    }
    componentWillUnmount(){
        console.log("執行componentWillUnmount");
    }
    render(){
        console.log("執行render");
        return (
            <div>大家好,我叫張培躍</div>
        )
    }
}
class My extends React.Component{
    constructor(){
        super();
        this.state={
            isShow:true
        };
        this.changeIsShow=this.changeIsShow.bind(this);
    }
    changeIsShow(){
        this.setState({
            isShow:!this.state.isShow
        })
    }
    render(){
        return (
            <div>
                <input type="button" value="點我啊" 
                          onClick={this.changeIsShow}/>
                {/*根據isShow來對Book進行掛載與卸載*/}
                {this.state.isShow?<Book />:null}
            </div>
        )
    }
}
ReactDOM.render(
    <My/>,
    document.querySelector("#wrap")
)
到目前爲止,咱們已經見證了組件從出生到死亡的一個過程!接下來我們一起通過一個計數器的實例來了解這些函數的作用!

現在我們要完成一個倒計時的小應用,如下:


首先通過constructor進行初始化:

class AddOne extends React.Component{
    constructor(){
        super();
        //設置初始值
        this.state={
            num:0,
            timer:null
        }
    }
    componentWillMount(){
        // 組件掛載時添加計數器
        this.timer=setInterval(()=>{
            this.setState({
                num:this.state.num+1
            })
        },1000)
    }
    componentWillUnmount(){
        // 組件卸載時移除計數器
        clearInterval(this.timer);
    }
    render(){
        return (
            <div>
                <h1>{this.state.num}</h1>
            </div>
        )
    }
}
class Start extends React.Component{
    constructor(){
        super();
        this.state={
            isShow:false
        };
        this.changeIsShow=this.changeIsShow.bind(this);
    }
    changeIsShow(){
        this.setState({
            isShow:!this.state.isShow
        })
    }
    render(){
        return (
            <div>
                <input type="button" value={this.state.isShow?"結束":"開始計時"} onClick={this.changeIsShow}/>
                {this.state.isShow?<AddOne />:null}
            </div>
        );
    }
}
ReactDOM.render(
    <Start/>,
    document.querySelector("#wrap")
)

小結一下:

  • componentWillMount:在組件調用 render 方法之前調用。
  • componentDidMount:是 DOM 元素已經插入頁面後調用。
  • componentWillUnmount:組件對應的 DOM 元素從頁面中卸載之前調用。

未完,等續

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章