学习视频源自: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语法中,使用 {} 代替 ""
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)
}