從零開始學React:二檔 React生命週期以及元件開發

Mr.奇淼發表於2019-02-13

手挽手帶你學React入門二檔,元件開發的開始,合理運用生命週期和元件,能夠讓你的開發變地流利又happy,這篇文章帶你學會建立元件,運用組建。學起來吧!

視訊教程

React 元件生命週期

學習React,生命週期很重要,我們瞭解完生命週期的各個元件,對寫高效能元件會有很大的幫助. 我們先來看一張圖

從零開始學React:二檔 React生命週期以及元件開發

元件初始化的時候

1、getDefaultProps()

設定預設的props,也可以用dufaultProps設定元件的預設屬性.

2、getInitialState()

在使用es6的class語法時是沒有這個鉤子函式的,可以直接在constructor中定義this.state。此時可以訪問this.props

3、componentWillMount()

元件初始化時只呼叫,以後元件更新不呼叫,整個生命週期只呼叫一次,此時可以修改state。

4、 render()

react最重要的步驟,建立虛擬dom,進行diff演算法,更新dom樹都在此進行。此時就不能更改state了。

5、componentDidMount()

元件渲染之後呼叫,只呼叫一次。

元件更新的時候 6、componentWillReceiveProps(nextProps)

元件初始化時不呼叫,元件接受新的props時呼叫。

7、shouldComponentUpdate(nextProps, nextState)

react效能優化非常重要的一環。元件接受新的state或者props時呼叫,我們可以設定在此對比前後兩個props和state是否相同,如果相同則返回false阻止更新,因為相同的屬性狀態一定會生成相同的dom樹,這樣就不需要創造新的dom樹和舊的dom樹進行diff演算法對比,節省大量效能,尤其是在dom結構複雜的時候

8、componentWillUpdata(nextProps, nextState)

元件初始化時不呼叫,只有在元件將要更新時才呼叫,此時可以修改state,render 的時候會用你更改的值,但是這裡面不能呼叫 this.setState(),會讓你陷入死迴圈

9、render()

元件渲染

10、componentDidUpdate()

元件初始化時不呼叫,元件更新完成後呼叫,此時可以獲取dom節點。

元件解除安裝的時候

11、componentWillUnmount()

元件將要解除安裝時呼叫,一些事件監聽和定時器需要在此時清除。


// 在我們元件第一次載入完的時候會這樣執行生命週期
export default class App extends Component {
    constructor(){
        super()
        this.state={
        
        }
    }

    // getDefaultProps(){
    // es6 不支援這個
    // console.log('1.執行getDefaultProps')
    // 具體用法在下面
    // }
    
    // getInitialState(){
    // es6裡不用它了
    // console.log('2.執行getInitialState')
    // 這個實際上是上面的state 在 constructor實現
    // }
    componentWillMount(){
        console.log('3.執行componentWillMount')
    }
    render() {
        console.log('4.執行render')
        return (
            
        )
    }
    componentDidMount(){
        console.log('5.執行componentWillMount')

    }
}

// 接 getDefaultProps
// 在ES6裡這麼玩

// 還有一種玩法是ES7裡面的
// static defaultProps = {
 //     name: 'demo'
 // };  這裡我們不多講了。。講多了該暈了 感興趣的加我微信 shouzi_1994
App.defaultProps = {
    name: 'demo'
};
複製程式碼

// 在我們元件更新的時候我們進行如下生命週期
export default class App extends Component {
    constructor(){
        super()
        this.state={
            name:'test'
        }
    }
    componentWillReceiveProps(nextProps){
        // props 更新的時候呼叫
        console.log('1.執行componentWillReceiveProps')
    }
    shouldComponentUpdate(nextProps, nextState){
        console.log('2.執行shouldComponentUpdate')
        // 這個需要著重強調,我們優化效能用它很重要!如果我們即將更新的東西和原來的資料相同,return一個false就停止元件更新節約效能
        if(nextState.name == this.state.name){
            return false
        }else{
            return true
        }
    }
    componentWillMount(nextProps, nextState){
        // 在元件渲染前 呼叫這個
        console.log('3.執行componentWillMount')
    }
    change=()=>{
        this.setState({name:'QM'})
    }
    render() {
        console.log('4.執行render')
        return (
            <button onClick={this.change}>測試</button>
        )
    }
   componentDidUpdate(){
        console.log('5.執行componentDidUpdate')       
   }
   componentWillUnmount(){
    //    這個在解除安裝的時候會呼叫 業務邏輯寫裡面就好了 
   }
}
複製程式碼

生命週期簡單介紹就這些了,真正工作中要根據業務需求來呼叫,大大增加你的開發效率,處理你開發中遇到的難題。

元件建立

其實我們前面的App實際上就是一個元件了,我們類比到vue裡面,它就是腳手架為我們建立的App.vue 是一個根元件,我們別的元件都渲染到這個元件的內部就好了。

那麼 我們如何建立一個子元件呢?

export default class App extends Component {
    constructor(){
        super()
        this.state={
        
        }
    }
  
    render() {
        //要想使用子元件也很簡單 用標籤的形式拿進來就好了
        return (
            <Children />
        )
    }
  
}

//下面我們再寫一個class 並且繼承 Component 這就是一個元件了

class Children extends Component{
    constructor(){
        super()
        this.state={
        
        }
    }
    render(){
        return(
            <h1>我是子元件</h1>
        )
    }
}

複製程式碼

元件傳參

我們學會了如何建立元件,那麼元件怎麼傳參呢?這個是非常重要的東西,畢竟元件之間不能通訊,那就沒有建立它的意義了。

父子傳參

在React中父子傳參極其類似Vue 直接在標籤上填寫屬性,但是在子元件接收的時候會有所不同

我們接著使用上面的程式碼

export default class App extends Component {
    constructor(){
        super()
        this.state={
        
        }
    }
  
    render() {
        //要想使用子元件也很簡單 用標籤的形式拿進來就好了
        return (
            <Children params={"我從父元件傳過來"}/>
        )
    }
  
}

//下面我們再寫一個class 並且繼承 Component 這就是一個元件了

class Children extends Component{
    constructor(){
        super()
        this.state={
        
        }
    }
    // 通過這種形式 我們就可以把父元件的東西傳遞給子元件了,所有的屬性儲存在props裡面,上面我們介紹生命週期的時候,也提了一下如何建立預設props。這裡我們寫一下

    render(){
        return(
            <div>
                <h1>我是子元件</h1>
                <h2>{this.props.params}</h2>
            </div>
        )
    }
    // 如果父元件沒有傳遞 params 我們又想使用預設的props 那麼就要使用下面的寫法
}

Children.defaultProps = {
    params: '我是預設的東西'
};

// 設定了預設props以後 如果我們不傳參就會預設展示我們預設規定好的屬性了
複製程式碼

插槽(類似Vue solt) 為什麼要把插槽放在這裡講解呢?實際上React的插槽是通過傳參的形式下來的,怎麼理解呢?我覺得放進程式碼中是最容易理解的。

export default class App extends Component {
    constructor(){
        super()
        this.state={
        
        }
    }
  
    render() {
        //要想使用子元件也很簡單 用標籤的形式拿進來就好了
        return (
            <Children params={"我從父元件傳過來"}>
                <div>傳遞第一個</div>
                <div>傳遞第二個</div>
                <div>傳遞第三個</div>
            </Children>
        )
    }
  
}

//下面我們再寫一個class 並且繼承 Component 這就是一個元件了

class Children extends Component{
    constructor(){
        super()
        this.state={
        
        }
    }
    // 通過這種形式 我們就可以把父元件的東西傳遞給子元件了,所有的屬性儲存在props裡面,上面我們介紹生命週期的時候,也提了一下如何建立預設props。這裡我們寫一下

    render(){
        console.log(this.props)
        // {children:Array(3)
        // params:"我從父元件傳過來"}


        // children的內容為 [
            // {$$typeof: Symbol(react.element), type: "div", key: null, ref: null, props: {…}, …},
            // {$$typeof: Symbol(react.element), type: "div", key: null, ref: null, props: {…}, …},
            // {$$typeof: Symbol(react.element), type: "div", key: null, ref: null, props: {…}, …}
            // ]
        //我們可以看到 他們被順序放到了 this.props.children中 並且 這是個陣列 內部儲存的就是虛擬dom
        
        return(
            <div>
                <h1>我是子元件</h1>
                {/* 這裡我用迴圈的方式把三個children取出來直接渲染即可 */}
                {this.props.children.map((item,key)=>{
                    return item
                })}
                {/* 我們更加直觀一點 */}
                {this.props.children[1]}
                {this.props.children[0]}
                {this.props.children[2]}
                 {/* 看到這裡大家應該可以知道插槽的簡單用法了吧 */}
            </div>
        )
    }
    // 如果父元件沒有傳遞 params 我們又想使用預設的props 那麼就要使用下面的寫法
}

Children.defaultProps = {
    params: '我是預設的東西'
};

複製程式碼

子傳父參 在Vue中我們可以通過定義函式,以實參的形式傳遞,在父元件捕獲的形式來獲取想要傳遞的引數,那麼在React中這個辦法是否也同樣適用呢?答案是肯定的,依舊是通過父元件宣告函式,傳遞給子元件,子元件呼叫並傳入引數,在父元件捕獲即可。

export default class App extends Component {
    constructor(){
        super()
        this.state={
            myPatams:"test"
        }
    }
    getParams(params){
        console.log(params,this)
        this.setState({
            myPatams:params
        })
    }
    render() {
        //要想使用子元件也很簡單 用標籤的形式拿進來就好了
        return (
            <div>    
                {this.state.myPatams}
                <Children params={"我從父元件傳過來"} getParams={this.getParams.bind(this)}></Children>
                {/* 這裡我們把函式傳遞下去,一定要bind this 否則在我們在子元件中使用bind來呼叫的時候,this的指向會跑到子元件中 我們拿到的引數意義就不大了 當然箭頭函式也是完全沒問題的 */}
            </div>
        )
    }
  
}

//下面我們再寫一個class 並且繼承 Component 這就是一個元件了

class Children extends Component{
    constructor(){
        super()
        this.state={
        
        }
    }
    render(){

        return(
            <div>
                <h1>我是子元件</h1>
                <button onClick={this.props.getParams.bind(this,"我是子傳過來的引數")}>子傳父參</button>
                {/* 我們在這裡呼叫父元件傳遞過來的方法,並且傳入引數 */}
            </div>
        )
    }
}

Children.defaultProps = {
    params: '我是預設的東西'
};


複製程式碼

非父非子傳參(events 方法) 這種非關係型元件傳參一般使用redux來傳參,這裡我們還有沒學習到,我們藉助node的EventEmitter也可以輕鬆實現

import React,{Component} from 'react'
// 首先我們建立一個監聽器
import EventEmitter from 'events'; //事件監控物件
let emitter = new EventEmitter; //建立一個事件監控者 普通級別的監聽
emitter.setMaxListeners(100000); //設定監控管理器的最大監聽數量
export default class App extends Component {
    constructor(){
        super()
        this.state={
            myPatams:"test"
        }
    }
    getParams(params){
        console.log(params,this)
        this.setState({
            myPatams:params
        })
    }
    render() {
        //要想使用子元件也很簡單 用標籤的形式拿進來就好了
        return (
            <div>    
                {this.state.myPatams}
                <Children params={"我從父元件傳過來"} getParams={this.getParams.bind(this)}></Children>
                {/* 這裡我們把函式傳遞下去,一定要bind this 否則在我們在子元件中使用bind來呼叫的時候,this的指向會跑到子元件中 我們拿到的引數意義就不大了 當然箭頭函式也是完全沒問題的 */}
                <ChildrenTwo />
            </div>
        )
    }
  
}

//下面我們再寫一個class 並且繼承 Component 這就是一個元件了

class Children extends Component{
    constructor(){
        super()
        this.state={
            emit:""
        }
    }
    componentWillMount(){
        emitter.on('childrenEmit',(param)=>{  //我們在這裡設定監聽
            console.log(param)
            this.setState({
                emit:param
            })
        })
    }
    render(){

        return(
            <div>
                <h1>{this.state.emit}</h1>
                <button onClick={this.props.getParams.bind(this,"我是子傳過來的引數")}>子傳父參</button>
                {/* 我們在這裡呼叫父元件傳遞過來的方法,並且傳入引數 */}
            </div>
        )
    }
}

class ChildrenTwo extends Component{
    constructor(){
        super()
        this.state={
        
        }
    }
    gaveParams(param){
        emitter.emit('childrenEmit',param)  //從第二個子元件觸發並且傳遞引數
    }
    render(){

        return(
            <div>
                <h1>我是子元件</h1>
                <button onClick={this.gaveParams.bind(this,"我是ChildrenTwo傳過來的引數")}>非父非子傳參</button>
            </div>
        )
    }
}



複製程式碼

元件抽離

寫了這麼多,相信大家差不多會寫自己的元件並且在元件中傳遞引數了,這裡還有一個問題,大家是不是發現我的所有元件都寫在了一個JS內部,這樣如果是一個大專案,我們的這個JS就會變地非常臃腫,這時候我們就要抽離這個元件。實際上非常簡單,用到的就是我們ES6的 export 匯出 即可 如果只有一個元件 那麼 還可以像我們書寫App這個基礎元件一樣 使用 export default 預設匯出

這裡我給大家抽離一下

// App.js
import React,{Component} from 'react'
import {Children} from './components/Children.js'  // 因為我們的Children中使用的是 export 來暴露

//import Children from './components/Children.js' //  如果使用的是 export default 則應該這樣寫

export default class App extends Component {
    constructor(){
        super()
        this.state={
            myPatams:"test"
        }
    }
    getParams(params){
        console.log(params,this)
        this.setState({
            myPatams:params
        })
    }
    render() {
        //要想使用子元件也很簡單 用標籤的形式拿進來就好了
        return (
            <div>    
                {this.state.myPatams}
                <Children params={"我從父元件傳過來"} getParams={this.getParams.bind(this)}></Children>
                {/* 這裡我們把函式傳遞下去,一定要bind this 否則在我們在子元件中使用bind來呼叫的時候,this的指向會跑到子元件中 我們拿到的引數意義就不大了 當然箭頭函式也是完全沒問題的 */}
            </div>
        )
    }
  
}
複製程式碼
// children.js
import React,{Component} from 'react'

// 當我們抽離出來以後 必須要再次引入 react的必要元件

export class Children extends Component{  //我們這裡使用的是export來暴露  如果只有一個元件 也可以使用 export default 來暴露
    constructor(){
        super()
        this.state={
        
        }
    }
    render(){

        return(
            <div>
                <h1>我是子元件</h1>
                <button onClick={this.props.getParams.bind(this,"我是子傳過來的引數")}>子傳父參</button>
                {/* 我們在這裡呼叫父元件傳遞過來的方法,並且傳入引數 */}
            </div>
        )
    }
}

Children.defaultProps = {
    params: '我是預設的東西'
};


複製程式碼

大功告成了 程式碼是不是清晰多了??

總結

這一期內容不多 主要是介紹元件的生命週期 使用方法 以及如何傳參,這些內容可全都是乾貨,工作中會經常經常使用到,希望大家能自己寫小demo來熟悉一下寫法,下一期將會帶大家學習React-Router 以及context和高階元件的建立,為我們學習Redux打下基礎

視訊製作中

相關文章