《React进阶之路》观后感

241 阅读6分钟

React属于MVC还是MVVM

react只属于MVC中的View层,数据处理需要借助Redux或Mbox,也不属于MVVM,MVVM需要双向绑定,React是单向数据流,数据驱动视图

React四大特点

  • 声明式的视图层
  • 数据驱动视图
  • 虚拟DOM
  • 高效的DOM操作(diff算法)

Babel ES6转ES5

Babel是一个javascript编译器,配合webpack,在编译的时候,将ES6代码,转成AST语法树,然后转成ES5,在浏览器运行

JSX语法不是必须的,它只是个语法糖

JSX语法只是React.createElement (component, props, ...children)的语法糖

Class组件

class继承自React.Component 在组件的constructor中,要先调用super(props),这实际上是调用了React.Component的constructor方法,用来完成class组件的初始化

组件的生命周期

1.挂载阶段

这个阶段组件被创建,执行初始化,并被挂载到dom中,完成组件的第一次渲染,依次调用的生命周期:
  • constructor
  • componentWillMount
  • render
  • componentDidMount

2.更新阶段

组件被挂载到DOM后,组件的props或state可以引起组件更新。组件更新阶段,出发的生命周期有:
  • componentWillReceiveProps
  • shouldComponentUpdate
  • componentWillUpdate
  • render
  • componentDidUpdate

3.卸载阶段

  • componentWillUnmount

组件state

1.如何判断一个变量是否应该作为state

  • 这个变量是否通过props从父组件中获取?如果是,那么它不是一个状态。
  • 这个变量是否在组件的整个生命周期中都保持不变?如果是,那么它不是一个状态。
  • 这个变量是否可以通过其他状态(state)或者属性(props)计算得到?如果是,那么它不是一个状态。
  • 这个变量是否在组件的render方法中使用?如果不是,那么它不是一个状态。这种情况下,这个变量更适合定义为组件的一个普通属性。

2.state 的更新是异步的

setState 只在合成事件和钩子函数中是“异步”的,在原生事件和 setTimeout 中都是同步的。

3.setState批处理

调用setState时,组件的state并不会立即改变,setState只是把要修改的状态放入一个队列中,React会优化真正的执行时机,并且出于性能原因,可能会将多次setState的状态修改合并成一次状态修改。所以不要依赖当前的state,计算下一个state。

4.state与不可变对象

  • 状态的类型是不可变类型(数字、字符串、布尔值、null、undefined)可以直接赋值
  • 状态的类型是数组:不要使用push、pop、shift、unshift、splice等方法修改数组类型的状态,因为这些方法都是在原数组的基础上修改的,而concat、slice、filter会返回一个新的数组。或者使用ES6[...xxxxx]构造一个新数组。
  • 状态的类型是普通对象(不包含字符串、数组): 1.使用ES6的Object.assign({}, obj) 2.使用对象扩展语法{...obj,x:1}

总结一下,创建新的状态对象的关键是,避免使用会直接修改原对象的方法,而是使用可以返回一个新对象的方法。

组件通信

  1. 父传子 通过props
  2. 子传父 通过回调函数
  3. 兄弟通信 通过状态提升
  4. 多层级 通过Context上下文

ref

无法获取函数式组件的实例对象,但是可以通过props获取函数组件内部的dom元素

虚拟DOM

虚拟DOM和真实DOM是想对应的, 虚拟DOM是对结构化文本的抽象表达,是一个javascript对象。

<div className="foo">
    <h1>Hello React</h1>
</div>
可以用这样的一个JavaScript对象来表述:
{
    type: 'div',
    props: {
        className: 'foo',
            children: {
                type: 'h1',
                props:{
                children: 'Hello React'
            }
        }
    }
}

diff算法

在Diff算法中,比较的两方是新的虚拟DOM和旧的虚拟DOM

  1. 当根节点是不同类型时,整体拆除重构
  2. 当根节点是相同的DOM元素类型时,只更新变化的属性
  3. 当根节点是相同的组件时,按照1.2的规则,递归子节点,递归完毕后,计算最终的差异,更新到真实DOM中。

高阶组件

高阶组件(简称HOC)接收React组件作为参数,并且返回一个新的React组件。高阶组件本质上也是一个函数,并不一个组件。

高阶组件的使用场景主要有以下4种:

  1. 操纵props
  2. 通过ref访问组件实例
  3. 组件状态提升
  4. 用其他元素包装组件

高阶组件传参

HOC(...params)(WrappedComponent)

常见的高阶组件都是由高阶组件处理通用逻辑,然后将相关属性传递给被包装组件,这种为属性代理,除了属性代理外,还可以通过继承方式实现高阶组件:通过继承被包装组件实现逻辑的复用。继承方式实现的高阶组件常用于渲染劫持。代码如下:

function withAuth(WrappedComponent) {
    return class extends WrappedComponent {
        render() {
            if (this.props.loggedIn) {
                return super.render();
            } else {
                return null;
            }
        }
    }
}
  1. 如果组合多个高阶组件,容易乱,属于骚操作,尽量使用属性代理的高阶组件。
  2. 在组件的外部使用高阶组件,不要在render方法和生命周期中使用,以避免重复渲染
  3. 如果需要使用被包装组件的静态方法,那么必须手动复制这些静态方法。因为高阶组件返回的新组件不包含被包装组件的静态方法。
  4. Refs不会被传递给被包装组件。如果在高阶组件的返回组件中定义了ref,那么它指向的是这个返回的组件,而不是被包装的组件。如果希望获得被包装组件的ref,自定义一个获取ref的方法,然后把这个方法,通过ref属性的值,传递给被包装组件。
  5. 高阶组件是一个函数,函数关注的是逻辑;父组件是一个组件,组件主要关注的是UI/DOM。

React Router

  1. react-router提供最基本的路由功能,实际使用时,我们不会直接安装react-router,而是根据应用运行的环境选择安装react-router-dom。
  2. BrowserRouter使用HTML 5的history API(pushState、replaceState等)实现应用的UI和 URL的同步。
  3. HashRouter使用URL的hash实现应用的UI和URL的同步。

路由配置

  1. path
  2. match
  3. 渲染方式:component、render、children
  4. switch和exact 精确匹配

Link

Link是React Router提供的链接组件

<Link to='/'>Home</Link>
<Link to={{
    pathname: '/posts',
    search: '?sort=name',
    hash: '#the-hash',
    state: { fromHome: true }
}}/>

也可以使用history手动跳转

history.push('/posts')
history.replace('/posts')

Redux

React关注的是视图层,对于大型应用而言,state并不能很好的管理状态,所以,我们需要借助Redux,来进行全局的状态管理

redux三大原则

  1. 唯一数据源
  2. state只读,修改通过action
  3. reducer是纯函数

mapStateToProps

mapStateToProps,顾名思义,把store中的state转成组件的props。 mapStateToProps除了接收state参数外,还可以使用第二个参数,代表容器组件的props对象

// ownProps 是组件的props对象
function mapStateToProps(state, ownProps) {
    //...
}

mapDispatchToProps

从store中接收dispatch参数,来发起action请求来修改state。

redux本身无法处理异步请求,需要借助中间件,常用的中间件包括redux-thunk,redux-saga

redux-saga采用generate语法,提供call,put等方法,实现异步请求

设计state

错误一:以API作为设计state的依据

错误二:以UI作为设计state的依据

正确做法:像设计数据库一样设计state