React高级
1.PropTypes校验传递值
在父组件向子组件传递数据时,使用了属性的方式,也就是props
。但是在实际开发中,需要对这些传递过去的值进行校验,包括非空,格式的验证。
我们在Xiaojiejie.js
组件里传递了4个值,有字符串,有数字,有方法,这些都是可以使用PropTypes
限制的。在使用需要先引入PropTypes:
import PropTypes from 'prop-types'
引入后,就可以在组件的下方进行引用了,需要注意的是代码要写在子组件的最下面(不是类里边):
class XiaojiejieItem extends Component {
...
}
//进行数据的校验
XiaojiejieItem.propTypes= {
avname:PropTypes.string.isRequired, //isRequired表示该属性必须传值
content:PropTypes.string, //内容必须为字符串类型
index:PropTypes.number, //索引必须是数字类型
deleteItem:PropTypes.func //删除元素必须是一个方法
}
必传值的校验
又例如现在格式改为:
render() {
return (
<div onClick={this.handleClick}>
{this.props.name}为你做- {this.props.content}
</div>
);
}
加了一个{this.props.name}
,但是这个值传不传其实无所谓,不传的话也不会报错。该如何去规定这个值是必传的呢?需要使用isRequired
关键字了,它表示必须进行传递,如果不传递就报错。在校验代码中加入下面这条语句:
name:PropTypes.string.isRequired
使用默认值defaultProps
defalutProps
就可以实现默认值的功能,比如现在把name
的默认值设置成"小明" ,然后把name
的属性删除掉:
XiaojiejieItem.defaultProps= { //defaultProps表示属性的默认值
name:'小明'
}
2.ref的使用方法
例如下面这段代码:
inputChange(e){
this.setState({
inputValue:e.target.value
})
}
e.target.value
显得不够语义化,这个时候就可以使用ref
来改写。如果要使用ref
来进行,需要现在JSX
中进行绑定, 绑定时最好使用ES6语法中的箭头函数,这样可以简洁明了绑定的DOM元素:
<input
id="jspang"
className="input"
value={this.state.inputValue}
onChange={this.inputChange.bind(this)}
ref={(input)=>{this.input=input}}
/>
绑定完了之后就可以写为:
inputChange(){
this.setState({
inputValue:this.input.value //这里的this.input指的就是文本框
})
}
ref的坑
比如现在我们要用ref
绑定取得要服务的数量,可以先用ref
进行绑定:
<ul ref={(ul)=>{this.ul=ul}}>
{
this.state.list.map((item,index)=>{
return (
<XiaojiejieItem
key={index+item}
content={item}
index={index}
deleteItem={this.deleteItem.bind(this)}
/>
)
})
}
</ul>
绑定后可以在addList()
方法中,获取当前有几个<div>
(有几行数据)的值.
addList(){
this.setState({
list:[...this.state.list,this.state.inputValue],
inputValue:''
})
console.log(this.ul.querySelectorAll('div').length)
}
经过测试发现能输出但是数量不正确,(就是这个坑),其实这个坑是因为React中更多
setState
是一个异步函数所造成的。也就是这个setState
,代码执行是有一个时间的,简单的说,就是因为是异步,还没等虚拟Dom渲染,我们的console.log
就已经执行了。
改进方法就是将语句放在setState
的回调函数中,如下所示:
addList(){
this.setState({
list:[...this.state.list,this.state.inputValue],
inputValue:''
},()=>{
console.log(this.ul.querySelectorAll('div').length)
})
}
3.React生命周期
React生命周期的四个大阶段:
Initialization
: 初始化阶段。Mounting
: 挂载阶段。Updation
: 更新阶段。Unmounting
: 销毁阶段。
生命周期函数就是在某一个时刻组件会自动调用执行的函数。就好比人的一生一定会有出生,成人,衰老,死亡这几个阶段,它们是无法避免的,到了一定的年龄就自然会经历。
之前写的Xiaojiejie.js
里边的render()
函数,就是一个生命周期函数,它在state
发生改变时自动执行。这就是一个标准的自动执行函数。但是constructor
我们叫构造函数,它是ES6的基本语法。虽然它和生命周期函数的性质一样,但不能认为是生命周期函数。但是可以把它当成一个生命周期函数,把它看成React的Initialization
阶段,定义属性(props)和状态数据(state)。
1.Initialization
:初始化阶段。
2.Mounting
挂载阶段
Mounting
阶段叫挂载阶段,伴随着整个虚拟DOM的生成,它里边有三个小的生命周期函数,分别是:
componentWillMount
: 在组件即将被挂载到页面的时刻执行。render
: 页面state
或props
发生变化时执行。componentDidMount
: 组件挂载完成时被执行。
componentWillMount
:
componentWillMount(){
console.log('componentWillMount----组件将要挂载到页面的时刻')
}
componentDidMount
:
componentDidMount(){
console.log('componentDidMount----组件挂载完成的时刻执行')
}
render
:
render(){
console.log('render---组件挂载中.......')
}
componentWillMount
和componentDidMount
这两个生命周期函数,只在页面刷新时执行一次,而render
函数是只要有state
和props
变化就会执行。
3.Updation
更新阶段。
shouldComponentUpdate:
shouldComponentUpdate
函数会在组件更新之前,自动被执行。它要求返回一个布尔类型的结果,必须有返回值,这里就直接返回一个true
了(真实开发中,这个是有大作用的)。
shouldComponentUpdate(){
console.log('shouldComponentUpdate---组件发生改变前执行')
return true
}
返回结果是每次文本框发生改变时都会随着改变。如果你返回了false
,这组件就不会进行更新了。 简单点说,就是返回true,就同意组件更新;返回false,就反对组件更新。
componentWillUpdate:
componentWillUpdate
在组件更新之前,但在shouldComponenUpdate
之后被执行。但是如果shouldComponentUpdate返回false,这个函数就不会被执行了。
//shouldComponentUpdate返回true才会被执行。
componentWillUpdate(){
console.log('componentWillUpdate---组件更新前,shouldComponentUpdate函数之后执行')
}
componentDidUpdate:
componentDidUpdate
在组件更新之后执行,它是组件更新的最后一个环节。
componentDidUpdate(){
console.log('componentDidUpdate----组件更新之后执行')
}
componentWillReceiveProps:
子组件接收到父组件传递过来的参数,父组件render函数重新被执行,这个生命周期就会被执行。凡是组件都有生命周期函数,所以子组件也是有的,并且子组件接收了props
,这时候函数就可以被执行了。要注意:
- 这个组件第一次存在于Dom中,函数是不会被执行的;
- 如果已经存在于Dom中,函数才会被执行。
4.Unmounting
销毁阶段
componentWillUnmount:
在XiaojiejieItem.js
,写入:
//当组件从页面中删除的时候执行
componentWillUnmount(){
console.log('child - componentWillUnmount')
}
当点击一行数据删除时就会执行这个函数。
接下去就是用React生命周期函数来对程序代码进行性能优化:
代码中有一个地方存在性能消耗,那就是在文本框输入的时候,每输入一个字母或者汉字,文本框会重新渲染,下面的列表数据也会渲染但其实下面的根本不需要渲染,只需要点击增加服务按钮时重新渲染即可。
如果你看不出来,在XiaojiejieItem.js
中render
函数改写成:
render() {
console.log('child-render')
return (
<div onClick={this.handleClick}>
{this.props.avname}为你做- {this.props.content}
</div>
);
}
你会发现在你输入内容的时候,控制台会频繁地输出
child-render
,但是列表数据并没有变化。
改进方法就是:
直接在XiaojiejieItem.js
中加入下面的代码:
shouldComponentUpdate(){
return false;
}
意思就是不允许子组件进行更新渲染。这时候在浏览器中查看,问题已经没有了。但是这样做太暴力了,直接否定了所有的东西,那如果在真实项目中,需要改变值属性值,达到渲染就没办法了。所以这种写法不恰当。
shouldComponentUpdate
有两个参数:
nextProps
:变化后的属性;
nextState
:变化后的状态;
shouldComponentUpdate(nextProps,nextState){
if(nextProps.content !== this.props.content){ 当子组件内容发生变化时才重新进行渲染
return true
}else{
return false
}
}
4.Axios
1.React用Axios请求远程数据
ajax
可以远程请求,但是这写起来太麻烦了,我们用程序的ajax请求框架Axios
来实现。首先安装Axios
:
npm install -save axios
关于npm
下载的几个知识点:
-
npm install xxx
: 安装项目到项目目录下,不会将模块依赖写入devDependencies
或dependencies
。 -
npm install -g xxx
:-g
的意思是将模块安装到全局,具体安装到磁盘哪个位置,要看npm cinfig prefix
的位置 -
npm install -save xxx
:-save
的意思是将模块安装到项目目录下,并在package
文件的dependencies
节点写入依赖。 -
npm install -save-dev xxx
:-save-dev
的意思是将模块安装到项目目录下,并在package
文件的devDependencies
节点写入依赖。
安装好axiso之后,需要在使用ajax的地方先引入axios,比如现在想在Xiaojiejie.js
中使用axios
,写入下面的代码进行引入:
import axios from 'axios'
引入后,可以在componentDidMount
生命周期函数里请求ajax
,我也建议在componentDidMount
函数里执行,因为在render
里执行,会出现很多问题,比如一直循环渲染;在componentWillMount
里执行,在使用React Native
时,又会有冲突。所以强烈建议在componentDidMount
函数里作ajax
请求。
componentDidMount(){
axios.post('https://web-api.juejin.im/v3/web/wbbr/bgeda')
.then((res)=>{console.log('axios 获取数据成功:'+JSON.stringify(res)) })
.catch((error)=>{console.log('axios 获取数据失败'+error)})
}
2.Axios请求EasyMock数据
选择使用Easy-mock
来模拟接口数据。
EasyMock网站:EasyMock
{
"data": ['基础按摩', '躺式采耳', '中药泡脚']
}
接着:
componentDidMount(){
axios.get('xxxx')
.then((res)=>{
console.log('axios 获取数据成功:'+JSON.stringify(res))
this.setState({
list:res.data.data
})
})
.catch((error)=>{console.log('axios 获取数据失败'+error)})
}
5.CSS3实现React动画
用CSS3
在React
中制作一个显示隐藏的动画特效,注意这是用CSS3
实现的,其实React
只做了业务逻辑。
首先在src
文件夹下,新建一个Boss.js
文件。然后用快速生成的方式生成基本结构:
import React, { Component } from 'react';
class Boss extends Component {
constructor(props) {
super(props);
this.state = { }
}
render() {
return (
<div>
<div>BOSS级人物-孙悟空</div>
<div><button>召唤Boss</button></div>
</div>
);
}
}
export default Boss;
界面UI设计好之后开始编写业务逻辑:
先在constructor
里增加state
值isShow
:
this.state = {
isShow:true
}
接着将页面上的元素进行数据绑定:
<div className={this.state.isShow ? 'show' : 'hide'}>BOSS级人物-孙悟空</div>
需要点击按钮时,有响应的事件,所以需要一个方法,我们编写一个toToggole()
方法:
toToggole(){
this.setState({
isShow:this.state.isShow ? false : true
})
}
最后给按钮上绑定点击事件:
<div><button onClick={this.toToggole}>召唤Boss</button></div>
注意这里需要到
constructor
里绑定一下this
:
constructor(props) {
super(props);
this.state = {
isShow:true
}
this.toToggole = this.toToggole.bind(this);
}
还可以加入动画效果:
style.css
:
.show{ opacity: 1; transition:all 1.5s ease-in;}
.hide{opacity: 0; transition:all 1.5s ease-in;}