模块与组件的理解

  1. 模块
    • 向外提供特定功能的js 程序, 一般就是一个js 文件
    • 复用js, 简化js 的编写, 提高js 运行效率
  2. 组件
    • 用来实现特定(局部)功能效果的代码集合(html/css/js)
    • 复用编码, 简化项目编码, 提高运行效率

组件的基本使用

创建组件有两种方式

  1. 工厂函数组件

    简单组件,通过函数返回值即可。函数名就是组件(标签)名。

    1
    2
    3
    4
    function MyComponent() {
    return <h2>工厂函数组件(简单组件)</h2>
    }
    ReactDOM.render(<MyComponent />, document.getElementById('test'))
  2. ES6类组件

    复杂组件,通过ES6类中的render方法。

    1
    2
    3
    4
    5
    6
    class MyComponent2 extends React.Component {
    render() {
    return <h2>ES6类组件(复杂组件)</h2>
    }
    }
    ReactDOM.render(<MyComponent2 />, document.getElementById('test2'))

image-20201114091744893

组件三大属性

state

state用于记录属性,其声明在constructor中,值是一个对象。(简单组件中没有状态)

1
2
3
4
5
6
7
8
constructor(props) {
// 固定写法
super(props)
// 设置属性
this.state = {
isLikeMe: false
}
}

render中可以通过this.state.xxx获取相关属性的值。此时的this指向当前对象。

1
2
3
4
5
render() {
// 读取状态
const { isLikeMe } = this.state
return <h2 onClick={this.handleClick}>{isLikeMe ? '你喜欢我' : '我喜欢你'}</h2>
}

h2标签绑定事件只需要在类中新增方法即可,但其默认this并不是指向当前对象,而是undefiend因此,需要在constructor将其指向当前对象。

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 Like extends React.Component {
// 接受一个参数
constructor(props) {
// 固定写法
super(props)
// 设置属性
this.state = {
isLikeMe: false
}
// 将新增的方法中的this强制绑定为组件对象
this.handleClick = this.handleClick.bind(this)
}
// 新添加方法:内部的this默认不是组件对象,而是undefined
handleClick() {
// 得到状态
const isLikeMe = !this.state.isLikeMe
// 更新状态
this.setState({ isLikeMe })
}
render() {
// 读取状态
const { isLikeMe } = this.state
return <h2 onClick={this.handleClick}>{isLikeMe ? '你喜欢我' : '我喜欢你'}</h2>
}
}

ccdeec99-3625-40e9-a51d-ccb01e8db4d2

props

基本用法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function Person(props) {
return (
<ul>
<li>姓名:{props.name}</li>
<li>年龄:{props.age}</li>
<li>性别:{props.sex}</li>
</ul>
)
}
const p1 = {
name: 'Tom',
age: 18,
sex: '女'
}
ReactDOM.render(<Person name={p1.name} age={p1.age} sex={p1.sex} />, document.getElementById('test'))

属性传入时也可以使用扩展运算符

1
ReactDOM.render(<Person {...p1} />, document.getElementById('test'))

通过ES6类的继承实现语法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class Test extends React.Component {
// 接受一个参数
constructor(props) {
// 固定写法
super(props)
}
render() {
return (
<ul>
<li>姓名:{this.props.name}</li>
<li>年龄:{this.props.age}</li>
<li>性别:{this.props.sex}</li>
</ul>
)
}
}
  1. 默认值

    1
    2
    3
    4
    Person.defaultProps = {
    sex: '男',
    age: 18
    }
  2. 设置属性的类型

    需要引入react-type文件

    1
    2
    3
    4
    5
    // 指定属性类型和必要性
    Person.propTypes = {
    name: PropTypes.string.isRequired,
    age: PropTypes.number
    }

refs和事件处理

  1. 为元素绑定ref

    1
    <input type="text" ref='content' />
  2. 事件中通过this.refs.content即可拿到当前DOM元素

    1
    2
    3
    4
    showInput() {
    const input = this.refs.content
    alert(input.value)
    }

官方推荐的写法并不是使用字符串,而是使用一个函数,将其挂载到React对象中。

1
<input type="text" ref={input => this.input = input}

此时通过this.input.value即可拿到值。

组件组合使用

f85a8f7c-ca9c-4fb4-850d-3abf22469003

DEMO涉及到三个组件与组件之间的传值。其原则如下

  1. 都需要使用到的数据则放在父组件中
  2. 状态定义在哪个组件,则哪个组件修改状态
  3. 父组件将修改状态的函数作为props属性传递给子组件
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
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
class App extends React.Component {
constructor(props) {
super(props)
this.state = {
todos: ['吃饭', '睡觉', '打代码']
}
// 绑定this
this.addItem = this.addItem.bind(this)
}

// 添加值的具体方法
addItem(val) {
// 将值放到数组最前方
this.state.todos.unshift(val)
// 设置状态
this.setState(this.state)
}

render() {
const {todos} = this.state
// 将addItem方法作为属性传入到子组件
return (
<div>
<h1>Simple TODO List</h1>
<Add count={todos.length} addItem={this.addItem}/>
<List todos={todos}/>
</div>
)

}
}
class Add extends React.Component {
constructor(props) {
super(props);
this.addItem = this.addItem.bind(this)
}

addItem() {
// 获取input的值
const todo = this.todoInput.value.trim()
if (!todo) return
// 调用父组件传来的方法
this.props.addItem(todo)
this.todoInput.value = ''
}

render() {
// 为input标签挂载到当前对象的属性
return (
<div>
<input type="text" ref={input => this.todoInput = input}/>
<button onClick={this.addItem}>add #{this.props.count + 1}</button>
</div>
)

}
}
Add.propsTypes = {
count: PropTypes.number.isRequired,
addItem: PropTypes.func
}

List组件渲染

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class List extends React.Component {
render() {
return (
<ul>
{
this.props.todos.map((todo, index) => <li key={index}>{todo}</li>)
}
</ul>
)

}
}

List.propsTypes = {
todos: PropTypes.array.isRequired
}

表单

收集表单数组有两种方式

  1. 受控组件

    表单项输入数据能自动收集成状态

    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
    class LoginForm extends React.Component {
    constructor(props) {
    super(props)
    this.state = {
    pwd: ''
    }
    this.handleSubmit = this.handleSubmit.bind(this)
    this.handleChange = this.handleChange.bind(this)
    }
    handleSubmit(e) {
    const { pwd } = this.state
    e.preventDefault()
    }
    handleChange(e) {
    const pwd = e.target.value
    this.setState({ pwd })
    }
    render() {
    return (
    <form action='/test' onSubmit={this.handleSubmit}>
    密码:<input type="password" value={this.state.pwd} onChange={this.handleChange} />
    <input type="submit" value="登录" />
    </form>
    )
    }
    }
  2. 非受控组件

    需要时才手动读取表单输入框中的数据

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    class LoginForm extends React.Component {
    constructor(props) {
    super(props)
    this.handleSubmit = this.handleSubmit.bind(this)
    }
    handleSubmit(e) {
    const name = this.NameInput.value
    e.preventDefault()
    }
    render() {
    return (
    <form action='/test' onSubmit={this.handleSubmit}>
    用户名:<input type="text" ref={input => this.NameInput = input} />
    <input type="submit" value="登录" />
    </form>
    )
    }
    }

生命周期

image-20201115142211647

0a98c3d6-bf58-4be6-ad13-c75e6430e582

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
38
39
class Life extends React.Component {
constructor(props) {
super(props)
// 初始化状态
this.state = {
opactity: 1
}
this.distroy = this.distroy.bind(this)
}
// 挂载后的生命周期
componentDidMount() {
// 启动循环定时器
this.intervalId = setInterval(() => {
let { opactity } = this.state
opactity -= 0.1
if (opactity <= 0) {
opactity = 1
}
this.setState({ opactity })
}, 100)
}

distroy() {
ReactDOM.unmountComponentAtNode(document.getElementById('test'))
}
// 销毁时的生命周期
componentWillUnmount() {
clearInterval(this.intervalId)
}
render() {
const { opactity } = this.state
return (
<div>
<h2 style={{ opacity: opactity }}>{this.props.msg}</h2>
<button onClick={this.distroy}>不活了</button>
</div>
)
}
}
  1. 组件的三个生命周期状态

    • Mount插入真实DOM
    • Update被重新渲染
    • Unmount被移出真实DOM
  2. 生命周期流程

    React生命周期流程

  3. 重要的钩子

    • render()

      初始化渲染或更新渲染调用

    • componentDidMount()

      开启监听, 发送ajax 请求

    • componentWillUnmount()

      做一些收尾工作, 如: 清理定时器

    • componentWillReceiveProps()

      后面需要时讲

虚拟DOM和DOM diff算法