React高级内容

知识点
8、React的生命周期函数
9、React生命周期函数的使用场景
10、使用Charles实现本地数据
11、React中实现CSS过渡动画
12、React中使用CSS动画效果
13、使用react-transition-group实现动画
14、react-transition-group的使用

8、React的生命周期函数

定义:生命周期函数指在某一个时刻组件会自动调用执行的函数。
生命周期函数:(如下图)
1)初始化Initialization:初始化数据props and state。
2)挂载组件Mounting:componentWillMount()在组件即将被挂载到页面的时刻,自动执行,只会在第一次挂载执行; render()执行组件挂载到页面;componentDidMount()在组件被挂在到页面之后,自动执行,只会在第一次挂载执行。
3)组件更新Updation:
props发生变化:componentWillReceiveProps()当一个组件从父组件接受参数props,只要父组件的render函数被重新执行了,子组件的这个生命周期函数就会被自动执行(换句话说,如果这个组件第一次存在于父组件中,不会被执行,如果这个组件之前已经存在于父组件中,才会执行);之后内容跟states的一样。
states发生变化:shouldComponentUpdate()组件被渲染一次之后,需要进行更新之前,会被自动执行,必须返回一个Boolean值(返回true,组件需要被更新;返回false,组件不需要被更新,不会执行后续周期函数);componentWillUpdate()在组件被更新之前,会被自动执行,但是它在shouldComponent() 之后被执行,如果shouldComponent() 返回true才被执行,如果返回false,则不会被执行;render()重新进行页面更新;componentDidUpdate()组件更新完成之后,它会被执行。
4)卸载组件Unmounting:componentWillUnmount()当这个组件即将被从页面中剔除的时候,自动执行。

在这里插入图片描述
9、React生命周期函数的使用场景

1)shouldComponentUpdate()使用,提升组件性能
在TodoList组件中,每次输入内容,由于更改了state.inputValue的值,使得render()会进行重新渲染,父组件重新渲染,子组件TodoItem也进行重新渲染。但是在没有点击提交前,子组件TodoItem的内容并不需要更新。因此,会造成性能上的浪费。优化性能的方法,使用shouldComponentUpdate()来判断是否需要更新。shouldComponentUpdate()在子组件被渲染一次之后,如果需要更新,则通过返回true/false来判断是否更新。
回顾性能优化知识点:a. 在constructor函数中this.handleClick = this.handleClick.bind(this);改变作用域,保证函数作用域绑定操作只执行一次,而且避免子组件的无谓的渲染;b. setState()是异步函数,底层内置了许多性能机制,可以把多次的数据修改合并成一次修改,降低虚拟DOM的比对次数;c. react底层使用了虚拟DOM ,还有同层比对、配值等概念,从而提升react性能;d. shouldComponentUpdate()使用

//此组件被渲染一次之后,如果需要更新,强制要求不更新
	//nextProps表示接下来props变成什么样
	//nextState表示接下来props变成什么样
	shouldComponentUpdate(nextProps, nextState) {
		if (nextProps.content !== this.props.content) {
			return true;
		} else {
			return false;
		}

	}

2)componentDidMount()的使用
假设需要向后台发起ajax请求数据,需要写在哪个生命周期函数比较合适?a.写在render()不好的。因为容易出现死循环,每次输入数据,就会重新执行render(),则重新发起一次数据请求,浪费资源;b. 写在componentDidMount()函数里。因为componentDidMount()只被执行一次,在组件被挂载到页面时被执行一次。为什么不写在componentWillMount()函数,它也只被执行一次,所以把ajax数据请求写在这里也是可以的,但是在使用React Native或者使用react做服务器端的同构等更深的技术,容易产生技术冲突。所以写在componentDidMount()函数最为合理。
安装axios模块:打开cmd,输入执行以下命令后,完成安装

cd <项目文件夹路径>
npm install axios --save

TodoList.js(添加新内容)

...
import axios from 'axios';
...
componentDidMount() {
		axios.get('/api/todolist')
			.then(() => {
				alert('succ')
			})
			.catch(() => {
				alert('error')
			});
	}
...

10、使用Charles实现本地数据mock

由于在实际开发中,一般进行前后端分离,通过接口来进行数据交互。所以我们使用charles-proxy来进行模拟数据接口。Charles能够抓取浏览器发起的请求,然后做出一些处理和返回。
1)安装charles和注册
charles下载:https://www.charlesproxy.com/latest-release/download.do
一直下一步下一步进行安装。

注册账号(不注册的话,30天会过期,每30s会关闭一次)

Registered Name:https://zhile.io

License Key: 48891cf209c6d32bf4

2)配置接口
a. 创建一个json文件,命名为TodoList(名字可自行更改)
TodoList.json

{
	"data": [1, 2, 3]
}

b. 打开charles,点击 Tools -> Map Local …,勾选Enable Map Local,点击Add按钮,按下图填写,其中Local Path选择刚才创建好的json文件。

在这里插入图片描述
出错点:

Host选项如果只写localhost,会出现浏览器访问时出现404,经过查看发现charles能抓取线上的包,但是不能抓取本地的包,那么把我们想要访问的接口挂到localhost.charlesproxy.com:3000域名下,就能访问了。
c. 修改TodoList.js文件代码的访问地址

...
componentDidMount() {
		axios.get('http://localhost.charlesproxy.com:3000/api/todolist')
			.then(() => {
				alert('succ')
			})
			.catch(() => {
				alert('error')
			});
	}
...

出错点:

  虽然ajax访问接口能正常的获取到数据,并成功得到202,但是返回结果仍为error,查看以下错误Access to XMLHttpRequest at 'http://localhost.charlesproxy.com:3000/api/todolist' from origin 'http://localhost:3000' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.,发现出现跨域问题。

在这里插入图片描述
charles如何配置跨域?
查看跨域资源共享 CORS 详解 https://blog.csdn.net/xxm0720/article/details/79795715
介绍:OPTIONS请求是非简单请求的一种处理方式。在真正发送请求之前,增加一次HTTP查询请求,称为"预检"请求(preflight),就是我们刚刚说到的参数为OPTIONS的第一次请求,它的作用是:询问服务器,当前网页所在的域名是否在服务器的许可名单之中,以及可以使用哪些HTTP请求和头信息字段。只有得到肯定答复,浏览器才会发出正式的HttpRequest(GET、POST等)第二次请求;否则就报错,也不会进行第二次请求。
解决方法:使用charles的rewrite修改options请求的response header
a. 点击Tools --> Rewrite;
b. 添加rewrite配置
在这里插入图片描述
c. 修改header
修改四个字段,具体字段内容如下:

Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: GET,POST,OPTIONS
Access-Control-Allow-Headers: Accept,Origin,X-Requested-With,Content-Type,Last-Modified
Allow: GET, HEAD, POST, PUT, DELETE, TRACE, OPTIONS, PATCH

在这里插入图片描述
d. 修改status code
在这里插入图片描述
e. 重新查看页面,可以发现能够成功返回”succ"。

3)动态加载数据显示到页面
加载获取到数据后,修改state数据,达到动态修改数据。查看页面,能够进行正常的增删操作。
TodoList.js(添加新内容)

...
import axios from 'axios';
...
	componentDidMount() {
		axios.get('http://localhost.charlesproxy.com:3000/api/todolist')
			.then((res) => {
				console.log(res.data);
				this.setState(() => ({
					list: [...res.data]
				}));
			})
			.catch((e) => {
				console.log(e);
			});
	}
...

11、React中CSS过渡动画

实现react最基础的动画样式

CssTransition1.js(新建js文件)

import React, {
	Component,
	Fragment
} from 'react';
import './css/CssTransition1.css';

class CssTransition1 extends Component {
	constructor(props) {
		super(props);
		this.handleToggle = this.handleToggle.bind(this);
		this.state = {
			show: true
		}
	}

	render() {
		return (
			<Fragment>
				<div className={this.state.show?'show':'hide'}>hello</div> 
				<button onClick={this.handleToggle}>toggle</button>
			</Fragment>
		);
	}

	handleToggle() {
		this.setState({
			show: this.state.show ? false : true
		});
	}
}

export default CssTransition1;

CssTransition1.css(新建css文件)

.hide{
	opacity: 0;
	transition: all 1s ease-in;
}
.show{
	opacity: 1;
	transition: all 1s ease-in;
}

index.js(修改js文件)

...
// import App from './App';
// import TodoList from './TodoList';
import CssTransition1 from './CssTransition1';
...
// ReactDOM.render(<App />, document.getElementById('root'));
// ReactDOM.render(<TodoList />, document.getElementById('root'));
ReactDOM.render(<CssTransition1 />, document.getElementById('root'));
...

12、React中使用CSS动画效果

CssTransition1.css(修改css文件)

.show{
	font-size: 30px;
	font-weight: 700;
	opacity: 1;
	/*transition: all 1s ease-in;*/
	/*forwards在动画结束的最后一帧,保存动画的css样式*/
	animation: show-item 2s ease-in forwards; 
}
.hide{
	font-size: 30px;
	font-weight: 700;
	opacity: 0;
	/*transition: all 1s ease-in;*/
	/*forwards在动画结束的最后一帧,保存动画的css样式*/
	animation: hide-item 2s ease-in forwards; 
}

@keyframes show-item{
	0%{
		opacity: 0;
		color: red;
	}
	50%{
		opacity: 0.5;
		color: green;
	}
	100%{
		opacity:1;
		color: blue;
	}
}
@keyframes hide-item{
	0%{
		opacity: 1;
		color: red;
	}
	50%{
		opacity: 0.5;
		color: green;
	}
	100%{
		opacity: 0;
		color: blue;
	}
}

12、使用react-transition-group实现动画

react第三方模块react-transition-group可以实现更复杂的动画效果。
具体可查看官方文档 http://reactcommunity.org/react-transition-group/
1)安装第三方模块react-transition-group
打开cmd,输入以下命令

cd <项目文件夹路径>  //进入项目文件夹目录下
npm install react-transition-group --save

2)CSSTransition使用

CSSTransition标签的in属性值为{this.state.show},表示何时要执行动画,无非就是show的值发生改变的时候。 CSSTransition标签的timeout属性,表示动画要执行多久,单位为毫秒; CSSTransition标签的classNames属性,表示一系列class样式的前缀名; CSSTransition标签的unmountOnExit属性,表示隐藏节点时,不占用文档流空间; CSSTransition标签的onEntered钩子,表示在入场结束后能自动执行的函数,可接受参数el,表示内部的节点内容; CSSTransition标签的appear属性值为true,表示当第一次展示在页面上时,也要展示动画效果; 更多属性用法,请查看 http://reactcommunity.org/react-transition-group/css-transition。 如果想实现更多的动画效果,可以使用Transition,具体查看http://reactcommunity.org/react-transition-group/transition

CssTransition2.js(新建js文件)

import React, {
	Component,
	Fragment
} from 'react';
import {
	CSSTransition
} from 'react-transition-group';
import './css/CssTransition2.css';

class CssTransition2 extends Component {
	constructor(props) {
		super(props);
		this.handleToggle = this.handleToggle.bind(this);
		this.state = {
			show: true
		}
	}

	render() {
		return (
			<Fragment>
				<CSSTransition 
					in={this.state.show}
					timeout={1000}
					classNames="fade"
					unmountOnExit
					onEntered={(el)=>{el.style.color="blue";}}
					appear={true}
				>
					<div>hello</div> 
				</CSSTransition>
				<button onClick={this.handleToggle}>toggle</button>
			</Fragment>
		);
	}

	handleToggle() {
		this.setState({
			show: this.state.show ? false : true
		});
	}
}

export default CssTransition2;

CssTransition2.css(新建css文件)

/*className可以使用fade前缀*/
/*fade-enter:入场动画的第一帧,准备入场*/
/*fade-appear:将其挂载到页面的入场动画的第一帧,准备入场*/
.fade-enter,.fade-appear {
	opacity: 0
}
/*fade-enter-active:开始入场到入场结束前的动画*/
/*fade-appear-active:将其挂载到页面的第一次开始入场到入场结束前的动画*/
.fade-enter-active,.fade-appear-active {
	opacity: 1;
	transition: opacity 1s ease-in;
}
/*入场结束后的样式*/
.fade-enter-done {
	opacity: 1;
}

/*出场动画执行前*/
 .fade-exit{
 	opacity: 1;
 }
 /*出场动画执行时*/
 .fade-exit-active{
 	opacity: 0;
	transition: opacity 1s ease-in;
 }
 /*出场动画结束后*/
 .fade-exit-done{
	opacity: 0;
 }

14、react-transition-group的使用

如果想要实现多个元素的动画效果,该如何做?
使用TransitionGroup配合CSSTransition来使用,TransitionGroup写在所有需要添加动画的组件外部,CSSTransition写在一个具体的需要动画的组件外部。
CssTransition3.js(新建js文件)

import React, {
	Component,
	Fragment
} from 'react';
import {
	CSSTransition,
	TransitionGroup
} from 'react-transition-group';
import './css/CssTransition3.css';

class CssTransition3 extends Component {
	constructor(props) {
		super(props);
		this.handleAddItem = this.handleAddItem.bind(this);
		this.state = {
			list: []
		}
	}

	render() {
		return (
			<Fragment>
				<TransitionGroup>
					{
						this.state.list.map((item,index)=>{
							return (
								<CSSTransition 
									timeout={1000}
									classNames="fade"
									unmountOnExit
									onEntered={(el)=>{el.style.color="blue";}}
									appear={true}
									key={index}
								>
									<div>item</div>
								</CSSTransition>
							)
						})
					}
				</TransitionGroup>
				<button onClick={this.handleAddItem}>add</button>
			</Fragment>
		);
	}

	handleAddItem() {
		this.setState((prevState) => {
			return {
				list: [...prevState.list, 'item']
			}
		});
	}
}

export default CssTransition3;

CssTransition3.css(新建css文件)

/*className可以使用fade前缀*/
/*fade-enter:入场动画的第一帧,准备入场*/
/*fade-appear:将其挂载到页面的入场动画的第一帧,准备入场*/
.fade-enter,.fade-appear {
	opacity: 0
}
/*fade-enter-active:开始入场到入场结束前的动画*/
/*fade-appear-active:将其挂载到页面的第一次开始入场到入场结束前的动画*/
.fade-enter-active,.fade-appear-active {
	opacity: 1;
	transition: opacity 1s ease-in;
}
/*入场结束后的样式*/
.fade-enter-done {
	opacity: 1;
}

/*出场动画执行前*/
 .fade-exit{
 	opacity: 1;
 }
 /*出场动画执行时*/
 .fade-exit-active{
 	opacity: 0;
	transition: opacity 1s ease-in;
 }
 /*出场动画结束后*/
 .fade-exit-done{
	opacity: 0;
 }

TodoList.js(代码记录)

import React, { Component, Fragment } from 'react';
import TodoItem  from './TodoItem'
import axios from 'axios'
import './style.css'

class TodoList extends Component {
	constructor (props){
		super(props);
		this.state = {
			inputValue : '',
			list: []
		}
		this.handleInputChange = this.handleInputChange.bind(this)
		this.handleBtnClick = this.handleBtnClick.bind(this)
		this.handleItemDelete = this.handleItemDelete.bind(this)

	}

	render() {
		return (
			<Fragment>
				<div>
					<label htmlFor='insertArea'>输入内容</label>
					<input 
					id='insertArea'
					className='input'
					value={this.state.inputValue}
					onChange={this.handleInputChange}
					ref={(input) => {this.input = input}}
					/> 
					<button onClick={this.handleBtnClick}>提交</button>
				</div>
				<ul>
					{ this.getTodoItem() }
				</ul>
			</Fragment>
		)
	}


	componentDidMount(){
		axios.get('http://localhost.charlesproxy.com:3000/api/todolist')
		.then( (res) => {
			console.log(res.data)
			this.setState(()=>({
				list:[...res.data]
			}))
		})
		.catch( (e) => {
			console.log(e)
		})
	}

	getTodoItem(){

		return this.state.list.map((item,index) => {
			return (
				<TodoItem 
				key={index}
				content={item} 
				index={index} 
				deleteItem={this.handleItemDelete}
				/>
				)
			})
	}

	handleInputChange(e){
		const value = e.target.value
		this.setState(() => ({
			inputValue:value
		}))
	}

	handleBtnClick(){
		this.setState((prevState) => ({
			list:[...prevState.list,prevState.inputValue],
			inputValue:''
		}))

	}

	handleItemDelete(index){
		this.setState((prevState) => {
			const list = [...prevState.list];
			list.splice(index,1)
			return {list}
		})
	}
}

export default TodoList;


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值