上接
Easy的React學習筆記(一.基礎):
https://blog.csdn.net/qq_37279880/article/details/102487420
目錄
15_使用PropTypes校驗傳遞值的類型(Typechecking With PropTypes)
16_ref屬性提高代碼語義化、setState的回調函數與console.log
1.Mounting: 組件掛載階段(組件掛載即組件插入DOM樹)
18_用生命週期函數shouldComponentUpdate改善程序性能
解決方法:用管理員身份運行命令提示符(cmd)再繼續安裝axios,安裝成功:
15_使用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都可以,
關鍵是校驗組件.propTypes的propTypes是唯一寫法。
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 發生變化時會觸發更新。組件更新的生命週期調用順序如下:
static getDerivedStateFromProps()
shouldComponentUpdate()
render()
getSnapshotBeforeUpdate()
componentDidUpdate()
注意:
下述方法即將過時,在新代碼中應該避免使用它們:
①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.js
的render
函數里加入下面的代碼,可以更直觀的看到這個問題:
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:
- 會把x模塊安裝到項目的node-modules目錄中
- 不會修改package.json文件(不添加依賴)
npm install x -g:
- 安裝模塊到全局,不會把x模塊安裝到項目的node-modules目錄中,具體安裝到磁盤哪個位置,要看 npm cinfig prefix的位置
- 不會修改package.json文件(不添加依賴)
npm install x -save
- 會把x模塊安裝到項目的node-modules目錄中
- 修改package.json文件的dependencies屬性寫入x模塊的依賴(添加依賴)
- 之後運行npm install - production或者註明NODE_ENV變量值爲production時,會自動把x模塊安裝到項目的node-modules目錄中
npm install x -save-dev
- 會把x模塊安裝到項目的node-modules目錄中
- 修改package.json文件的DevDependencies屬性寫入x模塊的依賴(添加依賴)
- 之後運行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 ;
}
效果:
注:
21_CSS3的keyframes(關鍵幀)動畫
keyframes比transition的優勢在於可以更細化動畫效果,以上一節的點擊按鈕隱藏、顯示動畫爲例,不僅可以設置透明度,還能設置屬性
他和transition
比的優勢是它可以更加細化的定義動畫效果。比如我們設置上節課的按鈕隱藏動畫,不僅可以設置透明度,還可以設置顏色。
注:
注意: 使用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樣式。
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},意思是想讓組件出現時就有動畫效果。
實現效果(服務列表添加動畫成功):