类组件
原理#
组件就是封装了html、css、js 及其其他多媒体资源的集合,做到自给自足,按数据而动
类组件,被react发现后,new出来一个实例对象,这个实力对象中挂载了React
提供的一些属性和你自定义的一些挂载到原型链上的方法,
并且在render()
方法中,返回类似html的标签,这便是虚拟dom了
类组件的核心是通过继承React.Component
,得到组件的能力
需要提前复习的是,this永远指向调用它的那个实例
render
里的this
指向类的实例对象
render()
中用到的数据this.state
,恰好是这个类组件实例的构造器constructor()
中放入实例中的,后边你会看到它也可以以自定义类属性的形式放到类的实例上,(这里区别与函数组件,函数组件上是没有this
的)
所以可以在render()
中,通过解构赋值的方式拿到具体的某个值,
render
是被React
调用的,自定义的函数被事件绑定了,但是不是React
调用的
class Weather extends React.Component{
constructor(props){
super(props)
this.state= {isHot:false}
}
render(){ const {isHot} = this.state return <h1>今天天气很{isHot?'炎热':'凉爽'}</h1> }
}
this的指向问题#
我们预期的是将方法全部放到类里边,这样this
就是类组件实例的,在实例中调用其内部的方法,得用this.functionName
这种方式类中方法中的this
构造器中的this
一定是当前类的实例对象
render
是被new的实例对象调用,
类中方法中的this
由于自定义函数是作为onClick
的回调,所以不是通过实例调用的,是直接调用,
类中所有自定义的方法,它自局部开好了严格模式
所以自定义函数中的this
指向undefined
解决方式
this.changeWeather=this.changWeather.bind(this)
constuctor#
构造器是否接受props,是否传递props
,取决于:是否希望在构造器中通过this
访问props
,(类实例中props,不是实参props)
类组件中的构造器能不写就不写,如果写的话,必须传递prop
,并且调用super(props)
组件实例上三个重要属性#
- state,状态,组件实例对象身上的数据
- props,本组件之外传递过来的数据
- refs
- context
state#
state
之所以是state
,是因为他只能通过setState
传递,并且这个更新是一个合并操作
,不是更新操作,只是修改部分键值对,其他的键值对会原样奉还
🤚state
和函数
的最佳实践
类组件属性
箭头函数的this
类中自定义的属性,可以直接传递到类的实例当中
类中通过箭头函数定义的方法,其箭头函数内部this
指向和外部的一致,在这里就是类的实例
这样就规避了反复通过bind()
,一次一次的将原型链中的自定义方法绑定到类实例上的反复操作
class MyComponets extends React.component{
constuctor{
}
state={age:12,name:'kobe',isHOt:true}
render(){
const {isHot} = this.state
return <h1 onClick={changeWeather}>今天天气很{isHot?'炎热':'凉爽'}</h1>
}
const changeWeather=()=>{
this.state.isHot=!this.state.isHot
}
}
props#
通过在jsx的html标签中,传递属性
可以通过修改属性值,渲染出多个不同的组件
props是只读的,不能对其进行修改
🤚基础操作
class Person extends React.Componet{
render(){
const {name,age} = this.props
return (
<ul>
<li>姓名:{this.props.name}
<li>年龄:{age}
<ul>
)
}
ReactDom.render(<Person name="kobe" age="12">,document.getElementById('app'))
ReactDom.render(<Person name="tmac" age="18">,document.getElementById('app'))
🤚批量传递
批量传递props
,是可以用jsx
语法中的对象展开...object
,
这里区别于es6
是
这里区别于ES6
的三点展开,那个只能展开数组,jsx
中的 就是展开此对象,
当在组件属性中传递Number
类型是,要用{},括起来,转化为js,因为只有js中才存在类型,jsx中的html属性传递的是字符串的键值对
class Person extends React.Componet{
render(){
const {name,age} = this.props
return (
<ul>
<li>姓名:{this.props.name}
<li>年龄:{age}
<ul>
)
}
ReactDom.render(<Person name="kobe",age={19}>,document.getElementById('app'))
const p={name:'kobe',age:12}
ReactDom.render(<Person {...p}>,document.getElementById('app'))
🤚对props进行限制
1. 对传递的标签属性进行类型限制
2. 必要性限制
3. 指定默认值
在React中通过在类组件中添加组件。propTypes
,添加规则
<script type="text/javascript" src="../js/prop-types.js"></script>
<script type="text/babel">
class Person extends React.Component{
//....
}
//限制类型和必要性
Person.propTypes={
name:React.ProTypes.string, //React<=15
name:ProTypes.string.isRequired, //React>=16,引入单独的`prop-types.js`
age:ProTypes.number,
sex:ProTypes.string.isRequired,//限制sex必传且为字符串
speak:Protypes.func, //方法也可以规范之
}
//指定默认值
Person.defaultProps={
sex:'男',
age:18
}
</script>
将一个属性加到类本身上,和将一个属性加到类的实例上方式是不一样的类自身加属性
class Car{
constuctor(name,price){
this.name=name
this.price=price
}
wheel=4 //在类中直接写赋值语句,是给car的实例对象添加一个属性
static test =100 //在其前边添加 static 是给这个类本身,而不是加给类的实例
}
🤚 props
最佳实践完整版本
class Person extends React.Component{
static propTypes={
name:React.ProTypes.string, //React<=15
name:ProTypes.string.isRequired, //React>=16,引入单独的`prop-types.js`
age:ProTypes.number,
sex:ProTypes.string.isRequired,//限制sex必传且为字符串
speak:Protypes.func, //方法也可以规范之
}
static defaultProps={
sex:'男',
age:18
}
render(){
// ....
}
}
ref#
一句话,ref是用来在react中
拿到最后渲染传来的真实dom
节点的,拿到之后可以对齐做一些dom操作
🤚String
类型的ref
多见于verson 16.8
之前
在jsx
的html标签中,打入ref=''
标签
React将会收集jsx
中所有的ref
,放入类实例对象中的this.refs
中
在this.refs
中通过解构赋值的形式拿到当前dom节点
class Person extends React.Component{
const {test}=this.refs
render(){
<div>
<h1 ref="test"></h1>
</div>
}
}
🤚回调函数方式的ref
通过在ref标签
上写匿名函数
的方式,拿到当前Dom
当前节点
class Person extends React.Component{
render(){
<div>
//通过箭头函数的方式,将当前节点挂载到当前类实例自身上
<input ref={currentNode=>{this.input1=currentNode}}></input>
</div>
}
}
事件绑定#
原生dom事件绑定
React中的事件绑定
原生事件绑定#
//第一种方法
const btn1=doncument.getElementById('btn1')
btn1.addEventListener('click',()=>{
alert('按钮1被点击了')
})
//第二种方法
const btn2=doncument.getElementById('btn2')
btn2.onclick=()=>{
alert('按钮2被点击了')
}
React事件绑定#
React中的事件绑定,因为React把事件指向了类组件之外的一个函数,作为回调,这个函数是拿不到类组件中的this
的,所有有一个本办法是,在constuctor
中将that=this
,将this缓存到一个全局变量中,
再在事件函数中调用that.state
类组件中的render
方法返回的类似html结构的JSX
中,将html的标签的属性都额外定义了一边,以React自身解析之用,例如onclick
>onClick
,class
>className
,这里简单复习,原生的两种绑定点击时间的方法
//第三种,在React中,请用一下方式
//html中一些原生标签属性,在jsx中都有其替代品,例如这里的onclick==>onClick
//注意这里的函数名不要加(),执行它
let that;
class Demo extends React.Componet{
render(){
const {isHot} = this.state
return <h1 onClick={changeWeather}>今天天气很{isHot?'炎热':'凉爽'}</h1>
}
function changeWeather(){
that.state.isHot=!that.state.isHot;
}
生命周期#
经典钩子#
React16
1. 初始化阶段,由ReactDOM.render()触发
---初次渲染
1. contructor()
2. componentWillMount()
3. render()
4. componetDidMount()
a. 常用,页面加载完毕后,做一些初始化的事
b. 开启定时器,发送网络请求,订阅消息
1. 更新阶段:由组件内部this.setState()
或父组件
重新render
触发
0. componentWillReceviceProps
,在首次不触发,二次更新state
,会触发这个钩子
1. shouldComponentUpdate()
(阀门),其中返会的布尔值,生命是否往下流动
2. componetWillUpdate()
, 注意通过foreceUpdate()
可触发强制更新,绕过阀门
3. render()
4. componentDidUpdate()
3. 卸载阶段:由ReactDOM。unMountComponentAtNode()
触发
1. componentWillUnmount()
a. 常用,一般在此钩子中,做一些收尾阶段的事
b. 关闭定时器,取消消息
增强钩子#
新版本的React17
生命周期钩子有所改变
1. componetWillMount()
改名为UNSAFE_componentWillMount()
,并且不被推荐使用
2. componentWillReceviceProps
改名为UNSAFE_componentWillReceviceProps()
,并且不被推荐使用
3. componentWillUpdate
改名为UNSAFE_componentWillUpdate()
4. 最重要的三个钩子render
componentDidMount
(出生) componentWillUnmount
(将死),在React17
,没有改变
对比#
挂载阶段
getDerivedStateFromProps
====>componentWillMount
更新阶段
getDerivedStateFromProps
====>componentWillReceviceProps'
getDerivedStateFromProps横跨了挂载和更新,从
props`得到一个派生的状态.它还是一个static 静态方法,给类自身添加的函数,
新旧生命周期相比,废弃了三个钩子,提出了两个新钩子,getDerivedStateFromProps
和getSnaphotBeforeUpdate
,且一般使用不到,是给开发者用的,相当于简化了日常的使用