logo头像
Snippet 博客主题

React入门——TodoList(二)

TodoList代码优化

在constructor中绑定this,提高性能

1
2
3
4
constructor(props) {
super(props);
this.handleClick = this.handleClick.bind(this)
}

代码拆分

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 使JSX更简洁
<ul>
{this.getTodoItem()}
</ul>

getTodoItem() {
return this.state.list.map((item, index) => {
return (
<ToDoItem
key={index}
content={item}
index={index}
deleteItem={this.handleItemDelete}
/>
)
})
}

异步更新状态

1
2
3
4
5
6
7
handleInputChange(e) {
const value = e.target.value;
// 用函数代替对象
this.setState(() => ({
inputValue: value
}))
}

该函数将接收先前的状态作为第一个参数,将此次更新被应用时的props做为第二个参数

1
2
3
4
5
6
handleBtnClick() {
this.setState((prevState, props) => ({
list: [...prevState.list, prevState.inputValue],
inputValue: ''
}))
}

围绕react衍生出的思考

1.声明式开发

节约大量DOM操作代码

相对于命令式开发 jQuery操作DOM

2.可以与其它框架并存

react只管理一个DOM

3.组件化

4.单向数据流

子组件不能修改父组件传过来的值

只能调用父组件的方法修改数据

5.视图层框架

6.函数式编程

为自动化测试提供了方便

PropTypes与DefaultProps的应用

首先,引入prop-types包(脚手架自带)

import ProTypes from 'prop-types'

1
2
3
4
5
6
7
8
9
10
ToDoItem.propTypes = {
test: PropTypes.string.isRequired,
content: PropTypes.string,
deleteItem: PropTypes.func,
index: PropTypes.number
}

ToDoItem.defaultProps = {
test: 'todo'
}

props,state与render函数的关系

当组件的state或者props发生改变的时候,render函数就会重新执行

React中的虚拟DOM

  1. state 数据
  2. JSX 模板
  3. 数据 + 模板 结合,生成真实的DOM,来显示
  4. state 发生改变
  5. 数据 + 模板 结合,生成真实的DOM,替换原始的DOM

缺陷:

第一次生成了一个完整的DOM片段
第二次生成了一个完整的DOM片段
第二次的DOM替换第一次的DOM,非常耗性能

  1. state 数据
  2. JSX 模板
  3. 数据 + 模板 结合,生成真实的DOM,来显示
  4. state 发生改变
  5. 数据 + 模板 结合,生成真实的DOM,并不直接替换替换原始的DOM
  6. 新的DOM和原始的DOM作比对,找差异
  7. 找出input框发生了变化
  8. 只用新的DOM中的input元素,替换掉老的DOM中的input元素

缺陷:
性能提升不明显

  1. state 数据
  2. JSX 模板

  3. 生成虚拟DOM(虚拟DOM就是一个JS对象,用它来描述真实的DOM)(损耗了性能)
    [‘div’, {id: ‘abc’}, [‘span’, {}, ‘hello world’]]

  4. 数据 + 模板 结合,生成真实的DOM,来显示\
    <div id="abc"><span>hello world</span></div>

  5. state 发生改变

  6. 数据 + 模板 生成新的虚拟DOM (极大的提升了性能)
    [‘div’, {id: ‘abc’}, [‘span’, {}, ‘btyh’]]
  7. 对比原始虚拟DOM和新的虚拟DOM的区别,找到区别是span中的内容
  8. 直接操作DOM,改变span中的内容

优点:

  1. 性能提升了
  2. 它使得跨端应用得以实现。 React Native

深入了解虚拟DOM

1
2
3
4
// JSX -> createElement -> JS对象 -> 真实的DOM
return <div>item</div>
// 相当于
return React.createElement('div', {}, 'item')

虚拟DOM中的Diff算法

Diff difference
把多次setState结合成一次setState,减少虚拟DOM比对的次数
Diff算法会逐层比对,如果一层不满足匹配就不会继续往下比对,用新的替换老的

React中ref的使用

采用回调 Refs的方式

1
2
3
4
5
6
7
<input
id='inserArea'
className='input'
value={this.state.inputValue}
onChange={this.handleInputChange}
ref={(input) => {this.input = input}}
/>

可以直接访问DOM元素

1
2
3
4
5
6
7
handleInputChange(e) {
// const value = e.target.value;
const value = this.input.value;
this.setState(() => ({
inputValue: value
}))
}

尽量避免使用ref

React的生命周期函数

生命周期函数是指在某一个时刻组件会自动调用执行的函数

  1. Initialization 组件初始化阶段
    setup props and state 在 constructor 中执行

  2. Mounting 组件第一次挂载阶段
    componentWillMount -> render -> componentDidMount

  3. Updation 组件更新

  • props改变 -> componentWillReceiveProps -> shouldComponentUpdate -> componentWillUpdate -> render -> componentDidUpdate
  • state改变 -> shouldComponentUpdate -> componentWillUpdate -> render -> componentDidUpdate
  • shouldComponentUpdate 如返回 false 后面不再执行
  • componentWillReceiveProps执行条件:1.一个组件要从父组件接收参数 2.父组件的 render 函数被重新执行
  1. Unmounting 组件卸载
    componentWillUnmount

生命周期函数的使用场景

  1. 父组件render函数重新执行,子组件render函数也会重新执行,这样会造成性能浪费

    解决方法:

    1
    2
    3
    4
    5
    6
    7
    8
    // 在子组件TodoItem中
    shouldComponentUpdate(nextProps, nextState) {
    if (nextProps.content !== this.props.content) {
    return true;
    } else {
    return false;
    }
    }
  2. 在componentDidMount执行Ajax数据请求,因为componentDidMount只执行一次

使用react-transition-group实现动画