Easy的React學習筆記(二.進階)

上接

Easy的React學習筆記(一.基礎):

https://blog.csdn.net/qq_37279880/article/details/102487420


目錄

 

15_使用PropTypes校驗傳遞值的類型(Typechecking With PropTypes)

1.屬性類型校驗的意義:

2.校驗傳遞值的類型,格式如下:

3.必需值校驗(isRequired關鍵字):

4.默認prop值(defaultProps關鍵字)

16_ref屬性提高代碼語義化、setState的回調函數與console.log

17_組件的生命週期函數

1.Mounting: 組件掛載階段(組件掛載即組件插入DOM樹)

2.Updating: 組件更新階段

3.Unmounting---組件卸載卸載時

18_用生命週期函數shouldComponentUpdate改善程序性能

19_axios數據請求

1.axios安裝

解決方法:用管理員身份運行命令提示符(cmd)再繼續安裝axios,安裝成功:


15_使用PropTypes校驗傳遞值的類型(Typechecking With PropTypes)

官方文檔連接:

 

Typechecking With PropTypes

https://react.docschina.org/docs/typechecking-with-proptypes.html

1.屬性類型校驗的意義:

在父組件向子組件傳遞數據時,是在父組件子組件添加屬性(props)的方式,但是在子組件中並沒有對這些屬性進行校驗,就是說父組件傳遞過來的任何值都不會報錯,這在實際開發中是不允許的,在大型項目中如果不進行校驗,開發後期業務邏輯會變得非常混亂出現錯誤,業務邏輯錯誤不像語法錯誤非常容易找,所以屬性類型校驗就非常有必要了。

應用:

在Xiaojiejie.js中爲子組件XiaojiejieItem傳遞通過添加屬性的方式傳遞了四個值,有字符串、數字、方法,在子組件XiaojiejieItem中都是可以通過PropTypes校驗傳遞過來的值的類型。

XiaojiejieItem.js頭部引入PropTypes:

import PropTypes from 'prop-types'
// import a from 'prop-types'

 注意這裏是導入默認組件,所以前面的PropTypes想取名什麼都可以,取a都可以,

關鍵是校驗組件.propTypespropTypes是唯一寫法。

2.校驗傳遞值的類型,格式如下:

組件.propTypes={

    屬性:PropTypes.屬性類型,

}

紅框內propTypes唯一寫法,綠框內PropTypes在導入'prop-types'默認組件時,名字隋邊取,不過爲了方便閱讀理解,我取PropTypes

XiaojiejieItem.js完整代碼如下:

import React, { Component } from 'react';
import PropTypes from 'prop-types'
// import a from 'prop-types'

class XiaojiejieItem extends Component {
    constructor(props){
        super(props)
        this.handleClick=this.handleClick.bind(this)
    }
    render() { 
        return ( 
            <li onClick={this.handleClick}>{this.props.content}</li>
        )
    }
    handleClick(){
        // 測試能否取得list的索引
        // console.log(this.props.index)
        // 子組件不能直接修改父組件的數據,TypeError: Cannot assign to read only property 'list' of object '#<Object>'
        // this.props.list=[],報錯
        this.props.deleteItem(this.props.index)
    }
}

XiaojiejieItem.propTypes={
        // content:a.string,
        // index:a.func  
        // deleteItem:a.func,
    content:PropTypes.string,
    index:PropTypes.number,
    deleteItem:PropTypes.func
}

export default XiaojiejieItem;

 效果(沒有報錯,因爲傳遞的值和我們要求的PropType值是一樣):

如果把index的PropType值改成string,console就會報錯了:

XiaojiejieItem.propTypes={
        // content:a.string,
        // index:a.func  
        // deleteItem:a.func,
    content:PropTypes.string,
    index:PropTypes.string,
    deleteItem:PropTypes.func
}

意思是index屬性傳過來的number類型,我們自己要求的是string類型,所以報錯了。

當傳入的 prop 值類型不正確時,JavaScript 控制檯將會顯示警告。出於性能方面的考慮,propTypes 僅在開發模式下進行檢查。

 

3.必需值校驗(isRequired關鍵字):

在XiaojiejieItem.js中的<li></li>加入代碼{this.personname}爲你服務:

render() { 
    return ( 
    <li onClick={this.handleClick}>{this.props.personname}爲你服務{this.props.content}</li>
    )
}

此時父組件Xiaojiejie.js沒有傳遞personname的值過來,也不會報錯:

如果我們在子組件XiaojiejieItem.js下方校驗處,爲personname的校驗加上isRequired關鍵字,父組件Xiaojiejie不傳值就會報錯:

XiaojiejieItem.propTypes={
        // content:a.string,
        // index:a.func  
        // deleteItem:a.func,
    content:PropTypes.string,
    index:PropTypes.number,
    deleteItem:PropTypes.func,
    personname:PropTypes.string.isRequired
}

Warning: Failed prop type: The prop `personname` is marked as required in `XiaojiejieItem`, but its value is `undefined`.

意思是我們在XiaojiejieItem的校驗時要求personname屬性必須傳值的,所以報錯了。

我們在爲父組件Xiaojiejie引用子組件XiaojiejieItem時,爲XiaojiejieItem的personname屬性定義值:

<XiaojiejieItem 
content={item} 
index={index}
key={item+index}
deleteItem={this.deleteItem.bind(this)}
personname='石原里美'
/>

報錯解決: 

4.默認prop值(defaultProps關鍵字)

我們在爲把父組件Xiaojiejie引用子組件XiaojiejieItem時XiaojiejieItem的personname屬性刪掉,在XiaojiejieItem.js中爲personname屬性加入默認值'新垣結衣'

XiaojiejieItem.defaultProps={
    personname:'新垣結衣'
}

實現效果:

格式:

子組件名.defaultProps={
    屬性:'屬性名'
}

16_ref屬性提高代碼語義化、setState的回調函數與console.log

1.用爲標籤加上ref屬性,提高代碼可讀性(語義化):

React開發過程中通常有不少語義化模糊的代碼,代碼語義化模糊影響開發效率、代碼可讀性,我們通過ref屬性提高代碼代碼可讀性。

之前的inputChange方法中通過inputValue:e.target.value的寫法修改文本框的值,這種寫法顯然不直觀。

如何實現:爲標籤加上ref屬性,從而把標籤綁定到'this.標籤'上,實現代碼語義化

爲input標籤加入ref屬性,通過ref屬性、箭頭函數把input標籤綁定到this.input上:

<input 
id='addList' 
className='input' 
value={this.state.inputValue} 
onChange={this.inputChange.bind(this)} 
// 通過ref屬性、箭頭函數把input標籤綁定到this.input上
ref={(input)=>{this.input=input}}
/> 

inputValue:e.target.value就修改爲:inputValue:this.input.value

inputChange(e){     //綁定響應事件改變文本框inputValue的值
    console.log(this);//onChange事件中重新bind了this指向Xiaojiejie,如果沒bind就是undefined
    console.log(e);//e是合成事件SyntheticEvent
    console.log(e.target);//e.target是合成事件的target屬性,即整個input標籤
    console.log(e.target.value);//e.target.value即input標籤的value值
    // this.state.inputValue=e.target.value;//錯誤寫法:因爲沒用沒用this.setState方法
    // console.log(this);//輸出undefined,所以要bind方法綁定this
    // console.log(this === window);//false
    this.setState({      //React中改變組件內部的狀態數據的值要用this.setState方法,注意setState的的大小寫
        // inputValue:e.target.value
        inputValue:this.input.value
    })
}

效果:

原理:

實際上通過console.log發現,其實e.target就是input標籤,所以我們通過爲input標籤加入ref屬性,通過ref屬性、箭頭函數把input標籤綁定到this.input上,從而實現語義化。

注意:

這就使我們的代碼變得語義化和優雅的多。但是就我個人的經驗來講,我是不建議用ref這樣操作的,因爲React的是數據驅動的,所以用ref會出現各種問題。

2.setState的回調函數與console.log

需求:

ref綁定取得ul內服務項(li)的數量。

實現:

①爲ul標籤加入ref屬性,通過ref屬性、箭頭函數把ul標籤綁定到this.ul上:

<ul ref={(ul)=>{this.ul=ul}}>
{ 
    this.state.list.map((item,index)=>{
        return(                                                
        //      拆分成子組件組件寫法
                <XiaojiejieItem 
                content={item} 
                index={index}
                key={item+index}
                deleteItem={this.deleteItem.bind(this)}
        )   
    })
} 
</ul>

 ②addList方法中通過this.ul.querySelectorAll('li').length輸出ul標籤內li的長度

addList(){      //爲按鈕綁定增加服務項事件
    this.setState({
        list:[...this.state.list,this.state.inputValue], //...這個是ES6的新語法的擴展運算符,名爲擴展運算符。作用把list數組進行了分解,形成了新的數組,然後再進行組合。這種寫法更簡單和直觀,所以推薦這種寫法。
        inputValue:''
    })
    console.log(this.ul.querySelectorAll('li').length)
}

HTML DOM querySelectorAll() 方法:

https://www.runoob.com/jsref/met-document-queryselectorall.html

效果(發現控制檯console的li長度總比實際的少1): 

原因react中的setState方法異步方法,setState方法的執行是需要時間的,addList方法中每次setState沒執行完就已經console輸出了。

解決方法:

③setState方法提供了回調函數,把console.log輸出寫進setState方法的回調函數,以保證輸出時setState方法已經執行,修改addList方法代碼如下:

addList(){      //爲按鈕綁定增加服務項事件
    this.setState({
        list:[...this.state.list,this.state.inputValue], //...這個是ES6的新語法的擴展運算符,名爲擴展運算符。作用把list數組進行了分解,形成了新的數組,然後再進行組合。這種寫法更簡單和直觀,所以推薦這種寫法。
        inputValue:''
    },
    ()=>{
        console.log(this.ul.querySelectorAll('li').length)
    })     
}

實現效果(控制檯console的li長度與實際相等):

17_組件的生命週期函數

生命週期函數:

定義:生命週期函數指在某一個時刻組件會自動調用執行的函數,用於在組件不同階段執行自定義功能。

舉例:

render()函數,就是一個生命週期函數,它在state(狀態值)發生改變時自動執行。這就是一個標準的自動執行函數。

React組件的生命週期圖(生命週期的三個階段,React16.4 version中把Iconstructor()的Initialization初始化階段放進了掛載階段,加粗的方法是常用的生命週期方法):

React組件生命週期主要分爲三個階段():

1.Mounting: 掛載階段(在組件被創建並插入到 DOM 時)

2.Updation: 更新階段

3.Unmounting: 卸載階段

1.Mounting: 組件掛載階段(組件掛載即組件插入DOM樹)

Mounting階段叫掛載階段,伴隨着整個虛擬DOM的生成,它裏邊有幾個小的生命週期函數,調用順序如下(其中只有constructor、render、componentDidMount是常用的,如上圖所示):  

①.constructor初始化(常用

在構造函數constructor()中初始化this.props、this.state。

constructor()不算生命週期函數,是構造函數,它是ES6的基本語法。

雖然它和生命週期函數的性質一樣,但不能認爲是生命週期函數。

但是你要心裏把它當成一個生命週期函數,把它看成React的Initialization階段,用來定義屬性(props)和狀態

(state)。

②.componentWillMount(已過時,幾乎不用) : 在組件即將被掛載到頁面(組件掛載前)的時刻執行。

//在React 16.9中componentWillMount已被棄用,改爲UNSAFE_componentWillMount,而且UNSAFE_componentWillMount也準備不用了、是過時的生命週期方法,在新代碼中最好不用。

③.render(常用) : 頁面state或props發生變化時執行(組件掛載中)。

render應爲純函數,意味着render不應該修改組件的state,每次調用時都返回相同的結果,並且render不會直接與瀏覽器交互,若

想與瀏覽器交互,應該在componentDidMount() 或其他生命週期方法中執行你的操作。保持 render() 爲純函數,可以使組件更容

易思考。

④.componentDidMount(常用) : 組件掛載完成時被執行(在組件已經被渲染到 DOM 中後運行)。

加入紅框中代碼:

效果: 

Warning: componentWillMount has been renamed, and is not recommended for use.

查閱資料,在React 16.9中componentWillMount已被棄用,改爲UNSAFE_componentWillMount,修改代碼:

效果:

生命週期函數書寫順序與執行順序無關:

這就是生命週期的順序。這三個函數書寫時是不用按順序的,你可以隨便改動他們的順序。

注意:

UNSAFE_componentWillMount和componentDidMount這兩個生命週期函數,只在頁面刷新時執行一次,而render函數只要

state和props變化就會執行,這個初學者一定要注意。

2.Updating: 組件更新階段

這是React生命週期中比較複雜的一部分,它有兩個基本部分組成,一個是props屬性改變,一個是state狀態改變(這個在生命週期的

圖片中可以清楚的看到)。

Updating:即組件發生改變時的更新階段,

當組件的 props 或 state 發生變化時會觸發更新。組件更新的生命週期調用順序如下:

注意:

下述方法即將過時,在新代碼中應該避免使用它們

 

①shouldComponentUpdate()函數:會在組件更新之前,自動被執行。比如寫入下面的代碼:

shouldComponentUpdate(){
    console.log('shouldComponentUpdate----組件發生改變(更新)前執行')
    return true
}

文本框輸入a,效果如下:

shouldComponentUpdate()函數有兩個返回值:true(默認值)、false。

返回值爲true:state每次發生變化,組件就會重新渲染,大部分情況下應該遵循默認行爲。

當props 或 state 發生變化時,shouldComponentUpdate()函數會在渲染前被調用。

首次渲染或使用orceUpdate(0時不會調用該方法。

返回值爲false:就算state每次發生變化,這組件業不會進行更新了。 簡單點說,但是返回值爲false很少用到,而且返回值爲false會導致一系列問題,這裏不做過多介紹,詳見官方文檔的解釋:

https://react.docschina.org/docs/react-component.html#shouldcomponentupdate

簡單來說,返回true,就同意組件更新;返回false,就反對組件更新。

②componentDidUpdate()函數:在組件更新之後執行,它是組件更新的最後一個環節。

componentDidUpdate(){
    console.log('componentDidUpdate----組件更新之後執行')
}

3.Unmounting---組件卸載卸載時

在子組件XiaojiejieItem中加入:

componentWillUnmount(){
    console.log('child - componentWillUnmount')
}

 點擊服務列表項,服務列表項被刪除後執行componentWillUnmount方法

18_用生命週期函數shouldComponentUpdate改善程序性能

本項目一直存在性能問題,就是子組件XiaojiejieItem頻繁且無用的渲染。

通過React Developer Tools可以清楚地看到,不過要首先進行一些設置,即打開組件渲染時高亮顯示的(Highlight updates when components render),設置步驟如下:

可以看到在父組件Xiaojieije的文本框中輸入時子組件XiaojiejieItem也在渲染:

我們也可以在XiaojiejieItem.jsrender函數里加入下面的代碼,可以更直觀的看到這個問題:

render() { 
    console.log('child--render')
    return ( 
    <li onClick={this.handleClick}>{this.props.personname}爲你服務:{this.props.content}</li>
    )
}

解決方法:

通過shouldComponentUpdate函數阻止子組件XiaojiejieItem無用渲染

shouldComponentUpdate(nextProps,nestState){
    if(nextProps.content!==this.props.content){
        return true
    }else{
        return false
    }
}

可以看到控制檯沒用在父組件發生變化時,子組件沒有重複輸出child--render,即子組件並沒有重複渲染。

這就算是完美解決了子組件的渲染性能問題,在react面試中性能優化會是區分一般程序員和有經驗程序員的標準之一。

19_axios數據請求

ajax可以遠程請求但是寫起來太麻煩了,我們用程序的ajax請求框架Axios來實現。

1.axios安裝

在項目根目錄下,輸入:

npm install -save axios

結果報錯:

npm ERR! code EPERM
npm ERR! syscall open
npm ERR! path C:\Program Files\nodejs\node_cache\_cacache\tmp\3219faae
npm ERR! errno -4048

解決方法:用管理員身份運行命令提示符(cmd)再繼續安裝axios,安裝成功:

npm install的4種常用方式的區別(附表格對比):

npm install x:

  1. 會把x模塊安裝到項目的node-modules目錄中
  2. 不會修改package.json文件(不添加依賴)

npm install x -g:

  1. 安裝模塊到全局,不會把x模塊安裝到項目的node-modules目錄中,具體安裝到磁盤哪個位置,要看 npm cinfig prefix的位置
  2. 不會修改package.json文件(不添加依賴)

npm install x -save

  1. 會把x模塊安裝到項目的node-modules目錄中
  2. 修改package.json文件的dependencies屬性寫入x模塊的依賴(添加依賴)
  3. 之後運行npm install - production或者註明NODE_ENV變量值爲production時,自動把x模塊安裝到項目的node-modules目錄中

npm install x -save-dev

  1. 會把x模塊安裝到項目的node-modules目錄中
  2. 修改package.json文件的DevDependencies屬性寫入x模塊的依賴(添加依賴)
  3. 之後運行npm install - production或者註明NODE_ENV變量值爲production時,不會自動把x模塊安裝到項目的node-modules目錄中
  npm install x npm install x -g npm install x -save npm install x -save-dev
會把x模塊安裝到項目的node-modules目錄中 Y N Y Y
是否修改package.json文件(添加依賴) N N Y,package.json文件的dependencies屬性寫入x模塊的依賴 Y,package.json文件的DevDependencies屬性寫入x模塊的依賴
之後運行npm install - production或者註明NODE_ENV變量值爲production時,是否把x模塊安裝到項目的node-modules目錄中     Y N


 

 

 

 

 

 

在package.json添加依賴的重要性:

程序開源上傳到github,自己在開發是用了npm install,沒有用npm install x -save-dev與npm install x -save-dev,即沒有添加

依賴,比人下載後項目是跑不起來的,所以在公司做實際項目時依賴是必須要寫的。

 

npm install x -save-dev與npm install x -save-dev使用場景主要區別:

npm install x -save在dependencies屬性(生產環境)添加依賴,即項目做完時要跑在服務器了,必須把 把包(模塊)進行依賴,

-save在開發模式、生產模式用都可以。

npm install x -save-dev在 DevDependencies屬性(開發環境)添加依賴,只在程序員進行測試、項目管理時使用。

2.axios請求數據

在Xiaojiejie.js頭部引入axios:

import axios from 'axios'

在生命週期函數componentDidMount請求ajax:

componentDidMount(){
    axios.post('https://web-api.juejin.im/v3/web/wbbr/bgeda')
        .then((res)=>{console.log('數據請求成功'+JSON.stringify(res))})
        .catch((error)=>{console.log('數據請求失敗'+error)})
}

建議在componentDidMount函數裏執行ajax:

①因爲在render裏執行,會出現很多問題,比如一直循環渲染。

②在componentWillMount裏執行,在使用RN時,又會有衝突。

效果:

3.axios從RAP2遠端api請求自己mock(模擬)的數據

上一小節,post用的是一個掘金的臨時api,這類api不是自己寫的api,隨時都有不能用的風險。實際開發過程中通常是前後端分離的,所以我們要自己mock(數據),這裏我們選擇用RAP2來模擬數據。

http://rap2.taobao.org/account/login

點擊新建接口後輸入接口名稱、地址,按提交就可以了。

小坑1:按複製地址的按鈕是沒用的,一定要點擊地址,在彈出頁面複製地址纔有用:

編輯請求參數和響應內容:

修改componentDidMount生命週期方法,把post改成get,並把地址改成彈出頁面複製地址:

componentDidMount(){
    axios.get('http://rap2.taobao.org:38080/app/mock/254023/Xiaojiejie')
        // .then((res)=>{console.log('數據請求成功'+JSON.stringify(res))})
        .then(
            (res)=>{
                console.log('數據請求成功'+JSON.stringify(res.data))
            }
        )
        .catch((error)=>{console.log('數據請求失敗'+error)})
}

效果如下(數據請求成功):

再修改componentDidMount生命週期方法,想讓服務列表的數據編程從api 中get過來的mock數據,加入setState方法:

componentDidMount(){
    axios.get('http://rap2.taobao.org:38080/app/mock/254023/Xiaojiejie')
        // .then((res)=>{console.log('數據請求成功'+JSON.stringify(res))})
        .then(
            (res)=>{
                console.log('數據請求成功'+JSON.stringify(res.data))
                this.setState({list:res.data})
            }
        )
        .catch((error)=>{console.log('數據請求失敗'+error)})
}

大坑1:報錯TypeError: this.state.list.map is not a function

在componentDidMount生命週期方法加上兩句console.log:

componentDidMount(){
    axios.get('http://rap2.taobao.org:38080/app/mock/254023/Xiaojiejie')
        // .then((res)=>{console.log('數據請求成功'+JSON.stringify(res))})
        .then(
            (res)=>{
                console.log('數據請求成功'+JSON.stringify(res.data))
                console.log('數據請求成功'+JSON.stringify(res.data.mockdata))
            }
        )
        .catch((error)=>{console.log('數據請求失敗'+error)})
}

效果:

實際上res.data.mockdata纔是我們想要的數組,而res.data根本不是個數組,所以之前componentDidMount中的把list設置成了res.data,而res.data不是數組,纔會報錯this.state.list.map is not a function,因爲只有array.map方法必須是“數組.map”

this.setState({list:res.data})

 

修改this.setState爲 this.setState({list:res.data.mockdata}),componentDidMount完整代碼如下:

componentDidMount(){
    axios.get('http://rap2.taobao.org:38080/app/mock/254023/Xiaojiejie')
        // .then((res)=>{console.log('數據請求成功'+JSON.stringify(res))})
        .then(
            (res)=>{
                console.log('數據請求成功'+JSON.stringify(res.data))
                console.log('數據請求成功'+JSON.stringify(res.data.mockdata))
                this.setState({list:res.data.mockdata})
            }
        )
        .catch((error)=>{console.log('數據請求失敗'+error)})
}

效果(mock數據請求成功):

此時組件列表不再是寫死的數據,而是從api接口get過來的的數據了。

20_CSS3實現react動畫

用CSS3在React中製作一個顯示/隱藏的動畫特效(注意:這是css3實現的效果,react只是做了業務邏輯)

1.創建Boss組件,代碼如下:

import React, { Component } from 'react';
class Boss extends Component {
    constructor(props) {
        super(props);
        this.state = {  }
    }
    render() { 
        return ( 
            <div>boss級人物:鎧爹</div>
            <div><button>召喚boss</button></div>
        );
    }
}
 
export default Boss;

2.編寫業務邏輯

具體要實現的效果:

點擊按鈕,“boss級人物:鎧爹”這行字出現或隱藏。

實現原理:

步驟一:

在changeToshow函數內通過setState方法修改this.state.toshow的值,進而修改鎧爹”這行文字的className屬性的值,再把changeToshow綁定在按鈕的點擊事件,這樣每次點擊按鈕就能改變一次className屬性值。

步驟二:再爲hide、show添加css即可實現“boss級人物:鎧爹”這行文字的顯示或隱藏。


步驟一:通過按鈕使“boss級人物:鎧爹”這行文字的className屬性在"hide"和"show "間切換。

boss子組件代碼如下(代碼看不懂可參考備註):

import React, { Component } from 'react';
class Boss extends Component {
    constructor(props) {
        super(props);
        this.state = { 
            toshow:true
        }
        this.changeToshow =this.changeToshow.bind(this)
    }
    render() { 
        return ( 
            <div>
                {/*this.state.toshow值爲true時,className爲show意思是顯示div內的文字;反之隱藏。  */}
                <div className={this.state.toshow?'show':'hide'}>boss級人物:鎧爹</div>
                <div>
                    <button onClick={this.changeToshow}>召喚boss</button>
                </div>
            </div>   
        );
    }
    // changeToshow用於切換this.state.toshow的值,爲true時切換爲false,爲false時切換爲true
    changeToshow(){
        this.setState({
            toshow:this.state.toshow?false:true
        })
    } 
}
 
export default Boss;

並在Xiaojiejie.js中引入Boss子組件:

import Boss from './Boss'

在Xiaojiejie.js的render最下方,加入Boss子組件:

<Boss />

點擊“召喚boss”按鈕效果如下(每次按按鈕className都會在show和hide間切換):

步驟二:在style.css爲hide、show添加css

.show {
    opacity:1;
    transition:all 1.5s ;
}
.hide {
    opacity:0;
    transition:all 1.5s ;
}

效果:

注:

CSS3 opacity 屬性

CSS3 transition 屬性

21_CSS3的keyframes(關鍵幀)動畫

keyframes比transition的優勢在於可以更細化動畫效果,以上一節的點擊按鈕隱藏、顯示動畫爲例,不僅可以設置透明度,還能設置屬性

他和transition比的優勢是它可以更加細化的定義動畫效果。比如我們設置上節課的按鈕隱藏動畫,不僅可以設置透明度,還可以設置顏色。

注:

CSS3 opacity 屬性

CSS3 transition 屬性

CSS3 animation(動畫) 屬性

CSS3 @keyframes 規則

forwards屬性

注意: 使用animation屬性來控制動畫的外觀,還使用選擇器綁定動畫。keyframes(關鍵幀)要和animation屬性搭配使用。


在style.css中加入如下代碼:

.show {
    animation:show-item 2s ease-in ;
}

.hide {
    animation:hide-item 2s ease-in ; 
}

@keyframes show-item {
    0%{
        opacity: 0;
        color: #7B68EE;
    }
    50%{
        opacity: 0.5;
        color: #FFD700;
    }
    100%{
        opacity: 1;
        color: #FF4500;
    }
}

@keyframes hide-item {
    0%{
        opacity: 1;
        color: #00FF00;
    }
    50%{
        opacity: 0.5;
        color: blueviolet;
    }
    100%{
        opacity: 0;
        color: cadetblue;
    }
}

效果(className爲hide時,鎧爹卻無法隱藏):

這是因爲沒爲設置forwards屬性,它是用來控制停止到最後一幀的。爲show、hide加上forwards屬性:

.show {
    animation:show-item 2s ease-in forwards;
}
.hide {
    animation:hide-item 2s ease-in forwards; 
}

效果(35_爲show、hide加上forwards屬性後鎧爹隱藏成功):

總結:keyframes也只是能實現一些簡單的動畫效果,一些複雜的動畫最好還是使用別人造好的輪子。

22_react-transition-group動畫組件

1.react-transition-group的優勢

React有非常好的開發環境,任何開發需要的基本需求都可以找到官方或大神造的輪子,動畫也不例外。

react-transition-group動畫組件表現很好,可以滿足日常動畫開發需求,而且是React官方提供的動畫過渡庫,有完整的API文檔。

注:

react-transition-group官方文檔

https://reactcommunity.org/react-transition-group/

react-transition-group主要有四個核心庫:


2.安裝react-transition-group

切換終端爲cmd後輸入以下代碼進行安裝(注:):

npm install react-transition-group --save

一開始安裝是報錯的,然後關閉vscode,之後以管理員身份運行vscode後,安裝包成功:


3.使用CSSTransition

引入CSSTransition:

import {CSSTransition} from 'react-transition-group'

使用的方法就和使用自定義組件一樣,直接寫<CSSTransition>,而且className屬性在CSSTransition相對應的是classNames(記得加s)屬性。修改上節寫的Boss.js文件裏的render區域:

render() { 
    return ( 
        <div>
            {/*this.state.toshow值爲true時,className爲show意思是顯示div內的文字;反之隱藏。  */}
            {/* <div className={this.state.toshow?'show':'hide'}>boss級人物:鎧爹</div> */}
            <CSSTransition 
                in={this.state.toshow}
                timeout={2000}
                classNames={boss-text}
            >
                <div>boss級人物:鎧爹</div>
            </CSSTransition>
            <div>
                <button onClick={this.changeToshow}>召喚boss</button>
            </div>
        </div>   
    );
}

注:

<CSSTransition>標籤的屬性:

in:in值的變化決定了動畫是enter還是exit,in值的變化是觸發動畫的扳機。

timeout:動畫持續時間(ms)

classNames:css類名


修改style.css:

.boss-text-enter {
    opacity: 0;
}
.boss-text-enter-active {
    opacity: 1;
    transition: opacity 2000ms;
}
.boss-text-enter-done {
    opacity: 1;
}
.boss-text-exit {
    opacity: 1;
}
.boss-text-exit-active {
    opacity: 0;
    transition: opacitty 2000ms;
}
.boss-text-exit-done {
    opacity: 0;
}

報錯:

原因是classNames={boss-text}不應該是{},而是引號

修改代碼:

<CSSTransition 
    in={this.state.toshow}
    timeout={2000}
    classNames='boss-text'
>

效果:

注:

xxx-enter: 進入(入場)前的CSS樣式;

xxx-enter-active:進入動畫直到完成時之前的CSS樣式;

xxx-enter-done:進入完成時保留的CSS樣式;

xxx-exit:退出(出場)前的CSS樣式;

xxx-exit-active:退出動畫知道完成時之前的的CSS樣式。

xxx-exit-done:退出完成時保留的CSS樣式。

CSS3 opacity 屬性

CSS3 transition-property屬性


unmountOnExit屬性:在元素退場時,自動把DOM也刪除,這是以前用CSS動畫沒辦法做到的。

元素退場時,把  <div>boss級人物:鎧爹</div> 也刪除了


4.使用transitionGrop

需求:爲服務列表添加動畫

實現:

在Xiaojiejie.js的頭部引入transitionGrop:

import {TransitionGroup,CSStransition} from 'react-transition-group'

修改Xiaojiejie.js,爲ul內,最外層加入TransitionGroup標籤,爲子組件XiaojiejieItem外層包裹CSSTransition標籤,代碼如下:

<ul ref={(ul)=>{this.ul=ul}}>
    <TransitionGroup>
        { 
            this.state.list.map((item,index)=>{
                return(                           
                    <CSSTransition
                        // in={this.state.toshow}
                        timeout={2000}
                        classNames='boss-text'
                        unmountOnExit
                        appear={true}   
                        key={item+index}
                    >
                        <XiaojiejieItem 
                        content={item} 
                        index={index}
                        key={item+index}
                        deleteItem={this.deleteItem.bind(this)}
                    </CSSTransition>
                )   
            })
        } 
    </TransitionGroup>
</ul>

CSSTransition的appear屬性:

appear={true},意思是想讓組件出現時就有動畫效果。

實現效果(服務列表添加動畫成功):

 

 

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