react定义组件&组件实例的三大属性
复杂和简单的定义就是:复杂组件有状态(state)
函数式组件(简单式组件)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<link rel="stylesheet" href="../favicon.ico">
<!-- 这里刷新不出来 就强制刷新 shift+刷新 -->
</head>
<body>
<!-- 准备好一个容器 -->
<div class="test">
</div>
<!-- 引入react核心库 -->
<script src="../js/react.development.js"></script>
<!-- 引入react-dom 用于支持react操作DOM -->
<script src="../js/react-dom.development.js"></script>
<!-- 引入Babel 用于将jsx转为js -->
<script src="../js/babel.min.js"></script>
<script type="text/babel">
// babel 翻译 处理之后 会开启严格模式(不允许自定义的函数 this指向window)
function MyComponents(){
console.log(this); //此处的this是undefined 因为Babel编译后开启了严格模式
return <h1>我是用函数定义的组件(适用于【简单组件】的定义)</h1>
}
// 组件一定是一个标签 并且标签的首字母必须大写 标签必须闭合(jsx的规定 )
ReactDOM.render(<MyComponents/>,document.querySelector('.test'));
/*
执行了ReactDOM.render发生了什么
React解析组件标签,找到了MyComponent组件
发现组件是使用函数定义的,随后调用该函数,将函数返回的虚拟DOM转为真实DOM,之后呈现在页面中
*/
</script>
</body>
</html>
类式组件(复杂式组件)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div class="test">
</div>
<script src="../js/react.development.js"></script>
<script src="../js/react-dom.development.js"></script>
<script src="../js/babel.min.js"></script>
<script type="text/babel">
// 创建类式组件
// 必须要有继承、render和返回值
class MyComponents extends React.Component {
// render放在了类的原型对象上,供实例使用
render() {
// render中的this是:mycomponent的实例对象==MyComponent组件实例对象
console.log(this);
return <h2>我是用类定义的组件(适用于【复杂组件】的定义)</h2>
}
}
// 渲染组件到页面
// 注意这里的两个render是不一样的
ReactDOM.render(<MyComponents/>,document.querySelector('.test'))
/*
执行了ReactDOM.render发生了什么
React解析组件标签,找到了MyComponent组件
发现组件是使用类定义的,随后new出来MyComponent类的实例,并通过该实例调用到类的原型上的render方法
将render返回的虚拟DOM转为真实DOM,之后呈现在页面中
*/
</script>
</body>
</html>
组件实例的三大核心属性1:state
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div class="test">
</div>
<script src="./js/react.development.js"></script>
<script src="./js/react-dom.development.js"></script>
<script src="./js/babel.min.js"></script>
<script type="text/babel">
// 创建组件
class Weather extends React.Component {
// 构造器调用一次
constructor(props){
// 关于this的操作 super必须方法最前方
super(props);
// 借助构造器初始化状态
this.state={isHot:false };
// bind会生成一个新的函数,并且可以改变this的指向
// 解决了changeWeather中的this指向问题
this.changeWeather=this.changeWeather.bind(this)
}
// render调用n+1次 1是初始化的调用 n是点击的切换的次数
render () {
// console.log(this);
// 读取状态 注意这里的花括号 表示的是在state中创建一个对象isHot
const {isHot}=this.state;
// 注意这里是onClick 并没有调用函数 并且用的是花括号
// 这里并不可以直接调用,changeWeather是供实例使用的,所以这里要用this
// 从this里面查找changeWeather
return <h2 onClick={this.changeWeather}>今天的天气很{isHot?'炎热':'凉爽'}</h2>
}
// changeWeather 点击几次就调用几次
changeWeather(){
// changeWeather放在了Weather的原型对象上,供实例使用
// changeWeather是onClick的回调,所以并不是实例的回调,而是直接调用
// 直接调用的this指向应该是window,但是有严格模式
// class类中的方法默认开启了局部的严格模式,并且在Babel里面也是严格模式,所以changWeather中的this就是undefined
console.log(this.state.isHot);
// 注意state是不可以随便进行更改的 需要借助一个内置的API进行更改
const isHot=this.state.isHot;
// this.state.isHot=!isHot; //直接更改
// 只要通过合法的方式 更改state react就会重新调用一次render
// setState 只是合并了状态 而不是替换
this.setState({isHot:!isHot});
}
}
ReactDOM.render(<Weather/>,document.querySelector('.test'))
</script>
</body>
</html>
代码的简写(这里只写了script里面的代码)
<script type="text/babel">
class Weather extends React.Component{
// 在类中可以直接进行赋值语句
state={isHot:false};
render(){
const {isHot}=this.state;
return <h2 onClick={this.changeWeather}>今天的天气很{isHot?"炎热":"凉爽"}</h2>
}
// 自定义的函数 主要是:使用在事件回调
// 这里一定要使用箭头函数 因为箭头函数没有this指向 this是外层的
// 注意这里是 用赋值语句创建了一个函数
changeWeather=()=>{
const isHot=this.state.isHot;
this.setState({isHot:!isHot})
}
}
ReactDOM.render(<Weather/>,document.querySelector('.test'))
</script>
js对象解构赋值:
es6的写法:const {xxx}=this.state,const{}说明接受的是一个对象
其实相当于:const xxx=this.state.xxx
点击事件的三种使用方法:
react使用规则:所有原生里面以“on”开头的事件,都要用一下写法(onclick-->onClick;onblur-->onBlur)
总结
-
state是组件对象中最重要的属性,值是对象(可以包含多个key-value的组合)
-
组件被称为“状态机”,通过更新组件的state来更新对应的页面显示(重新渲染组件)
-
组件中render方法中的this为组件实例对象
-
组件自定义的方法中this俄日undefined
1)通过强制绑定this(函数对象的bind())
2)箭头函数 (通过赋值语句)
-
状态数据,不能直接修改或更新(通过setState)
组件里面维护着状态,状态里面存着数据,在更新里面的数据时,组件就进行渲染
组件实例的三大核心属性2:props
使用方法
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div class="test">
</div>
<div class="test2">
</div>
<script src="./js/react.development.js"></script>
<script src="./js/react-dom.development.js"></script>
<script src="./js/babel.min.js"></script>
<!-- 引入prop-types,用于对组件标签属性进行限制 -->
<script src="./js/prop-types.js"></script>
<script type="text/babel">
class Person extends React.Component{
render(){
console.log(this);
const {name,age,sex} =this.props
// props是只读的
// this.props.name='jack' //这里代码会报错,props进行了更改
return (
<ul>
<li>姓名:{name}</li>
<li>性别:{sex}</li>
<li>年龄:{age+1}</li>
</ul>
)
}
}
// 对组件进行规则限制
// 这里propType是react组件里面的规矩 并且p小写
Person.propTypes = {
// react里面的内置属性 设置属性的数据类型
// 引入库文件 注意这里用大写
// string 表示组件的内置属性必须是字符形式,isRequired表示必须要填写
name: PropTypes.string.isRequired,
age: PropTypes.number,
sex: PropTypes.string,
speak:PropTypes.func //限制speak为函数
}
// 指定默认标签属性值
Person.defaultProps = {
name:'Seven',
age:19
}
// 1.对传递的标签属性进行类型限制
// 2.对某一个属性添加一个默认值
// 渲染组件到页面
// 这里age若想是number型数据 就要加花括号 react没有number数据 但是js中有,所有使用{}
ReactDOM.render(<Person sex="女"/>,document.querySelector(".test"))
const p ={name:'jerry',sex:"男",age:20}
// ReactDOM.render(<Person name={p.name} />,document.querySelector(".test2"))
// {...p}相当于39的语法糖 但是获取到属性名必须一致 不然获取不到信息
// 这里的花括号表示花括号里面的代码是js 这里并不是{...p}展开运算符展开了对象
// Babel和react加在一起就可以使用...p展开一个对象 但是仅仅适用于标签属性的传递 其他地方都不可以
ReactDOM.render(<Person {...p}/>,document.querySelector(".test2")) //批量的传递标签属性 传递标签==传递props
</script>
</body>
</html>
简写方式:
<script type="text/babel">
class Person extends React.Component{
// 将标签上的属性添加给Person类 而不是实例上
// 对标签属性进行类型,必要性的限制
static propTypes={
name:PropTypes.string.isRequired,
age:PropTypes.number,
sex:PropTypes.string,
speak:PropTypes.func,
}
static defaultProps={
name:'Seven',
sex:'女'
}
render(){
const {name,age,sex} = this.props;
return (
<ul>
<li>name:{name}</li>
<li>age:{age+1}</li>
<li>sex:{sex}</li>
</ul>
)
}
}
ReactDOM.render(<Person name="tom" age={20} sex="女" />,document.querySelector('.test'))
const p={name:'jerry',age:19,sex:"女"}
ReactDOM.render(<Person {...p} />,document.querySelector('.test2'))
</script>
将对标签的限制放在了类里面。
展开运算符的复习:
<script>
let arr1=[1,2,3,4,5];
let arr2=[6,7,8,9];
console.log(...arr1);//展开一个数组
console.log(...arr1,...arr2); //连接一个数组
// 展开运算符在函数中使用
// reduce函数
// 累加和
function sum(...numbers) {
return numbers.reduce((preValue,currentValue)=>{
return preValue+currentValue
})
}
console.log(sum(1,2,3,4,5))
let person={name:"tom",age:19}; //展开运算符不能展开对象
let person2=person; //这里并不是对象的复制
// 构造字面量对象时使用展开运算符
let person3={...person};
person.name="ab";
console.log(person2); //{name: 'ab', age: 19}
console.log(person3); //{name: 'tom', age: 19}
// console.log(...person);
// 在复制对象的时候 更改对象里面的属性
// 属性的更改实际上是合并的操作
let person4={...person,name:"jerry",address:"moon"};
console.log(person4); //{name: 'jerry', age: 19, address: 'moon'}
</script>
函数式组件使用props
因为函数可以传递参数,所以可将props传进函数中,(state和refs不可以,因为没有this)
普通函数:由于没有对象的或者实例的调用,所以this指向js顶级对象window
<script type="text/babel">
function Person (props){
console.log(this) //undefined
const {name,age,sex}=props;
return (
<ul>
<li>name:{name}</li>
<li>age:{age+1}</li>
<li>sex:{sex}</li>
</ul>
)
}
// 将对组件标签的限制放在 函数的外部
Person.propTypes={
name:PropTypes.string.isRequired,
age:PropTypes.number,
sex:PropTypes.string,
speak:PropTypes.func,
}
Person.defaultProps={
name:'Seven',
sex:'女'
}
ReactDOM.render(<Person age={20} sex="女" />,document.querySelector('.test'))
</script>
总结
- 每个组件的对象都会有props(properties的简写)属性
- 组件标签中的所有属性都保存在props中
- 通过标签属性从组件内传递变化的数据
- 注意:组件内部不要修改props数据
- 使用prop-types库进行限制(需要引入prop-type库)
- 内部读取某个属性值 this.props.name
- 扩展属性:将对象的所有属性通过prop传递 <Person {...p}>
- 默认的属性值 Person.defaultProps={age:19}
- 组件类的构造函数 constructor(props){super(props);console.log(props)//打印所有属性}
组件实例的三大核心属性3:refs
字符串形式的ref使用方法(因为效率问题,逐渐被淘汰):
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div class="test">
</div>
<script src="./js/react.development.js"></script>
<script src="./js/react-dom.development.js"></script>
<script src="./js/babel.min.js"></script>
<!-- 引入prop-types,用于对组件标签属性进行限制 -->
<script src="./js/prop-types.js"></script>
<script type="text/babel">
class Demo extends React.Component{
showDate1=()=>{
// 注意这里是refs
const {input1}=this.refs
alert(input1.value)
}
showDate2=()=>{
const {input2}=this.refs
alert(input2.value)
}
render(){
return(
<div>
<input ref="input1" type="text" placeholder="点击按钮提示数据"/>
<button onClick={this.showDate1}>点击提示左侧数据</button>
<input ref="input2" onBlur={this.showDate2} type="text" placeholder="失去焦点提示数据"/>
</div>
)
}
}
// 渲染组件到页面
ReactDOM.render(<Demo/>,document.querySelector('.test'))
</script>
</body>
</html>
回调式的ref:
内联式(使用较多)
ref能够自动调用函数 并且将当前节点作为实参传递给函数
只需将当前节点赋值给实例自身
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div class="test">
</div>
<script src="./js/react.development.js"></script>
<script src="./js/react-dom.development.js"></script>
<script src="./js/babel.min.js"></script>
<!-- 引入prop-types,用于对组件标签属性进行限制 -->
<script src="./js/prop-types.js"></script>
<script type="text/babel">
class Demo extends React.Component{
state={isHot:true}
changeWeather=()=>{
const {isHot}=this.state;
this.setState({isHot:!isHot})
}
showDate1=()=>{
const {input}=this
alert(input.value)
}
render(){
const {isHot}=this.state;
return(
<div>
<h2>今天天气很{isHot?'炎热':'凉爽'}</h2>
<input ref={currentNode=>{this.input=currentNode;console.log("@",currentNode);}} type="text" placeholder="点击按钮提示数据"/>
<button onClick={this.showDate1}>点击提示左侧数据</button>
<button onClick={this.changeWeather}>点击我切换天气</button>
</div>
)
}
}
// 渲染组件到页面
ReactDOM.render(<Demo/>,document.querySelector('.test'))
</script>
</body>
</html>
由于再更新(state发生变化)过程中会点函数会被执行两次,第一次传入参数null(用于清除更新之前的函数操作)第二次传入参数DOM元素。因为每次渲染时会创建一个新的函数实例,清空旧的并且设置新的。
class绑定组件式ref:
<script type="text/babel">
class Demo extends React.Component{
state={isHot:true}
changeWeather=()=>{
const {isHot}=this.state;
this.setState({isHot:!isHot})
}
showDate1=()=>{
const {input}=this
alert(input.value)
}
saveInput=(c)=>{
this.input=c;
console.log('@',c);
}
render(){
const {isHot}=this.state;
return(
<div>
<h2>今天天气很{isHot?'炎热':'凉爽'}</h2>
{/*<input ref={currentNode=>{this.input1=currentNode;console.log("@",currentNode);}} type="text" placeholder="点击按钮提示数据"/> */}
<input ref={this.saveInput} type="text"/>
<button onClick={this.showDate1}>点击提示左侧数据</button>
<button onClick={this.changeWeather}>点击我切换天气</button>
</div>
)
}
}
// 渲染组件到页面
ReactDOM.render(<Demo/>,document.querySelector('.test'))
</script>
最新的createRef:
<script type="text/babel">
class Demo extends React.Component{
/*
React.createRef调用 后可以返回一个容器,该容器可以存储被ref所标识的节点,但是该容器是”专人专用“
*/
myRef=React.createRef();
showDate1=()=>{
console.log(this.myRef.current.value);
}
render(){
return(
<div>
<input ref={this.myRef} type="text"/>
<button onClick={this.showDate1}>点击提示左侧数据</button>
</div>
)
}
}
// 渲染组件到页面
ReactDOM.render(<Demo/>,document.querySelector('.test'))
</script>