React 学习笔记(一):JSX;工厂函数组件、ES6类组件;三大属性(state、props、refs)

学习视频源自:https://www.bilibili.com/video/BV1Et41137Sb?p=4
UP 主:Java基基
机构:尚硅谷

一、相关浅析

1.1 React

React:用于构建用户界面的 JavaScript 库

Rreact 高效的原因:虚拟 DOM;DOM Diff 算法,最小化页面重绘(更新元素)
在虚拟DOM上做修改,然后虚拟DOM再去操作真实DOM

1.2 babel

Babel:是一个 JavaScript 编译器

babel:把 ES6 转成 ES5,方便使用 ES6 进行开发
在这里插入图片描述

二、简单上手

2.1 引入文件

相关 js 库:

  • react.js:react 的核心库
  • react-dom.js:react 扩展库,操作 DOM
  • babel.min.js:解析 JSX 语法代码转为纯 JS 语法代码的库(浏览器不能直接识别 JSX)(JSX:JS 的扩展语法,全称 JavaScript XML,使用 JSX 语法时,标签名任意、属性名任意–可以自定义)

在这里插入图片描述

2.2 简单测试

  • render():渲染虚拟DOM到真实DOM,render(virtualDOM, containerDOM),即render(虚拟DOM, 真实DOM)

代码:

<body>
	<div id="test"></div>
	// 注意类型是 babel,不是 javascript,因为要使用 JSX 语法
	<script type="text/babel">
		// 1.创建虚拟DOM对象
		var vDOM = <h1>Hello React!</h1>// 不是字符串;JSX语法
		// 2.将虚拟DOM渲染到真实DOM容器中
		React.render(vDOM, document.getElementById('test'))
	</script>
</body>

效果图:看标签结构
在这里插入图片描述

  • createElement():createElement(标签名, { 属性: 属性名 }, 内容)
    例子:createElement('h2', { 'id': 'myID' }, 'Hello')

  • toLowerCase():转换成纯小写

  • toUpperCase():转换成纯大写

2.3 JS 语法和 JSX 语法比较

JSX官方说明
JSX中文文档

从下图可以看出,使用 JSX 语法可读性更强,书写更方便

JSX语法中,使用 {} 代替 ""
在这里插入图片描述

2.4 在Google添加扩展程序:react developer tool

2.5 运行性能

直接运行会很慢,所以在真实项目中一般先编译后运行

三、JSX 练习

3.1 效果图

实现动态展示数据 li 列表
在这里插入图片描述

3.2 代码

重点:如何根据数据数组,写出对应的标签数组–map() 方法(在React中这将很常用)

<div id="test"></div>
<script>
	const listData = ['jquery', 'zeptoo', 'angular', 'react全家桶', 'vue全家桶']
	// 创建虚拟DOM
	const ul = (
		<ul>
			{ // 箭头函数
				listData.map((item, index) => <li key={index}>{item}</li>)
			}
		</ul>
	)
	// 渲染真实DOM
	React.render(ul, document.getElementById('test'))
</script>

备注:key 属性类似于 Vue 中 for 循环里面的 :key

3.3 感受

老师说,React 写起来,页面代码比较乱,嗯……这话不假,HTML 和 JS 混在一起(JS代码使用 { } 包裹),确实不是很分明

老师也说,要适应,哈哈哈……

四、React 面向组件编程

面向对象->面向模块->面向组件

4.1 React 组件的基本定义和使用

两种方式:工厂函数组件、类组件,函数组件方式比类组件方式效率高,因为类组件需要创建对象
工厂函数组件(简单组件):没有状态的组件
类组件(复杂组件):含有状态的组件
所以,如果需要添加状态

方式一、工厂函数组件(简单组件)
<div class="example1"></div>
<script type="text/babel">
	// 1.定义组件
	function MyComponent1() {
		return <h2>工厂函数组件</h2>
	}
	// 2.渲染组件内的标签
	ReactDOM.render(<MyComponent1 />, document.getElementVyId('example1'))
</script>

注意:(1)工厂函数必须有 return;(2)工厂函数返回值为虚拟DOM

方式二、ES6 类组件(复杂组件)
<div class="example2"></div>
<script type="text/babel">
	// 1.定义组件
	class MyComponent2 extends React.Component() {
		render() {
			return <h2>ES6类组件</h2>
		}
	}
	// 2.渲染组件内的标签
	ReactDOM.render(<MyComponent2 />, document.getElementVyId('example2'))
</script>

备注:React 是对象,Component 是该对象的一个属性

4.2 组件三大属性

props、refs、state
在这里插入图片描述
打印this:
在这里插入图片描述

4.2.1 state
(1)基础

state:值是对象类型。通过更新组件的 state 来更新对应的页面显示(重新渲染组件)
有以下步骤:初始化状态、读取状态、更新状态(组件界面自动更新)

// this 是当前组件对象

// 1.初始化状态:值为对象类型
constructor(props) {
	super(props)
	this.state = {
		stateProp1: value1,
		stateProp2: value2
	}
}

// 2.读取某个状态值:通过对象的key获取value
const statePropertyName = this.state.statePropertyName// 获取state对象的statePropertyName属性
// const { statePropertyName } = this.state//ES6写法

// 3.更新状态--组件界面更新:参数是对象类型
this.setState({
	stateProp1: value1,
	stateProp2: value2
})

注意:更新状态需要使用 setState(因为直接修改state,组件并不会重新触发render())

// 直接修改state,组件并不会重新触发render()
this.state.comment = 'Hello';// 错误

// 正确的修改方式是使用setState()
this.setState({comment: 'Hello'});// 正确
(2)小案例:交互

监听点击事件更新DOM(通过更新状态值实现)(状态值相当于标记flag)

注意:

  • 在render(React.components中的方法)中,this为当前组件类的对象;
  • 在自定义的方法中,this不是当前组件类,而是默认为undefined

解决方法:将自定义方法中的this强制绑定为组件对象

  • 方式一:在 constructor 中,写 this.handleClick = this.handleClick.bind(this)(通过 bind 方法产生一个新的函数,该函数的函数体体和实际操作的handleClick的 函数体一模一样)
  • 方式二:在 render 中,写 return <h2 onClick="this.handleClick.bind(this)">

推荐使用方式一(只执行一次bind),因为方式二不是很高效(每次监听触发都会重新执行一次bind)

<div class="example"></div>
<script type="text/babel">
	// 1.定义组件
	class Like extends React.Component() {
		// 1.1 状态值--重写组件方法
		constructor(props) {
			super(props)//固定格式
			this.state = { isLikeMe: false }// 初始化状态值
			// 将自定义方法中的this强制绑定为组件对象
			this.handleClick = this.handleClick.bind(this)// 产生一个新的函数体,该函数体是实际操作的函数体
		}
		// 1.2 组件方法--自定义方法:自定义方法内部的this默认是undefined,而非组件对象
		handleClick() {
			const isLikeMe = !this.state.isLikeMe
			this.setState({ isLikeMe })// ES6语法
			// this.setState({ isLikeMe: isLikeMe })
		}
		// 1.3 渲染标签--重写组件方法:render中的this为当前组件类的对象
		render() {
			const isLikeMe = this.state.isLikeMe// 读取状态值
			// const { isLikeMe } = this.state//ES6语法
			return <h2 onClick="this.handleClick">isLikeMe ? '你喜欢我':'我喜欢你' </h2>// 注意是onClick,不是onclick
			// return <h2 onClick="this.handleClick.bind(this)">isLikeMe ? '你喜欢我':'我喜欢你' </h2>// 或者直接在监听事件中绑定this,上面的方式更常用(因为这个方式效率低,每次监听触发都会重新执行一次bind)
		}
	}
	// 2.渲染组件内的标签
	ReactDOM.render(<Like />, document.getElementVyId('example'))
</script>

解构赋值:{ }(ES6语法)
const { isLikeMe } = this.state 等价于 const isLikeMe = this.state.isLikeMe

4.2.2 props
4.2.2.1 基础
  • 读取属性值:this.props.propertyName(其中,propertyName为自定义的属性名,this.props为固定代码)
  • 属性值类型限制和必要性限制:MyComponent.propTypes= { type1: React.propTypes.string.isRequired, type2: React.propTypes.number }(红色部分之外为自定义部分)
    注意:React.PropTypes自v15.5已经弃用,使用prop-types库代替,写法如下:
    MyComponent.propTypes= { type1: propTypes.string.isRequired, type2: propTypes.number }(红色部分之外为自定义部分)
  • 扩展属性:将对象的所有属性通过 props 传递
  • 设置属性默认值:MyComponent.defaultProps = { prop1: ‘props1默认值’, prop2: ‘props2默认值’ }(红色部分之外为自定义部分)
  • 组件类的构造函数
4.2.2.2 小案例(一)

自定义组件,实现人员信息显示
要求:(1)如果性别没有指定,默认为男;(2)如果年龄没有指定,默认为18
在这里插入图片描述
工厂函数方式实现:

<div class="example"></div>
<script type="text/babel">
	// 1.定义组件
	function Person(props) {
		return (
			<ul>
				<li>姓名:{ props.name }</li>
				<li>性别:{ props.sex }</li>
				<li>年龄:{ props.age }</li>
			</ul>
		)
	}
	// 2.指定默认值
	Person.defaultProps = {
		sex: '男',
		age: 18
	}
	// 3.渲染组件标签
	const p1 = {
		name: 'JACK'
	}
	ReactDOM.render(<Person name={p1.name} />, document.getElementById('example'))
</script>
4.2.2.3 小案例(二):指定属性值的类型和必要性
  • defaultProps:设置默认值
  • propTypes:设置必要性限制

需要引入 prop-types.js

代码:

<div class="example"></div>
<script type="text/babel">
	// 1.定义组件
	function Person(props) {
		return (
			<ul>
				<li>姓名:{ props.name }</li>
				<li>性别:{ props.sex }</li>
				<li>年龄:{ props.age }</li>
			</ul>
		)
	}
	// 2.指定默认值
	Person.defaultProps = {
		sex: '男',
		age: 18
	}
	// 3.属性类型限制和必要性限制
	Person.propTypes = {
		name: PropTypes.string.isRequired,// 类型设为string;必须传值
		age: PropTypes.number// 类型为number;不要求必须传值
	}
	// 4.渲染组件标签
	const p1 = {
		name: 'JACK'
	}
	ReactDOM.render(<Person name={p1.name} age={ 20 } />, document.getElementById('example'))
</script>

备注:age=“18”,此时18是字符串;age={ 18 },此时18是数字

效果图:在这里插入图片描述

4.2.2.4 小案例–补充:扩展运算符

...:扩展运算符,可以使用在对象或者数组上
…打包:function fn(…as) { },fn(1, 2, 3)
…解包:const arr1=[1,2,3],const arr2=[0, …arr1, 4]
<Person {…p1} />是解包

<div class="example"></div>
<script type="text/babel">
	// 1.定义组件
	function Person(props) {
		return (……)
	}
	// 2.渲染组件标签
	const p1 = {
		name: 'JACK',
		sex: '男',
		age: 18
	}
	// ReactDOM.render(<Person name={p1.name} sex={ p1.sex } age={ p1.age } />, document.getElementById('example'))
	ReactDOM.render(<Person {...p1} />, document.getElementById('example'))// 使用扩展运算符
</script>
4.2.2.5 小案例–类组件方式–不知道对不对
<div class="example"></div>
<script type="text/babel">
	// 1.定义组件
	class Person extends React.Component() {
		render() {
			return(
				<ul>
					<li>姓名:{ this.props.name }</li>
					<li>性别:{ this.props.sex }</li>
					<li>年龄:{ this.props.age }</li>
				</ul>
			)
		}
	}
	// 2.指定默认值
	Person.defaultProps = {
		sex: '男',
		age: 18
	}
	// 3.属性类型限制和必要性限制
	Person.propTypes = {
		name: PropTypes.string.isRequired,// 类型设为string;必须传值
		age: PropTypes.number// 类型为number;不要求必须传值
	}
	// 4.渲染组件标签
	const p1 = {
		name: 'JACK'
	}
	ReactDOM.render(<Person />, document.getElementVyId('example'))
</script>
4.2.3 refs

refs:标识组件内元素(相当于document.getElementById,获取DOM元素)
DOM操作官方说明

4.2.3.1 小案例

要求:自定义组件,并完成以下功能

  • 点击按钮,提示第一个输入框的值
  • 第二个输入框失去焦点,提示输入值
    在这里插入图片描述
<body>
	<div id="example"></div>
	<script type="text/babel">
		// 1.定义组件
		class MyComponent extends React.Components{
			// 1.1 强制绑定(给自定义方法的this强制绑定为组件对象)
			constructor(props) {
				super(props)
				this.showInput = this.showInput.bind(this)
				this.showOutBlur = this.showOutBlur.bind(this)
				
			}
			// 1.2 自定义方法
			showInput() {
				const input = this.refs.input
				alert(input.value)
			}
			showOutBlur(event) {
				alert(event.target.value)
			}
			// 1.3 渲染
			render() {
				return(
					<div>
						<input ref="input" type="text" />
						<button onClick={ this.showInput }>提示输入</button>
						<input onBlur={ this.showOutBlur } type="text" placeholder="该输入框失去焦点时提示输入内容" />
					</div>
				)
			}
		}
		// 2.渲染组件标签
		ReactDOM.render(<MyComponent />, document.getElementById('example'))
	</script>
</body>
4.2.3.2 代码说明1:event.target.value

所有的事件回调函数都有一个形参 event
event.target.value() 获取当前文本框的值(由事件触发时:回车、点击等)

4.2.3.3 代码说明2:通过 ref 获取DOM元素

官方不建议:

showInputFirst() {
	// 官方不建议
	const input = this.refs.inputOne
	alert(input.value)
}
render() {
	return(
		<div>
			// 官方不建议
			<input type="text" ref="inputOne" />
		</div>
	)
}

官方建议:

showInputFirst() {
	// 官方建议
	alert(this.input.value)
}
render() {
	return(
		<div>
			// 官方建议
			<input type="text" ref="inputOne" ref={ input => { this.input = input } } />
		</div>
	)
}

分析:input => { this.input = input }
当前标签元素 => { this.自定义命名 = 当前标签元素 }
意思:当前标签元素 => { 将当前标签元素写入组件对象中 }

4.2.4 state 和 props 的区别

详细可参考该博文:https://blog.csdn.net/b954960630/article/details/79822639

4.3 解决 this 问题的两种方法

  • 强制绑定:bind(this)
  • 箭头函数
omponentDidMount() {
	this.intervalID = setInterval(function() {
		let {opcity} = this.state
		opcity -= 0.1
		if(opcity<=0) { opcity = 1 }
		this.setState({opcity})
	}.bind(this),200) // 绑定this为当前react实例
}
omponentDidMount() {
	// 使用箭头函数解决this问题
	setInterval(()=>{
		this.setState({date: new Date()})
	}, 1000)
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值