一. React 简介
1. react是什么
React 用于构建用户界面 JS 库,是一个将数据渲染为 HTML 试图的开源 JS 库。
2. 为什么学:
- 原生 JS 操作 DOM 繁琐,效率低
- 使用 JS 直接操作 DOM,浏览器会进行大量的重绘重排
- 原生 JS 没有组件化编码方案,代码复用低。
二. React入门
1. 基础案例
创建虚拟 DOM,渲染虚拟 DOM 到页面
<!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 id="test"></div>
<div id="demo"></div>
<!-- 引入 react 核心库 -->
<script type="text/javascript" src="../js/react.development.js"></script>
<!-- 引入 react-dom,用于支持 react 操作 DOM -->
<script type="text/javascript" src="../js/react-dom.development.js"></script>
<!-- 引入 babel,用于将 jsx 转为 js -->
<script type="text/javascript" src="../js/babel.min.js"></script>
<script type="text/babel"> /* 此处一定要写 babel */
// 1.创建虚拟 DOM
const VDOM = (
<h1 id="title">
<span>Hello,React</span>
</h1>
) /* 此处一定不要写引号,因为不是字符串 */
// 2.渲染虚拟 DOM 到页面
ReactDOM.render(VDOM,document.getElementById('test'))
const TDOM = document.getElementById('test')
console.log('虚拟DOM',VDOM)
console.log('真实DOM',TDOM)
debugger;
console.log(typeof VDOM)
console.log(VDOM instanceof Object)
// 关于虚拟DOM
// 1.本质是Object类型的对象(一般对象)
// 2.虚拟DOM比较“轻”,真实DOM比较“重”,因为虚拟DOM是React内部在用,无需真实DOM上那么的属性。
// 3.虚拟DOM最终会被React转化为真实DOM,呈现在页面上。
</script>
</body>
</html>
2. JSX语法规则
1. 定义虚拟 DOM 时,不要写引号。
2. 标签中混入 JS 表达式时要用{}。
3. 样式的类名指定不要用 class,要用 className 。
4. 内联样式,要用 style={{key:value}} 的形式去写
5. 只有一个跟标签
6. 标签必须闭合
7. 标签首字母
- 若小写字母开头,则将该标签转为 html 中同名元素,若 html 中无该标签对应的同名元素,则报错。
- 若大写字母开头,react 就去渲染对应的组件,若组件没有定义,则报错。
示例代码如下:
<!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 id="test"></div>
<!-- 引入 react 核心库 -->
<script type="text/javascript" src="../js/react.development.js"></script>
<!-- 引入 react-dom,用于支持 react 操作 DOM -->
<script type="text/javascript" src="../js/react-dom.development.js"></script>
<!-- 引入 babel,用于将 jsx 转为 js -->
<script type="text/javascript" src="../js/babel.min.js"></script>
<script type="text/babel"> /* 此处一定要写 babel */
/*
一定要注意区分:【js语句(代码)}】 与 【js表达式】
1.表达式:一个表达式会产生一个值,可以放在任何一个需求值的地方
下面这些都是表达式:
(1). a
(2). a+b
(3). demo(1)
(4). arr.map()
(5). function test () {}
2.语句(代码):
下面这些都是语句(代码):
(1).if(){}
(1).for(){}
(1).switch(){case:xxx}
*/
// 模拟一些数据
const data = ['Angular','React','Vue']
// 1.创建虚拟 DOM
const VDOM = (
<div>
<h1>demo</h1>
<ul>
{
data.map((item, index) => {
return <li key={index}>{item}</li>
})
}
</ul>
</div>
)
// 2.渲染虚拟DOM到页面
ReactDOM.render(VDOM,document.getElementById('test'))
</script>
</body>
</html>
3. 模块化与组件、模块化与组件化的理解
1. 模块
- 理解:向外提供特定功能的 JS 程序,一般就是一个 JS 文件
- 为什么要拆成模块:随着业务逻辑增加,代码越来越多且复杂
- 作用:复用 JS,简化 JS 的编写,提高 JS 运行效率
2. 组件
- 理解:用来实现局部功能效果的代码和资源的集合(html / css / js / image 等等)
- 为什么:一个界面的功能更复杂
- 作用:复用编码,简化项目编码,提高运行效率
3.模块化
当应用的 JS 都以模块化来编写的,这个应用就是一个模块化的应用
4.组件化
当应用是以多组件的方式实现,这个应用就是一个组件化的应用
三. React 面向组件编程
1. 基本理解和使用
1. 函数式组件
示例:
<!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>函数式组件</title>
</head>
<body>
<!-- 准备好一个容器 -->
<div id="test"></div>
<!-- 引入 react 核心库 -->
<script type="text/javascript" src="../js/react.development.js"></script>
<!-- 引入 react-dom,用于支持 react 操作 DOM -->
<script type="text/javascript" src="../js/react-dom.development.js"></script>
<!-- 引入 babel,用于将 jsx 转为 js -->
<script type="text/javascript" src="../js/babel.min.js"></script>
<script type="text/babel"> /* 此处一定要写 babel */
// 1.创建函数式组件
function MyComponent () {
console.log(this) //此处的this是undefined,因为babel编译后开启了严格模式
return <h2>我是用函数定义的组件(适用于【简单组件】的定义)</h2> //函数必须有返回值
}
// 2.渲染组件到页面
ReactDOM.render(<MyComponent/>,document.getElementById('test'))
/*
执行了ReactDOM.render(<MyComponent/>,document.getElementById('test'))之后发生了什么?
1.React解析组件标签,找到了MyComponent组件。
2.发现组件是使用函数定义的,随后调用该函数,将返回的虚拟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>
<script type="text/javascript">
/*
总结:
1.类中的构造器不是必须写的,要对实例进行一些初始化的操作,如添加指定属性时才写。
2.如果A类继承了B类,且A类中写了构造器,那么A类构造器中的super是必须要调用的。
3.类中所定义的方法,都是放在了类的原型对象上,供实例去使用。
*/
// 创建一个Person类
class Person {
// 构造器方法
constructor (name ,age) {
// 构造器中的this是谁? ———— 类的实例对象性
this.name = name
this.age = age
}
// 一般方法
speak () {
// speak 方法放在了哪里? ———— 类的原型对象上,供实例使用
// 通过Person实例调用speak时,speak中的this就是Person实例
console.log(`我叫${this.name},我年龄是${this.age}`)
}
}
// 创建一个Student类,继承与Person类
class Student extends Person {
constructor (name,age,grade) {
super(name,age)// super 必须放在最前面
this.grade = grade
this.school = 'xxx学院'
}
// 重写从父类继承过来的方法
speak () {
console.log(`我叫${this.name},我年龄是${this.age},我读的是${this.grade}`)
}
// study 方法放在了哪里? ———— 类的原型对象上,供实例使用
// 通过Person实例调用study时,study中的this就是Student实例
study () {
console.log('我很努力的学习')
}
}
const s1 = new Student('yzh',25,'大四')
console.log(s1)
s1.speak()
s1.study()
</script>
</body>
</html>
2.类式组件
<!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></title>
</head>
<body>
<!-- 准备好一个容器 -->
<div id="test"></div>
<!-- 引入 react 核心库 -->
<script type="text/javascript" src="../js/react.development.js"></script>
<!-- 引入 react-dom,用于支持 react 操作 DOM -->
<script type="text/javascript" src="../js/react-dom.development.js"></script>
<!-- 引入 babel,用于将 jsx 转为 js -->
<script type="text/javascript" src="../js/babel.min.js"></script>
<script type="text/babel"> /* 此处一定要写 babel */
// 1.创建类式组件
/*
注意:
1.必须继承父类
2.必须要写render
3.render必须有返回值
*/
class MyComponent extends React.Component {
render () {
//render是放在哪里的?———— MyComponent的原型对象上,供实例使用。
//render中的this是谁?———— MyComponent的实例对象 <=> MyComponent组件实例对象
console.log('render中的this',this)
return <h1>我是用类定义的组件(适用于【复杂组件】的定义)</h1>
}
}
// 2.渲染组件到页面
ReactDOM.render(<MyComponent/>,document.getElementById('test'))
/*
执行了ReactDOM.render(<MyComponent/>,document.getElementById('test'))之后发生了什么?
1.React解析组件标签,找到了MyComponent组件。
2.发现组件是使用类定义的,随后new出来该类的实例,并通过该实例调用到原型上的render方法。
3.将render返回的虚拟DOM转为真实DOM,随后呈现在页面中。
*/
</script>
</body>
</html>
2. 组件实例三大属性
1. state
- state是组件对象最重要的属性,值是对象(可以包含多个key-value的组合)
- 组件被称为“状态机”,通过更新组件的state来更新对应的页面显示(重新渲染组件)
强力注意:
1. 组件中render方法中的this为组件实例对象
2. 组件自定义的方法中this为undefined,如何解决?
- 强制绑定this:通过函数对象的bind()
- 箭头函数
3. 状态数据,不能直接修改或更新
基础标准写法示例如下:
<!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></title>
</head>
<body>
<!-- 准备好一个容器 -->
<div id="test"></div>
<!-- 引入 react 核心库 -->
<script type="text/javascript" src="../js/react.development.js"></script>
<!-- 引入 react-dom,用于支持 react 操作 DOM -->
<script type="text/javascript" src="../js/react-dom.development.js"></script>
<!-- 引入 babel,用于将 jsx 转为 js -->
<script type="text/javascript" src="../js/babel.min.js"></script>
<script type="text/babel"> /* 此处一定要写 babel */
// 1.创建组件
class Weather extends React.Component{
// 构造器调用几次? ———— 1次
constructor (props) {
console.log('constructor')
super(props)
// 初始化状态
this.state = {
isHot:true,
wind:'微风'
}
// 解决changeWeather中this只想问题
this.changeWeather = this.changeWeather.bind(this)
}
// render调用几次? ———— 1+n次 1是初始化的那次 n是状态更新的次数
render () {
console.log('render')
// 读取状态
const { isHot, wind } = this.state
return <h1 onClick={this.changeWeather}>今天天气很{isHot ? '炎热' : '寒冷' },{wind}</h1>
}
// changeWeather调用几次? ———— 点几次调几次
changeWeather () {
console.log('changeWeather')
// changeWeather放在哪里? ———— Weather的原型对象上,供实例使用
// 由于changWeather是作为onClick的回调,所以不是通过实例调用的,是直接调用
// 类中的方法默认开启了局部的严格模式,所以changeWeather中的this为undefined
// 获取原来的isHot值
const isHot = this.state.isHot
// 严重注意:状态必须通过setState进行更新,且更新是一种合并,不是替换
this.setState({
isHot:!isHot
})
// 严重注意:状态(state)不可直接更改,下面这行就是直接更改。
// this.state.isHot = !isHot //这是错误的写法
}
}
// 2.渲染组件到页面
ReactDOM.render(<Weather/>,document.getElementById('test'))
</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></title>
</head>
<body>
<!-- 准备好一个容器 -->
<div id="test"></div>
<!-- 引入 react 核心库 -->
<script type="text/javascript" src="../js/react.development.js"></script>
<!-- 引入 react-dom,用于支持 react 操作 DOM -->
<script type="text/javascript" src="../js/react-dom.development.js"></script>
<!-- 引入 babel,用于将 jsx 转为 js -->
<script type="text/javascript" src="../js/babel.min.js"></script>
<script type="text/babel"> /* 此处一定要写 babel */
// 1.创建组件
class Weather extends React.Component{
//初始化状态
state = { isHot:true, wind:'微风' }
render () {
const { isHot, wind } = this.state
return <h1 onClick={this.changeWeather}>今天天气很{isHot ? '炎热' : '寒冷' },{wind}</h1>
}
// 自定义方法 ———— 要用赋值语句的形式+箭头函数
changeWeather = () => {
const isHot = this.state.isHot
this.setState({ isHot:!isHot })
}
}
// 2.渲染组件到页面
ReactDOM.render(<Weather/>,document.getElementById('test'))
</script>
</body>
</html>
2. props
理解:
- 每个组件对象都会有props(properties的简写)属性
- 组件标签的所有属性都保存在props中
作用:
- 通过标签属性从组件外向组件内传递变化的数据
- 注意:组件内部不要修改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></title>
</head>
<body>
<!-- 准备好一个容器 -->
<div id="test1"></div>
<div id="test2"></div>
<div id="test3"></div>
<!-- 引入 react 核心库 -->
<script type="text/javascript" src="../js/react.development.js"></script>
<!-- 引入 react-dom,用于支持 react 操作 DOM -->
<script type="text/javascript" src="../js/react-dom.development.js"></script>
<!-- 引入 babel,用于将 jsx 转为 js -->
<script type="text/javascript" src="../js/babel.min.js"></script>
<!-- 引入prop-types,用于对组件标签属性进行限制 -->
<script type="text/javascript" src="../js/prop-types.js"></script>
<script type="text/babel"> /* 此处一定要写 babel */
// 1.创建组件
class Person extends React.Component{
render () {
console.log(this)
const { name, sex, age } = this.props
//props是只读的
// this.props.name = 'jack' //此行代码会报错,因为props是只读的
return (
<ul>
<li>姓名:{name}</li>
<li>性别:{sex}</li>
<li>年龄:{age+1}</li>
</ul>
)
}
}
// 对标签属性进行类型、必传的限制
Person.propTypes = {
name:PropTypes.string.isRequired,//限制name必传,且为字符串
sex:PropTypes.string,//限制sex为字符串
age:PropTypes.number,//限制age为字符串
speak:PropTypes.func//限制speak为函数
}
// 指定默认标签属性值
Person.defaultProps = {
sex:'男',//sex默认值为男
age:18//age默认值为18
}
// 2.渲染组件到页面
ReactDOM.render(<Person name="yzh" speak={speak}/>,document.getElementById('test1'))
ReactDOM.render(<Person name="yyy" sex="女" age={20}/>,document.getElementById('test2'))
const info = {name:'叶', sex:'男', age:25}
ReactDOM.render(<Person {...info}/>,document.getElementById('test3'))
function speak () {
console.log('123')
}
</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></title>
</head>
<body>
<!-- 准备好一个容器 -->
<div id="test1"></div>
<div id="test2"></div>
<div id="test3"></div>
<!-- 引入 react 核心库 -->
<script type="text/javascript" src="../js/react.development.js"></script>
<!-- 引入 react-dom,用于支持 react 操作 DOM -->
<script type="text/javascript" src="../js/react-dom.development.js"></script>
<!-- 引入 babel,用于将 jsx 转为 js -->
<script type="text/javascript" src="../js/babel.min.js"></script>
<!-- 引入prop-types,用于对组件标签属性进行限制 -->
<script type="text/javascript" src="../js/prop-types.js"></script>
<script type="text/babel"> /* 此处一定要写 babel */
// 1.创建组件
class Person extends React.Component{
// 对标签属性进行类型、必传的限制
static propTypes = {
name:PropTypes.string.isRequired,//限制name必传,且为字符串
sex:PropTypes.string,//限制sex为字符串
age:PropTypes.number,//限制age为字符串
speak:PropTypes.func//限制speak为函数
}
// 指定默认标签属性值
static defaultProps = {
sex:'男',//sex默认值为男
age:18//age默认值为18
}
render () {
console.log(this)
const { name, sex, age } = this.props
//props是只读的
// this.props.name = 'jack' //此行代码会报错,因为props是只读的
return (
<ul>
<li>姓名:{name}</li>
<li>性别:{sex}</li>
<li>年龄:{age+1}</li>
</ul>
)
}
}
// 2.渲染组件到页面
ReactDOM.render(<Person name="yzh" speak={speak}/>,document.getElementById('test1'))
ReactDOM.render(<Person name="yyy" sex="女" age={20}/>,document.getElementById('test2'))
const info = {name:'叶', sex:'男', age:25}
ReactDOM.render(<Person {...info}/>,document.getElementById('test3'))
function speak () {
console.log('123')
}
</script>
</body>
</html>
3. ref
理解:组件内的标签可以定义ref属性来标识自己
字符串形式的ref 示例代码如下:
// 1.创建组件
class Demo extends React.Component{
// 展示左侧输入框的数据
showData = () => {
const { input } = this.refs
alert(input.value)
}
// 展示右侧输入框的数据
showData2 = () => {
const { input2 } = this.refs
alert(input2.value)
}
render(){
return (
<div>
<input ref="input" type="text" placeholder="点击按钮提示数据"/>
<button type="button" onClick={this.showData}>点击提示左侧的数据</button>
<input ref="input2" onBlur={this.showData2} type="text" placeholder="失去焦点提示数据"/>
</div>
)
}
}
// 渲染组件到页面
ReactDOM.render(<Demo/>,document.getElementById('test'))
回调函数形式的ref 示例代码如下:
// 1.创建组件
class Demo extends React.Component{
// 展示左侧输入框的数据
showData = () => {
const { input1 } = this
alert(input1.value)
}
// 展示右侧输入框的数据
showData2 = () => {
const { input2 } = this
alert(input2.value)
}
render(){
return (
<div>
<input ref={e => this.input1 = e} type="text" placeholder="点击按钮提示数据"/>
<button type="button" onClick={this.showData}>点击提示左侧的数据</button>
<input ref={e => this.input2 = e} onBlur={this.showData2} type="text" placeholder="失去焦点提示数据"/>
</div>
)
}
}
// 渲染组件到页面
ReactDOM.render(<Demo/>,document.getElementById('test'))
回调ref中回调执行次数的问题 示例代码如下:
// 1.创建组件
class Demo extends React.Component{
state = {isHot:false}
showData = () => {
const { input1 } = this
alert(input1.value)
}
changeWeather = () => {
// 获取原来的状态
const { isHot } = this.state
// 更新状态
this.setState({isHot:!isHot})
}
saveInput = (e) => {
this.input1 = e
console.log('e',e)
}
render(){
const { isHot } = this.state
return (
<div>
<h1>今天天气{isHot ? '炎热':'凉爽'}</h1>
{/* <input ref={(e) => {this.input1 = e;console.log('e',e)}} type="text" placeholder="点击按钮提示数据"/> */}
<input ref={this.saveInput} type="text" placeholder="点击按钮提示数据"/>
<button type="button" onClick={this.showData}>点击提示左侧的数据</button>
<button type="button" onClick={this.changeWeather}>点击切换天气</button>
</div>
)
}
}
// 渲染组件到页面
ReactDOM.render(<Demo/>,document.getElementById('test'))
createRef 示例代码如下:
// 1.创建组件
class Demo extends React.Component{
/*
React.createRef调用后可以返回一个容器,该容器可以存储被ref所标识的节点,该容器是“专人专用”的
*/
myRef = React.createRef()
myRef2 = React.createRef()
// 展示左侧输入框的数据
showData = () => {
alert(this.myRef.current.value)
}
// 展示右侧输入框的数据
showData2 = () => {
alert(this.myRef2.current.value)
}
render(){
return (
<div>
<input ref={this.myRef} type="text" placeholder="点击按钮提示数据"/>
<button type="button" onClick={this.showData}>点击提示左侧的数据</button>
<input ref={this.myRef2} onBlur={this.showData2} type="text" placeholder="失去焦点提示数据"/>
</div>
)
}
}
// 渲染组件到页面
ReactDOM.render(<Demo/>,document.getElementById('test'))
3. 事件处理
1.通过onXxx属性指定事件处理函数(注意大小写)
- React使用的是自定义(合成)事件, 而不是使用的原生DOM事件 ———— 为了更好的兼容性
- React中的事件是通过事件委托方式处理的(委托给组件最外层的元素) ———— 为了高效
2.通过event.target得到发生事件的DOM元素对象 ———— 不要过度的使用ref
// 1.创建组件
class Demo extends React.Component{
/*
1.通过onXxx属性指定事件处理函数(注意大小写)
React使用的是自定义(合成)事件, 而不是使用的原生DOM事件 ———— 为了更好的兼容性
React中的事件是通过事件委托方式处理的(委托给组件最外层的元素) ———— 为了高效
2.通过event.target得到发生事件的DOM元素对象 ———— 不要过度的使用ref
*/
myRef = React.createRef()
myRef2 = React.createRef()
// 展示左侧输入框的数据
showData = () => {
alert(this.myRef.current.value)
}
// 展示右侧输入框的数据
showData2 = (event) => {
alert(event.target.value)
}
render(){
return (
<div>
<input ref={this.myRef} type="text" placeholder="点击按钮提示数据"/>
<button type="button" onClick={this.showData}>点击提示左侧的数据</button>
<input onBlur={this.showData2} type="text" placeholder="失去焦点提示数据"/>
</div>
)
}
}
// 渲染组件到页面
ReactDOM.render(<Demo/>,document.getElementById('test'))
4. 收集表单数据
包含表单的组件分类
- 受控组件
- 非受控组件
受控组件示例代码如下:
// 1.创建组件
class Login extends React.Component{
// 初始化状态
state = {
username:'',//用户名
password:''
}
// 保存用户名到状态中
saveUsername = (event) => {
this.setState({username:event.target.value})
}
// 保存密码到状态中
savePassword = (event) => {
this.setState({password:event.target.value})
}
// 表单提交的回调
handleSubmit = (event) => {
event.preventDefault()//阻止表单提交
const { username, password } = this.state
alert(`你输入的用户名是${username},你输入的密码是${password}`)
}
render(){
return (
<form onSubmit={this.handleSubmit}>
用户名:<input onChange={this.saveUsername} type="text" name="username"/>
密码:<input onChange={this.savePassword} type="password" name="password"/>
<button>登陆</button>
</form>
)
}
}
// 渲染组件到页面
ReactDOM.render(<Login/>,document.getElementById('test'))
非受控示例代码如下:
// 1.创建组件
class Login extends React.Component{
handleSubmit = (event) => {
event.preventDefault()//阻止表单提交
const { username, password } = this
alert(`你输入的用户名是${username.value},你输入的密码是${password.value}`)
}
render(){
return (
<form onSubmit={this.handleSubmit}>
用户名:<input ref={e => this.username = e} type="text" name="username"/>
密码:<input ref={e => this.password = e} type="password" name="password"/>
<button>登陆</button>
</form>
)
}
}
// 渲染组件到页面
ReactDOM.render(<Login/>,document.getElementById('test'))
5. 高阶函数和函数柯里化
高阶函数:如果一个函数符合下面2个规范中的任何一个,那该函数就是高阶函数
- 若A函数,接受的参数是一个函数,那么A就可以称之为高阶函数
- 若A函数,调用的返回值依然是一个函数,那么A就可以称之为高阶函数常见的高阶函数有:Promise、setTimeout、arr.map()等等
函数的柯里化:通过函数调用继续返回函数的方式,实现多次接收参数最后统一处理的函数编码形式。
示例代码如下:
<!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>高阶函数-函数柯里化</title>
</head>
<body>
<!-- 准备好一个容器 -->
<div id="test"></div>
<!-- 引入 react 核心库 -->
<script type="text/javascript" src="../js/react.development.js"></script>
<!-- 引入 react-dom,用于支持 react 操作 DOM -->
<script type="text/javascript" src="../js/react-dom.development.js"></script>
<!-- 引入 babel,用于将 jsx 转为 js -->
<script type="text/javascript" src="../js/babel.min.js"></script>
<script type="text/babel"> /* 此处一定要写 babel */
// 1.创建组件
class Login extends React.Component{
/*
高阶函数:如果一个函数符合下面2个规范中的任何一个,那该函数就是高阶函数
1.若A函数,接受的参数是一个函数,那么A就可以称之为高阶函数
2.若A函数,调用的返回值依然是一个函数,那么A就可以称之为高阶函数
常见的高阶函数有:Promise、setTimeout、arr.map()等等
函数的柯里化:通过函数调用继续返回函数的方式,实现多次接收参数最后统一处理的函数编码形式。
示例:
function sum(a){
return (b) => {
return (c) => {
return a+b+c
}
}
}
const result = sum(1)(2)(3)
console.log(result)
*/
// 初始化状态
state = {
username:'',//用户名
password:''
}
// 保存表单数据到状态中
saveFormData = (dataType) => {
return (event) => {
this.setState({[dataType]:event.target.value})
}
}
// 表单提交的回调
handleSubmit = (event) => {
event.preventDefault()//阻止表单提交
const { username, password } = this.state
alert(`你输入的用户名是${username},你输入的密码是${password}`)
}
render(){
return (
<form onSubmit={this.handleSubmit}>
用户名:<input onChange={this.saveFormData('username')} type="text" name="username"/>
密码:<input onChange={this.saveFormData('password')} type="password" name="password"/>
<button>登陆</button>
</form>
)
}
}
// 渲染组件到页面
ReactDOM.render(<Login/>,document.getElementById('test'))
</script>
</body>
</html>
6. 组件的生命周期
理解:
- 组件从创建到死亡它会经历一些特定的阶段。
- React组件中包含一系列钩子函数(生命周期回调函数),会在特定的时刻调用。
- 我们在定义组件时,会在特定的生命周期回调函数中做特定的工作。
生命周期的三个阶段(旧)
1.初始化阶段:由ReactDOM.render()触发--初次渲染
(1).constructor()
(2).componentWillMount()
(3).render()
(4).componentDidMount()
常用,一般在这个钩子中做一些初始化的事,例如:开启定时器、发送网络请求、订阅消息
2.更新阶段:由组件内部this.setState()或父组件新render触发
(1).shouldComponentUpdate()
(2).componentWillUpdate()
(3).render()
(4).componentDidUpdate()
3.卸载组件:由ReactDOM.unmountComponentAtNode()触发
(1).componentWillUnmount()
常用,一般在这个钩子中做一些收尾的事,例如:关闭定时器、取消订阅消息
<!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>react生命周期(旧)</title> </head> <body> <!-- 准备好一个容器 --> <div id="test"></div> <!-- 引入 react 核心库 --> <script type="text/javascript" src="../js/react.development.js"></script> <!-- 引入 react-dom,用于支持 react 操作 DOM --> <script type="text/javascript" src="../js/react-dom.development.js"></script> <!-- 引入 babel,用于将 jsx 转为 js --> <script type="text/javascript" src="../js/babel.min.js"></script> <script type="text/babel"> /* 此处一定要写 babel */ // 1.创建组件 class Count extends React.Component{ constructor(props){ console.log('count-constructor') super(props) // 初始化状态 this.state = {count:0} } // 加1按钮的回调 add = ()=> { // 获取原状态 const { count } = this.state // 更新状态 this.setState({count:count + 1}) } // 卸载组件按钮的回调 death = () => { ReactDOM.unmountComponentAtNode(document.getElementById('test')) } // 强制更新按钮的回调 force = () => { this.forceUpdate() } // 组件将要挂载的钩子 componentWillMount(){ console.log('count-componentWillMount') } //组件挂载完毕的钩子 componentDidMount(){ console.log('count-componentDidMount') } //组件将要卸载的钩子 componentWillUnmount(){ console.log('count-componentWillUnmount') } // 控制组件更新的阀门 shouldComponentUpdate(){ console.log('count-shouldComponentUpdate') return true } // 组件将要更新的钩子 componentWillUpdate(){ console.log('count-componentWillUpdate') } // 组件更新完毕的钩子 componentDidUpdate(){ console.log('count-componentDidUpdate') } render(){ console.log('render') const { count } = this.state return ( <div> <h2>当前求和为{count}</h2> <button onClick={this.add}>点我+1</button> <button onClick={this.death}>卸载组件</button> <button onClick={this.force}>不更改任何状态中的数据,强制更新一下</button> </div> ) } } class A extends React.Component{ // 初始化状态 state = {carName:'奔驰'} changeCar = () => { this.setState({carName:'宝马'}) } render(){ return ( <div> <div>我是A组件</div> <button onClick={this.changeCar}>换车</button> <B carName={this.state.carName}/> </div> ) } } class B extends React.Component{ // 组件将要接收新的props的钩子 componentWillReceiveProps(props){ console.log('B-componentWillReceiveProps',props) } // 控制组件更新的阀门 shouldComponentUpdate(){ console.log('B-shouldComponentUpdate') return true } // 组件将要更新的钩子 componentWillUpdate(){ console.log('B-componentWillUpdate') } // 组件更新完毕的钩子 componentDidUpdate(){ console.log('B-componentDidUpdate') } render(){ console.log('B-render') return ( <div>我是B组件,接收到的车是:{this.props.carName}</div> ) } } // 渲染组件到页面上 ReactDOM.render(<A/>,document.getElementById('test')) </script> </body> </html>
生命周期的三个阶段(新)
1.初始化阶段:由ReactDOM.render()触发--初次渲染
(1).constructor()
(2).getDerivedStateFromProps()
(3).render()
(4).componentDidMount()
常用,一般在这个钩子中做一些初始化的事,例如:开启定时器、发送网络请求、订阅消息
2.更新阶段:由组件内部this.setState()或父组件新render触发
(1).getDerivedStateFromProps()
(2).shouldComponentUpdate()
(3).render()
(4).getSnapshotBeforeUpdate()
(5).componentDidUpdate()
3.卸载组件:由ReactDOM.unmountComponentAtNode()触发
(1).componentWillUnmount()
常用,一般在这个钩子中做一些收尾的事,例如:关闭定时器、取消订阅消息
<!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>react生命周期(新)</title> </head> <body> <!-- 准备好一个容器 --> <div id="test"></div> <!-- 引入 react 核心库 --> <script type="text/javascript" src="../js/17.0.1/react.development.js"></script> <!-- 引入 react-dom,用于支持 react 操作 DOM --> <script type="text/javascript" src="../js/17.0.1/react-dom.development.js"></script> <!-- 引入 babel,用于将 jsx 转为 js --> <script type="text/javascript" src="../js/17.0.1/babel.min.js"></script> <script type="text/babel"> /* 此处一定要写 babel */ // 1.创建组件 class Count extends React.Component{ constructor(props){ console.log('count-constructor') super(props) // 初始化状态 this.state = {count:0} } // 加1按钮的回调 add = ()=> { // 获取原状态 const { count } = this.state // 更新状态 this.setState({count:count + 1}) } // 卸载组件按钮的回调 death = () => { ReactDOM.unmountComponentAtNode(document.getElementById('test')) } // 强制更新按钮的回调 force = () => { this.forceUpdate() } // 若state的值在任何时候都取决于props,那么可以使用getDerivedStateFromProps static getDerivedStateFromProps(props,state){ console.log('count-getDerivedStateFromProps',props,state) return null } // 在更新之前获取快照 getSnapshotBeforeUpdate(){ console.log('count-getSnapshotBeforecUpdate') return 'yzh' } //组件挂载完毕的钩子 componentDidMount(){ console.log('count-componentDidMount') } //组件将要卸载的钩子 componentWillUnmount(){ console.log('count-componentWillUnmount') } // 控制组件更新的阀门 shouldComponentUpdate(){ console.log('count-shouldComponentUpdate') return true } // 组件更新完毕的钩子 componentDidUpdate(preProps,preState,snapshotValue){ console.log('count-componentDidUpdate',preProps,preState,snapshotValue) } render(){ console.log('render') const { count } = this.state return ( <div> <h2>当前求和为{count}</h2> <button onClick={this.add}>点我+1</button> <button onClick={this.death}>卸载组件</button> <button onClick={this.force}>不更改任何状态中的数据,强制更新一下</button> </div> ) } } // 渲染组件到页面上 ReactDOM.render(<Count count={199}/>,document.getElementById('test')) </script> </body> </html>
重要的钩子:
- render:初始化渲染或更新渲染调用
- componentDidMount:开启监听,发送ajax请求
- componentWillUnmount:做一些收尾的工作,如:清理定时器
即将废弃的钩子:
- componentWillMount
- componentWillReceiveProps
- componentWillUpdate
现在使用会出现警告,下一个大版本需要加上UNSAFE_前缀才能使用,以后可能会被彻底废弃,不建议使用。
7. key的作用
经典面试题:
1). react/vue中的key有什么作用?(key的内部原理是什么?)
2). 为什么遍历列表时,key最好不要用index?
1. 虚拟DOM中key的作用:
1). 简单的说: key是虚拟DOM对象的标识, 在更新显示时key起着极其重要的作用。
2). 详细的说:
当状态中的数据发生变化时,react会根据【新数据】生成【新的虚拟DOM】,
随后React进行【新虚拟DOM】与【旧虚拟DOM】的diff比较,比较规则如下:
a. 旧虚拟DOM中找到了与新虚拟DOM相同的key:
(1).若虚拟DOM中内容没变, 直接使用之前的真实DOM
(2).若虚拟DOM中内容变了, 则生成新的真实DOM,随后替换掉页面中之前的真实DOM
b. 旧虚拟DOM中未找到与新虚拟DOM相同的key,根据数据创建新的真实DOM,随后渲染到到页面
2. 用index作为key可能会引发的问题:
1). 若对数据进行:逆序添加、逆序删除等破坏顺序操作:
会产生没有必要的真实DOM更新 ==> 界面效果没问题, 但效率低。
2). 如果结构中还包含输入类的DOM:
会产生错误DOM更新 ==> 界面有问题。
3). 注意!如果不存在对数据的逆序添加、逆序删除等破坏顺序操作,
仅用于渲染列表用于展示,使用index作为key是没有问题的。
3. 开发中如何选择key?:
1).最好使用每条数据的唯一标识作为key, 比如id、手机号、身份证号、学号等唯一值。
2).如果确定只是简单的展示数据,用index也是可以的。
示例代码如下:
<!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>key的作用</title>
</head>
<body>
<!-- 准备好一个容器 -->
<div id="test"></div>
<!-- 引入 react 核心库 -->
<script type="text/javascript" src="../js/17.0.1/react.development.js"></script>
<!-- 引入 react-dom,用于支持 react 操作 DOM -->
<script type="text/javascript" src="../js/17.0.1/react-dom.development.js"></script>
<!-- 引入 babel,用于将 jsx 转为 js -->
<script type="text/javascript" src="../js/17.0.1/babel.min.js"></script>
<script type="text/babel"> /* 此处一定要写 babel */
/*
慢动作回放----使用index索引值作为key
初始数据:
{id:1,name:'小张',age:18},
{id:2,name:'小李',age:19},
初始的虚拟DOM:
<li key=0>小张---18<input type="text"/></li>
<li key=1>小李---19<input type="text"/></li>
更新后的数据:
{id:3,name:'小王',age:20},
{id:1,name:'小张',age:18},
{id:2,name:'小李',age:19},
更新数据后的虚拟DOM:
<li key=0>小王---20<input type="text"/></li>
<li key=1>小张---18<input type="text"/></li>
<li key=2>小李---19<input type="text"/></li>
-----------------------------------------------------------------
慢动作回放----使用id唯一标识作为key
初始数据:
{id:1,name:'小张',age:18},
{id:2,name:'小李',age:19},
初始的虚拟DOM:
<li key=1>小张---18<input type="text"/></li>
<li key=2>小李---19<input type="text"/></li>
更新后的数据:
{id:3,name:'小王',age:20},
{id:1,name:'小张',age:18},
{id:2,name:'小李',age:19},
更新数据后的虚拟DOM:
<li key=3>小王---20<input type="text"/></li>
<li key=1>小张---18<input type="text"/></li>
<li key=2>小李---19<input type="text"/></li>
*/
class Person extends React.Component{
state = {
persons:[
{id:1,name:'小张',age:18},
{id:2,name:'小李',age:19},
]
}
add = () => {
const { persons } = this.state
const p = {id:persons.length + 1, name:'小王', age:20}
this.setState({persons:[...persons,p]})
}
render(){
return (
<div>
<h1>展示人员信息</h1>
<button onClick={this.add}>添加一个小王</button>
<h1>使用index(索引值)作为key</h1>
<ul>
{
this.state.persons.map((personObj,index) => {
return <li key={index}>{personObj.name}---{personObj.age}<input type="text"/></li>
})
}
</ul>
<hr/>
<h1>使用id(数据的唯一标识)作为key</h1>
<ul>
{
this.state.persons.map((personObj,index) => {
return <li key={personObj.id}>{personObj.name}---{personObj.age}<input type="text"/>- </li>
})
}
</ul>
</div>
)
}
}
ReactDOM.render(<Person/>,document.getElementById('test'))
</script>
</body>
</html>