React

  1. 1. React 入门
    1. 1.1. React 简介
    2. 1.2. React JSX
  2. 2. 面向组件编程
    1. 2.1. 组件的定义
      1. 2.1.1. 函数式组件
      2. 2.1.2. 类式组件
    2. 2.2. 组件实例三大属性
      1. 2.2.1. state
      2. 2.2.2. props
        1. 2.2.2.1. 类式组件中的使用
        2. 2.2.2.2. 函数式组件中的使用
      3. 2.2.3. refs
        1. 2.2.3.1. 回调形式的refs
        2. 2.2.3.2. createRef形式的refs
    3. 2.3. 事件处理
    4. 2.4. 表单的组件分类
      1. 2.4.1. 受控组件
      2. 2.4.2. 非受控组件
    5. 2.5. 组件声明周期
  3. 3. React-Router
  4. 4. PubSub
  5. 5. Ant-Design
  6. 6. 参考

React - The library for web and native user interfaces

React 入门

React 简介

React 是一个将数据渲染为 HTML 视图的开源 JavaScript 库,由 facebook 公司开发并开源

为什么要学习 React?

  1. 原生 JavaScript 操作 DOM 繁琐,效率低(DOM-API 操作 UI
1
2
3
document.getElementById('app')
document.querySelector('#app')
document.getElementsByTagName('span')
  1. 使用 JavaScript 直接操作 DOM,浏览器会进行大量的重绘重排
  2. 原生 JavaScript 没有组件化编码方案,代码复用率低

React 的特点

  1. 采用组件化模式,声明式编码,提高开发效率及组件复用率
  2. 在 React Native 中可以使用 React 语法进行移动端开发
  3. 使用虚拟DOM和优秀的Diffing 算法,尽量减少与真实 DOM 的交互
虚拟DOM
  1. 本质是 Object 类型的对象(一般对象)
  2. 虚拟 DOM 比较轻量级,真实 DOM 比较庞大,因为虚拟 DOM 是 React 内部在用,无需真实 DOM 上那么多属性
  3. 虚拟 DOM 最终会被 React 转换为真实 DOM,呈现在页面上

学习 React 前需要掌握的知识

  • 判断 this 的指向
  • Class
  • ES6 语法规范
  • npm 包管理器
  • 原型,原型链
  • 数组常用方法
  • 模块化

React JSX

JSX 全称 JavaScript XML,是一种语法糖,可以在 JavaScript 中编写 HTML 代码

JSX 语法规则
  1. 定义虚拟 DOM 时不要写引号
  2. 标签中混入 JS 表达式时要用 {}
  3. 样式的类名指定不要用 class,要用 className
  4. 内敛样式,要用双大括号的形式去写
  5. 虚拟 DOM 必须只有一个根标签
  6. 标签必须闭合,自闭合也可以
  7. 标签首字母
    1. 如果小写字母开头,则将该标签转为 HTML 中同名元素,若 HTML 中无该标签对应的同名元素,则报错
    2. 如果大写字母开头,React 就去渲染对应的组件,若组件没有定义,则报错
1
2
// 内敛样式
style={{color:'skyblue',fontSize:'24px'}}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 根据动态数据生成 li 列表
const data = ['A','B','C']
// 创建虚拟 DOM
const VDOM = (
<div>
<ul>
{
data.map((item,index)=>{
return <li key={index}>{item}</li>
})
}
</ul>
</div>
)
// 渲染虚拟 DOM 到页面
ReactDOM.render(VDOM,document.querySelector('.test'))

面向组件编程

渲染类组件标签的基本流程

  1. React 内部会创建组件实例对象
  2. 调用 render() 得到虚拟 DOM,并解析为真实 DOM
  3. 插入到指定的页面元素内部

组件的定义

函数式组件

1
2
3
4
5
6
// 1. 先创建函数,函数可以有参数,也可以没有,但是必须要有返回值 返回一个虚拟 DOM
function MyComponent(props) {
return <h1>Hello, {props.name}</h1>;
}
// 2. 渲染组件到页面
ReactDOM.Render(<MyComponent name = "world" />,document.getElementById("div"));

类式组件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 1. 创建类式组件
class MyComponent extend React.Component {
render() {
// render 是放在 MyComponent 原型对象上的,供示例使用
// remder 中的 this 是当前组件的实例对象
return <h1>Hello, {this.props.name}</h1>;
}
}

// 2. 渲染组件到页面
ReactDOM.Render(<MyComponent name = "world" />,document.getElementById("div"));
/*
执行了 ReactDOM.Render 方法后,React 解析组件标签,找到了 MyComponent 组件,并且发现组件是使用类定义的,随后 new 出 MyComponent 类的实例,并且调用了实例的 render 方法,将 render 方法的返回的虚拟 DOM 转为真实 DOM,随后渲染到页面中
*/

组件实例三大属性

state

state

React 把组件看成是一个状态机(State Machines)。通过与用户的交互,实现不同状态,然后渲染 UI,让用户界面和数据保持一致

React 里,只需更新组件的 state,然后根据新的 state 重新渲染用户界面(不要操作 DOM)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
// 1. 创建组件
class Weather extends React.Component {
// 调用 1 次
constructor(props){
super(props);
// 初始化状态
this.state = {
isHot: false,
wind: "微风",
};
// 解决 changeWeather 中 this 指向问题
this.changeWeather = this.changeWeather.bind(this);
}
// 调用 1 + N 次
render() {
// 读取状态
const { isHot, wind } = this.state;
return <h1 onClick={this.changeWeather}>今天天气很{isHot ? "炎热" : "凉爽"}, {wind}</h1>;
}

// changeWeather 放在 Weather 原型对象上,供实例使用
// 由于 changeWeather 是作为 onClick 的回调函数,所以不是通过实例调用,而是直接调用
// 由于类中的方法默认开启了局部严格模式,所以 changeWeather 中的 this 指向 undefined
// 调用 N 次
changeWeather(){
// 获取原来的 isHot 值
const isHot = this.state.isHot;
// 严重注意:状态必须通过 setState() 来更新,且更新是一种合并,不是替换
this.setState({
isHot: !isHot,
});
}
}

// 2. 渲染组件到页面
ReactDOM.Render(<Weather />,document.getElementById("div"));

在优化过程中遇到的问题

  1. 组件中的 render 方法中的 this 为组件实例对象
  2. 组件自定义方法中由于开启了严格模式,this 指向 undefined 如何解决
    1. 通过 bind 改变 this 指向
    2. 推荐采用箭头函数,箭头函数的 this 指向
  3. state 数据不能直接修改或者更新

优化后:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// 1. 创建组件
class Weather extends React.Component {
// 初始化状态
state = {
isHot: false,
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("div"));

props

state 不同,state 是组件自身的状态,而 props 则是外部传入的数据

类式组件中的使用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 1. 创建组件
class Person extends React.Component {
render(){
// 也可以这样获取
// const {name,age,sex} = this.props
return (
<ul>
<li>{this.props.name}</li>
<li>{this.props.age}</li>
<li>{this.props.sex}</li>
</ul>
)
}
}

// 2. 渲染组件到页面
ReactDOM.Render(<Person name="zhang" age="20" sex="male" />,document.getElementById("div"));
props

在使用的时候可以通过 this.props 来获取值类式组件的 props:

  1. 通过在组件标签上传递值,在组件中就可以获取到所传递的值
  2. 在构造器里的 props 参数里可以获取到 props
  3. 可以分别设置 propTypesdefaultProps 两个属性来分别操作 props 的规范和默认值,两者都是直接添加在类式组件的原型对象上的(所以需要添加 static
  4. 同时可以通过 ... 运算符来简化
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
// 1. 创建组件
class Person extends React.Component {
// // 通常构造器可以不写
// constructor(props){
// // 构造器是否接受 props,是否传递给 super,取决于是否希望在构造器中使用 this.props
// // console.log(props);
// super(props);
// // console.log("constructor",this.props);
// }

// 对标签属性进行类型、必要性的限制
static propTypes = {
name:PropTypes.string.isRequired, // 限制 name 属性必须是字符串,且必须传递
age:PropTypes.number, // 限制 age 属性必须是数值
sex:PropTypes.string, // 限制 sex 属性必须是字符串
}

// 指定默认标签属性值
static defaultProps = {
age: 18, // age 默认值为 18
sex: 'male', // sex 默认值为男
}

render(){
return (
<ul>
<li>{this.props.name}</li>
<li>{this.props.age}</li>
<li>{this.props.sex}</li>
</ul>
)
}
}

// 2. 渲染组件到页面
const p = {name="zhang",age="20",sex="male"}
ReactDOM.Render(<Person {...p} />,document.getElementById("div"));
函数式组件中的使用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
// 1. 创建组件
function Person (props) {
render(){
const {name,age,sex} = props
return (
<ul>
<li>{name}</li>
<li>{age}</li>
<li>{sex}</li>
</ul>
)
}
// 对标签属性进行类型、必要性的限制
Person.propTypes = {
name:PropTypes.string.isRequired, // 限制 name 属性必须是字符串,且必须传递
age:PropTypes.number, // 限制 age 属性必须是数值
sex:PropTypes.string, // 限制 sex 属性必须是字符串
}

// 指定默认标签属性值
Person.defaultProps = {
age: 18, // age 默认值为 18
sex: 'male', // sex 默认值为男
}
}

// 2. 渲染组件到页面
ReactDOM.Render(<Person name="zhang" sex="male", age={20} />,document.getElementById("div"));

refs

Refs 提供了一种方式,允许我们访问 DOM 节点或在 render 方法中创建的 React 元素

refs

共有3种操作 refs 的方法:

  1. React.createRef() 创建 ref 容器
  2. 回调函数形式
  3. 字符串形式(已弃用)
1
2
3
4
5
6
7
8
9
// 1. 字符串形式的 ref
<input ref="var1" />

// 2. 回调形式的 ref
<input ref={currentNode => this.var1 = currentNode } />

// 3. createRef 形式的 ref
var1 = React.createRef()
<input ref={this.var1} />
回调形式的refs

组件实例的 ref 属性传递一个回调函数 c => this.input1 = c (箭头函数简写),这样会在实例的属性中存储对 DOM 节点的引用,使用时可通过 this.input1 来使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class Demo extends React.Component {

// 回调形式的 ref
showData = ()=>{
const { input1 } = this
alert(input1.value)
}

render(){
return (
<div>
<input ref={currentNode => this.input1 = currentNode } type="text" placeholder="点击按钮提示数据"/>
<button onClick={this.showData}>点我提示数据</button>
</div>
)
}
}
createRef形式的refs

React 给我们提供了一个相应的 API,它会自动的将该 DOM 元素放入实例对象中,推荐这种方式,但需要注意的是:一个节点创建一个容器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class Demo extends React.Component {
// 给 input 节点创建 ref 容器
myRef = React.createRef();

// 回调形式的 ref
showData = ()=>{
// current 关键字可以获取到 ref 容器中的 DOM 元素
console.log(this.myRef.current.value);
}

render(){
return (
<div>
<input ref={this.myRef} type="text" placeholder="点击按钮提示数据" />
<button onClick={this.showData}>点我提示数据</button>
</div>
)
}
}

事件处理

事件处理
  1. 通过 onXxx 属性指定事件处理函数(注意大小写)
    1. React 使用的是自定义(合成)事件,而不是使用原生 DOM 事件,这是为了更好的兼容性
    2. React 中的事件是通过事件委托方式处理的(委托给组件最外层的元素),这是为了高效
  2. 通过 event.target 得到发生事件的 DOM 元素对象,不要过度使用 refs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
class Demo extends React.Component {
// 给 input 节点创建 ref 容器
myRef = React.createRef();
myRef2 = React.createRef();

showData = ()=>{
// current 关键字可以获取到 ref 容器中的 DOM 元素
console.log(this.myRef.current.value);
}

showData2 = (event)=>{
// console.log(this.myRef.current.value);
alert(event.target.value)
}

render(){
return (
<div>
<input ref={this.myRef} type="text" placeholder="点击按钮提示数据" />
<button onClick={this.showData}>点我提示数据</button>
<input onBlur={this.showData2} type="text" placeholder="失去焦点提示数据" />
</div>
)
}
}

发生事件的元素正好是需要操作的元素,就可以避免使用 Refs

表单的组件分类

受控组件

非受控组件

组件声明周期

React-Router

React-Router

PubSub

Ant-Design

参考