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},意思是想让组件出现时就有动画效果。

实现效果(服务列表添加动画成功):

 

 

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