在REACT工程化项目中(yscode),我们会把需要写REACT组件的JS命名为.jsx
目的 : 让创建的文件识别jsx语法,而在create-react-app 脚手架创建的项目中,已经包含了对.jsx文件的处理
REACT 中的组件:每一个组件都是一个单独的个体(数据私有,有自己完整的生命周期,有自己的视图)
1.函数式组件
只要返回一个JSX元素就可以(掌握props)
- 当REACT-DOM.RENDER渲染的时候,如果TYPE是函数(函数式组件),会把函数执行(函数式组件要求必须有返回JSX虚拟DOM的RETURN返回值),会把解析出来的props(属性+子元素)传递给函数,属性是只读的;
- 对于传递的子元素(props.children)可以基于React.Children.map来迭代遍历
特点:
- 简单(开发维护简单和渲染速度也快)
- 函数式组件是静态组件,一旦组件被调用,组件中的内容渲染完成,当前组件中的信息基本上就不变了(给组件传递进来的属性只读,也不能赋值),(除非重新调用组件传递不同的信息)=> ”静态组件“; 报错信息: Cannot assign to read only property 'title' of object
- 没有state状态管控内容,生命周期函数等随时更改组件中的内容(只有父组件重新调用它,才可能被更改)
- 不能给传递的属性直接的赋值
props.title = props.title || '我是标题' ×
let title = props.title || ‘我是标题 √
- 函数式组件不能像类组件一样,基于prop-types给属性设置默认的规则
// 只要在JS中使用jsx,则必须引入REACT(因为需要用到CREATE-ELEMNT)
// React.Children.map 遍历传递进来的子JSX对象(子元素props.children)
import React from 'react';
export default function Vote(props) {
console.log(props) // => {index: 10, title: "每天开心大笑~"}
return <div>
{props.title}
{/* {props.children} */}
{React.Children.map(props.children, (item, index) => {
if (index === 0) {
return <h2>
{item} === {index}
</h2>
}
})}
</div>
}
==================================
ReactDOM.render(<div>
hello world~
<Vote index={10} title='每天都要开心大笑~'>
<span>哈哈哈</span>
<span>呵呵呵</span>
</Vote>
<Vote title = 'REACT支持单闭和标签组件调用(VUE不支持)'/>
</div>, document.getElementById('root'))
2.基于React.Component 类创建组件
我们先来看一下ES6中关于类的一些知识点
/*
* 继承
* 1.原型继承
* 2.CALL继承
* 3.寄生组合继承 Object.create
* 4.ES6中基于CLASS实现继承
*/
class Parent { }
class Clock extends Parent {
constructor() {
super(); //=>Must call super constructor in derived class before accessing 'this' or returning from derived constructor 只要用到CONSTRUCTOR,第一行就要写SUPER(类似于CALL继承,会把父类当做函数执行,让函数中的THIS是子类的实例)
this.x = 100;
}
y = 200; //=>新增的语法:给实例设置的私有属性 和 CONSTRUCTOR中的 this.y=200效果一样
AAA() {
// Clock.prototype.AAA
}
static CCC = 300; //=>ES7新增的语法:直接在类上设置静态属性(REACT脚手架中给我们设置了关于这种语法的处理:@babel/plugin-proposal-class-properties)
static BBB() {
// Clock.BBB
}
}
let c = new Clock();
console.dir(c);
console.dir(Clock);
类组件应用:
class Xxx extends React.Component/React.PureComponent
当REACT-DOM.RENDER渲染的时候,如果发现虚拟DOM中TYPE是一个类组件,会创建这个类的一实例,并且把解析出来的PROPS传递给这个类:new Clock(props)
=>先执行CONSTRUCTOR(此时PROPS并未挂载到实例上,基于THIS.PROPS不能获取到值,但是可以直接使用形参中的PROPS;
**解决方法:**SUPER(PROPS)这样在CONSTRUCTOR中也可以用THIS.PROPS了)
=>当CONSTRUCTOR执行完,REACT会帮我们继续处理
->把PROPS/CONTEXT...挂载到实例上(后期在其它的钩子函数中可以基于THIS.PROPS获取传递的属性值)
->REACT帮我们把RENDER方法执行
类组件有自己的生命周期(lifecycle)
getDefaultProps
getInitialState
static childContextTypes={}
component
class Clock extends React.Component{
constructor(props){
// props:调取组件传递进来的属性
没有执行super()
console.log(props,this.props) // {title: "今天天气真好"} undefined(如果super(props)会读取到传递的属性
super();
//SUPER执行,相当于把React.Component当做普通函数执行,让方法中的THIS是当前实例:this=>{props:xxx,constructor:xxx,refs:{},updater:{...}}
}
// 必须要有render函数,它返回的内容使我们当前组件要渲染的视图
render(){
return <div>
<h2>{this.props.title}</h2>
</div>
}
}
ReactDOM.render(<div>
<Clock title='今天天气真好'></Clock>
</div>, document.getElementById('root'))
3. React状态管理
1.设置默认值
基于第三方插件prop-types设置属性的规则:默认值和其它规则
/*1.安装 $ yarn add prop-types*/
/*2.设置默认值*/
static defaultProps={
xxx:xxx
}
/*3.设置一些其它规则*/
static propTypes = {
title: PropTypes.string.isRequired
}
/*
*PropTypes.isRequired 必须传递
*PropTypes.string/bool/number/func/object/symbol/node(元素对象)/element(JSX元素)/instanceOf(Xxx)必须是某个类的实例/oneOf(['News', 'Photos'])多个中的一个/oneOfType([PropTypes.string,PropTypes.number,PropTypes.instanceOf(Message)])多个类型中的一个
*/
和VUE一样,我们设定的规则不会阻碍内容的渲染,不符合规则的在控制台报错
2.setState
setState 在合成时间或者生命周期函数中都是异步操作
把setState 放到一个异步操作中,此时它就没必要异步了,走的是同步处理;
- 定时器
- 原生JS事件
- ajax异步请求
合成事件:
React有一个类似于浏览器的渲染队列机制,它会在多次执行set-state的情况下,如果是异步操作,React把其合成一次进行重新渲染,如果修改的状态一样,以最后一次为主;
`handle = ev => {
* SET-STATE在合成事件或者生命周期函数中都是异步操作 this.setState({ n: 100 }); this.setState({ m: 100 });}`
默认是get/set 看不到数据,
可以通过ev.persist(); 或者ev.pageX直接获取
import React from 'react';
import ReactDOM from 'react-dom';
import propTypes from 'prop-types'
class Clock extends React.Component {
static defaultProps = {
title: '北京时间'
};
static propTypes = {
title: propTypes.string
};
constructor(props) {
super(props);
//=>初始化状态(和VUE中的DATA一样),要求后期需要在组件中使用的状态都要在这里初始化一下
this.state = {
time: new Date().toLocaleString(),
m: 100
};
}
render() {
return <div>
<h2>{this.props.title}</h2>
<p>{this.state.time}</p>
</div>;
}
//第一次加载组件渲染完毕 等价于 VUE中的MOUNTED
componentDidMount(){
// this.state.time = "哈哈哈"; //=>这样确实可以修改状态信息,但是不会通知组件重新渲染
// this.forceUpdate();/可以配合使用,让视图强制渲染 => componentWillUpdate(跳过should步骤)
//=>每一次修改状态应该基于:setState方法(相对于this.state.xxx=xxx来说,不仅更改了状态,还会通知视图重新渲染)
// partialState:部分状态(对象),我们初始化的状态有很多,想修改谁,这块只写谁即可(REACT内部是把之前的状态和传递的partialState进行合并替换的) Object.assign(this.state,partialState)
// callback:setState在某些情况下是异步操作,此回调函数代表,通知视图重新渲染后执行的
//=>shouldComponentUpdate()=>componentWillUpdate()
this.setState({
time:'不知道现在时间哦~'
},() => {
//=>通知视图已经重新渲染完成了
console.log('C');
});
}
}
ReactDOM.render(<>
<Clock></Clock>
</>, document.getElementById('root'))
受控组件
我们把基于状态(或者属性)的更新来驱动视图渲染=>'受控组件'(受状态管控的组件)
属性什么时候可以改?
- 设置默认值
- 父组件调取子组件,重新传递不同的属性;
- 把获取的属性传递给组件的状态,后期修改状态
基于prop-types设置默认值
class Clock extends React.Component {
static defaultProps = {
title: '北京时间'
};
static propTypes = {
title: PropTypes.string
};
constructor(props) {
super(props);
this.state = {
time: new Date().toLocaleString()
};
}
render() {
return <div>
<h2>{this.props.title}</h2>
<p>{this.state.time}</p>
</div>;
}
componentDidMount() {
setInterval(() => {
this.setState({
time: new Date().toLocaleString()
});
}, 1000);
}
}
ReactDOM.render(<div>
<Clock></Clock>
</div>, document.getElementById('root'));
非受控组件
非受控组件:不受状态管控(直接操作DOM)
class Clock extends React.Component {
static defaultProps = {
title: '北京时间'
};
static propTypes = {
title: PropTypes.string
};
constructor(props) {
super(props);
}
render() {
return <div>
<h2>{this.props.title}</h2>
{/* <p ref='timeBox'>{new Date().toLocaleString()}</p> */}
<p ref={element => {
//=>REF真实项目中应用最多的是函数的模式
// element代表当前元素对象
// 直接把元素对象挂载到实例上了
this.timeBox = element;
}}>{new Date().toLocaleString()}</p>
</div>;
}
componentDidMount() {
//=>可以获取到DOM元素
// this.refs={timeBox:p}
setInterval(() => {
this.timeBox.innerHTML = new Date().toLocaleString();
// this.refs.timeBox.innerHTML = new Date().toLocaleString();
}, 1000);
}
}
ReactDOM.render(<div>
<Clock></Clock>
</div>, document.getElementById('root'));