一、React 面向组件编程
1.1、函数式组件(用函数定义的组件( 适用于 [ 简单组件 ] 的定义 ))
<!DOCTYPE html>
<html>
<head>
<title>函数式组件</title>
<!-- 引入React核心库 -->
<script src="https://unpkg.com/react@16/umd/react.development.js"></script>
<!-- 引入 react-dom: 用来支持 react 的dom操作(渲染) -->
<script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
<!-- 引入 babel:用来将引入 babel:用来将jsx语法转换成js -->
<script src="https://unpkg.com/babel-standalone@6.15.0/babel.min.js"></script>
</head>
<body>
<!-- 准备好一个容器 -->
<div id="app"></div>
<!-- script的类型必须是text/babel -->
<script type="text/babel">
// 创建函数式组件
function MyComponents(){
console.log(this); // 此处的this是undefined,因为babe1编译后开启了严格模式
return <h1>我是用函数定义的组件(适用于[简单组件]的定义)</h1>
}
// 渲染组件到页面
ReactDOM.render(<MyComponents />,document.getElementById("app"));
/*
执行了ReactDOM. render( <MyComponent/>......之后,发生了什么?
1. React解析组件标签,找到了MyComponent组件。
2.发现组件是使用函数定义的,随后调用该函数,将返回的虚拟DOM转为真实DOM,随后呈现在页面中。
*/
</script>
</body>
</html>
1.2、类式组件(用类定义的组件( 适用于 [ 复杂组件 ] 的定义 ))
<!DOCTYPE html>
<html>
<head>
<title>函数式组件</title>
<!-- 引入React核心库 -->
<script src="https://unpkg.com/react@16/umd/react.development.js"></script>
<!-- 引入 react-dom: 用来支持 react 的dom操作(渲染) -->
<script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
<!-- 引入 babel:用来将引入 babel:用来将jsx语法转换成js -->
<script src="https://unpkg.com/babel-standalone@6.15.0/babel.min.js"></script>
</head>
<body>
<!-- 准备好一个容器 -->
<div id="app"></div>
<!-- script的类型必须是text/babel -->
<script type="text/babel">
// 创建类式组件
class MyComponents extends React.Component {
//render是放在哪里的?—— MyComponent的原型对象上,供实例使用。
//render中的this是谁?一 MyComponent的实例对象。
render(){
console. log(' render中的this:' ,this);
return <h1>我是用类定义的组件(适用于[复杂组件]的定义)</h1>
}
}
// 渲染组件到页面
ReactDOM.render(<MyComponents />,document.getElementById("app"));
/*
执行了ReactDOM. render( <MyComponent/>......之后,发生了什么?
1. React解析组件标签,找到了MyComponent组件。
2.发现组件是使用类定义的,随后new出来该类的实例,并通过该实例调用到原型上的render方法。
3.将render返回的虚拟DOM转为真实DOM,随后呈现在页面中。
*/
</script>
</body>
</html>
简单组件和复杂组件的区别
简单组件:接收输入的数据并返回需要展示的内容(通过 this.props 访问)。
复杂组件:除了使用外部数据(通过 this.props 访问)以外,组件还可以维护其内部的状态数据(通过 this.state 访问)。当组件的状态数据改变时,组件会再次调用 render() 方法重新渲染对应的标记。
事件绑定
React 元素的事件处理和 DOM 元素的很相似,但是有一点语法上的不同:
React 事件的命名采用小驼峰式(camelCase),而不是纯小写。
使用 JSX 语法时你需要传入一个函数作为事件处理函数,而不是一个字符串。
例如,传统的 HTML:
<button onclick="activateLasers()">
Activate Lasers
</button>
在 React 中略微不同:
<button onClick={activateLasers}>
Activate Lasers
</button>
注意一:React中的事件绑定只需要传参数名就行,一定不能加上(),加上()的函数相当于函数调用,在创建组件实例的时候就会执行,然后会默认返回一个undefined赋给onClick,当你点击标签的时候由于事件绑定的是undefined,会导致绑定的事件调用不成功
<!DOCTYPE html>
<html>
<head>
<title>react事件绑定</title>
<!-- 引入React核心库 -->
<script src="https://unpkg.com/react@16/umd/react.development.js"></script>
<!-- 引入 react-dom: 用来支持 react 的dom操作(渲染) -->
<script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
<!-- 引入 babel:用来将引入 babel:用来将jsx语法转换成js -->
<script src="https://unpkg.com/babel-standalone@6.15.0/babel.min.js"></script>
</head>
<body>
<!-- 准备好一个容器 -->
<div id="app"></div>
<!-- script的类型必须是text/babel -->
<script type="text/babel">
// 创建类式组件
class Weahter extends React.Component {
constructor(props){
super(props);
}
render(){
// return <h1 onClick={this.changeWeahte()}>事件绑定</h1>//错误写法,不应该写(),写上()是调用函数,而不是绑定函数了
return <h1 onClick={this.changeWeahter}>事件绑定</h1>//正确写法
}
changeWeahter(){
console.log("事件调用成功");
}
}
// 渲染组件到页面
ReactDOM.render(<Weahter />,document.getElementById("app"));
</script>
</body>
</html>
注意二:事件绑定中函数 this 为 undefined问题
原因:
class 中的方法在 class 的原型对象上,供实例使用
而 class 中的方法是作为onclick的回调,所以不是通过实例调用的,是直接调用(直接调用this指向window)
因为 class 中的方法默认开启了局部的严格模式(严格模式禁止 this 指向 window ),所以 方法中的 this 为 undefined
解决方法:
一、强制绑定this: 通过函数对象的bind();
二、箭头函数(常用)
<script type="text/babel">
// 创建类式组件
class Weahter extends React.Component {
constructor(props){
super(props);
//解决changewleather中this指向问题(解决方法 一)
// this.changeWeahter = this.changeWeahter.bind(this);
}
render(){
// return <h1 onClick={this.changeWeahter}>事件绑定</h1>//这样写 changeWeahter 中的this 为undefined
return <h1 onClick={() => {this.changeWeahter()}}>事件绑定</h1>//(解决方法 二)
}
changeWeahter(){
// changeweather放在哪里?—— Weather的原型对象上,供实例使用
// 由于changeweather是作为onclick的回调,所以不是通过实例调用的,是直接调用(直接调用this指向window)
// 类中的方法默认开启了局部的严格模式(严格模式禁止this指向window),所以changeleather中的this为undefined
console.log("this",this);
}
}
// 渲染组件到页面
ReactDOM.render(<Weahter />,document.getElementById("app"));
</script>
一、React 组件三大核心属性
1、组件三大核心属性 state
React 把组件看成是一个状态机(State Machines)。通过与用户的交互,实现不同状态,然后渲染 UI,让用户界面和数据保持一致。
React 里,只需更新组件的 state,然后根据新的 state 重新渲染用户界面(不要操作 DOM)。
理解:
1、state是组件对象最重要的属性, 值是对象(可以包含多个key-value的组合)
2、组件被称为"状态机", 通过更新组件的state来更新对应的页面显示(重新渲染组件)
需求:点击h1标签切换今天天气炎热凉爽
<script type="text/babel">
// 创建类式组件
class Weahter extends React.Component {
constructor(props){
super(props);
//初始化状态
this.state = {
isHot: false,
};
}
render(){
return <h1 onClick={() => {this.changeWeahter}}>今天天气很{this.state.isHot ? "炎热" : "凉爽"}</h1>
}
changeWeahter(){
const { isHot } = this.state;
// 严重注意:状态(state)不可直接更改,下面这行就是直接更改!!!
// this.state.isHot = !this.state.isHot;
// 严重注意:状态必须通过setState进行更新,且更新是一种合并,不是替换。
this.setState({
isHot: !isHot
})
}
}
// 渲染组件到页面
ReactDOM.render(<Weahter />,document.getElementById("app"));
</script>
简写
<script type="text/babel">
// 创建类式组件
class Weahter extends React.Component {
//初始化状态
state = {
isHot: false,
};
render(){
return <h1 onClick={this.changeWeahter}>今天天气很{this.state.isHot ? "炎热" : "凉爽"}</h1>
}
//自定义方法——要用赋值语句的形式+箭头函数
changeWeahter = ()=>{
const { isHot } = this.state;
this.setState({
isHot: !isHot
})
}
}
// 渲染组件到页面
ReactDOM.render(<Weahter />,document.getElementById("app"));
</script>
2、组件三大核心属性 props
2.1、官网定义
组件,从概念上类似于 JavaScript 函数。它接受任意的入参(即 “props”),并返回用于描述页面展示内容的 React 元素。
state 和 props 主要的区别在于 props 是不可变的,而 state 可以根据与用户交互来改变。这就是为什么有些容器组件需要定义 state 来更新和修改数据。 而子组件只能通过 props 来传递数据
2.1. 理解
1.每个组件对象都会有props(properties的简写)属性
2.组件标签的所有属性都保存在props中
2.2. 作用
1.通过标签属性从组件外向组件内传递变化的数据
2.注意: 组件内部不要修改props数据
<!DOCTYPE html>
<html>
<head>
<title>05_props简写方式</title>
<!-- 引入React核心库 -->
<script src="https://unpkg.com/react@16/umd/react.development.js"></script>
<!-- 引入 react-dom: 用来支持 react 的dom操作(渲染) -->
<script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
<!-- 引入 babel:用来将引入 babel:用来将jsx语法转换成js -->
<script src="https://unpkg.com/babel-standalone@6.15.0/babel.min.js"></script>
<script src="https://cdn.bootcdn.net/ajax/libs/prop-types/15.7.2/prop-types.js"></script>
</head>
<body>
<!-- 准备好一个容器 -->
<div id="app"></div>
<div id="app2"></div>
<!-- script的类型必须是text/babel -->
<script type="text/babel">
// 创建类式组件
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: "人妖",
age: 18
}
constructor(props){
//构造器是否接受props,是否传递给super,取决于:是否希望在构造器中通过this访问props
super(props);
console.log("constructor",this.props);
}
render(){
// props是只读的
// this.props.name = "Jack";//慈航代码报错,因为props是只读的
const {name, sex, age} = this.props;
return (
<ul>
<li>姓名:{ name }</li>
<li>性别:{ sex }</li>
<li>年龄:{ age + 1 }</li>
</ul>
)
}
}
// 渲染组件到页面
ReactDOM.render(<Person name="LBipanda" sex="男" />,document.getElementById("app"));
</script>
</body>
</html>
3、组件三大核心属性 refs 与事件处理
理解
组件内的标签可以定义ref属性来标识自己
3.2、编码**
- 字符串形式的ref
<input ref="input1" type="text"/>
- 回调形式的ref
<input ref={c => {this.input1 = c}} type="text" />
- createRef创建ref容器·
myRef = React.createRef()
<input ref={this.myRef} type="text"/>
ref常用方式
script type="text/babel">
// 创建类式组件
class Person extends React.Component {
render(){
return (
<div>
<input ref={c => {this.input1 = c}} type="text" placehodel="点击展示左侧输入框的数据"/>
<button onClick={this.showData}>点击展示左侧输入框的数据</button>
</div>
)
}
showData = ()=>{
console.log(this);
let { input1 } = this;
alert(input1.value)
}
}
// 渲染组件到页面
ReactDOM.render(<Person />,document.getElementById("app"));
</script>
事件处理
1.通过onXxx属性指定事件处理函数(注意大小写)
(1)React使用的是自定义(合成)事件, 而不是使用的原生DOM事件
(2)React中的事件是通过事件委托方式处理的(委托给组件最外层的元素)
2.通过event.target得到发生事件的DOM元素对象
二、React 受控组件和非受控组件
受控组件就是可以被 react 状态(state)控制的组件
在 react 中,Input textarea 等组件默认是非受控组件(输入框内部的值是用户控制,和React无关,无法直接获取到值,需通过 ref 获取)。但是也可以转化成受控组件,就是通过 onChange 事件获取当前输入内容,将当前输入内容保存到组件的 state 属性中,此时就成为受控组件。
非受控组件:输入框内部的值是用户控制,和React无关,无法直接获取到值,需通过 ref 获取
<script type="text/babel">
// 创建类式组件
class Login extends React.Component {
render(){
return (
<form action="http://www.atguigu.com" onSubmit={this.submit}>
用户名:<input type="text" ref={c => this.inputName = c} name="username" /><br/>
密 码:<input type="password" ref={c => this.inputPass = c} name="password" /><br/>
<button>登录</button>
</form>
)
}
submit = (event) => {
event.preventDefault();//阻止表单提交
//通过 refs 获取
alert(`你输入的用户名是${this.inputName.value},密码是${this.inputPass.value}`)
}
}
// 渲染组件到页面
ReactDOM.render(<Login />,document.getElementById("app"));
</script>
受控组件:通过 onChange 事件获取当前输入内容,将当前输入内容保存到组件的 state 属性中
<script type="text/babel">
// 创建类式组件
class Login extends React.Component {
// 初始化状态
state = {}
render(){
return (
<form action="http://www.atguigu.com" onSubmit={this.submit}>
用户名:<input type="text" onChange={this.changeName} name="username" /><br/>
密 码:<input type="password" onChange={this.changePass} name="password" /><br/>
<button>登录</button>
</form>
)
}
submit = (event) => {
console.log();
event.preventDefault();//阻止表单提交
//通过 state 获取
alert(`你输入的用户名是${this.state.username},密码是${this.state.password}`)
}
//保存用户名到状态中
changeName = (event) => {
this.setState({username: event.target.value})
}
//保存密码到状态中
changePass = (event) => {
this.setState({password: event.target.value})
}
}
// 渲染组件到页面
ReactDOM.render(<Login />,document.getElementById("app"));
</script>
像上面这种受控组件有点代码冗余,一个输入框我就要调一个方法,如果有多个输入框的值需要保存在state。我们需要通过高阶函数来解决代码冗余
高阶函数:如果一个函数符合下面2个规范中的任何一个,那该函数就是高阶函数。
1.若A函数,接收的参数是一个函数,那么A就可以称之为高阶函数。
2.若A函数,调用的返回值依然是一个函数,那么A就可以称之为高阶函数。
常见的高阶函数有:Promise、 setTimeout、arr.map()等等
函数的柯里化: -通过函数调用继续返回函数的方式,实现多次接收参数最后统一处理的函数编码形式。
第一种方式(函数返回一个回调函数:个人建议使用第二种方式)
<script type="text/babel">
// 创建类式组件
class Login extends React.Component {
render(){
return (
<form action="http://www.atguigu.com" onSubmit={this.submit}>
用户名:<input type="text" onChange={this.saveFormData("username")} name="username" /><br/>
密 码:<input type="password" onChange={this.saveFormData("password")} name="password" /><br/>
<button>登录</button>
</form>
)
}
submit = (event) => {
event.preventDefault();//阻止表单提交
alert(`你输入的用户名是${this.state.username},密码是${this.state.password}`)
}
//保存表单数据到状态中
saveFormData = (dataType) =>{
// console.log(dataType);
return (event)=>{//返回一个回调函数
console.log(event.target.value);
this.setState({[dataType]: event.target.value})
}
}
}
// 渲染组件到页面
ReactDOM.render(<Login />,document.getElementById("app"));
</script>
第二种方式
<script type="text/babel">
// 创建类式组件
class Login extends React.Component {
render(){
return (
<form action="http://www.atguigu.com" onSubmit={this.submit}>
用户名:<input type="text" onChange={(event) => {this.saveFormData("username",event)}} name="username" /><br/>
密 码:<input type="password" onChange={event => this.saveFormData("password",event)} name="password" /><br/>
<button>登录</button>
</form>
)
}
submit = (event) => {
event.preventDefault();//阻止表单提交
alert(`你输入的用户名是${this.state.username},密码是${this.state.password}`)
}
//保存表单数据到状态中
saveFormData = (dataType,event) =>{
// console.log(dataType);
console.log(event.target.value);
this.setState({[dataType]: event.target.value})
}
}
// 渲染组件到页面
ReactDOM.render(<Login />,document.getElementById("app"));
</script>
三、React 生命周期
3.1、生命周期流程图(旧)
3.2、生命周期的三个阶段(旧)
- 初始化阶段: 由ReactDOM.render()触发---初次渲染
1.constructor()
2.componentWillMount()
3.render()====> 必须使用的一个
4.componentDidMount() ====> 常用
componentDidMount:一般在这个钩子中做一些初始化的事,例如:开启定时器、发送网络请求、订阅消息
- 更新阶段: 由组件内部this.setSate()或父组件重新render触发
1.shouldComponentUpdate()
2.componentWillUpdate()
3.render()
4.componentDidUpdate() - 卸载组件: 由ReactDOM.unmountComponentAtNode()触发
1.componentWillUnmount() ====> 常用
componentWillUnmount:一般在这个钩子中做一些收尾的事,例如:关闭定时器、取消订阅消息
3.3、生命周期流程图(新)
3.4、生命周期的三个阶段(新)
初始化阶段: 由ReactDOM.render()触发---初次渲染
1.constructor()
2.getDerivedStateFromProps
3.render()
4.componentDidMount()更新阶段: 由组件内部this.setSate()或父组件重新render触发
1.getDerivedStateFromProps
2.shouldComponentUpdate()
3.render()
4.getSnapshotBeforeUpdate
5.componentDidUpdate()卸载组件: 由ReactDOM.unmountComponentAtNode()触发
1.componentWillUnmount()
重要的勾子
1.render:初始化渲染或更新渲染调用
2.componentDidMount:开启监听, 发送ajax请求
3.componentWillUnmount:做一些收尾工作, 如: 清理定时器
即将废弃的勾子
1.componentWillMount
2.componentWillReceiveProps
3.componentWillUpdate
现在使用会出现警告,下一个大版本需要加上UNSAFE_前缀才能使用,以后可能会被彻底废弃,不建议使用。
三、虚拟DOM与DOM Diffing算法