以下仅是我个人一些笔记合集,如理解有误,还望帮忙及时指出,方便及时更正。

react 生命周期

mounte(首次渲染)

  • constructor(): 构造函数
    • 如果组件中存在constructor,则必须调用super(),如果不执行super,this将无法初始化;
    • 如果只是单纯的调用super,而没有传props,那么在constructor内就无法调用this.props,但是在组件其他地方依然可以调用this.props;
    • 不能在里面调用setState(),会报一个警告Warning: setState(…): Can only update a mounted or mounting component. This usually means you called setState() on an unmounted component. This is a no-op
    • 总结:有constructor,就必须有super();constructor中不需要访问this.props时可以不传props。
  • componentWillMount(): 组件挂载之前被调用
    • 由于它在render之前被调用,所以在这里setState()之后是可以在render中体现的,并且只渲染一次,不会导致重绘。
  • render():组件渲染,必须有return,返回一个jsx或者null。
  • componentDidMount():组件挂载之后立即被调用
    • 在这里可以做一些数据请求,DOM初始化等操作,也可以setState(),不过会重新渲染,即再次调用render。

update(props或state发生改变)

  • componentWillReceiveProps(nextProps): 已挂载组件收到新的props时调用
    • 当父组件发生render时,不管props有没有发生改变,子组件都会触发该函数。所以使用这个钩子函数时,记得比较当前props和nextProps
    • 可以在这里setState()
  • shouldComponentUpdate(nextProps, nextState): 是否应该更新组件,默认更新
    • 当组件收到新的props或state时,在render之前调用,默认返回true。我们可以定制这个钩子函数,根据业务返回true或false,可以在这里做优化
    • 如果返回false,那么之后的componentWillUpdate(),render(),componentDidUpdate()都不会调用,但是这只能阻止当前组件的render,并不能阻止子组件的render
    • 当你调用了forceUpdate()时,React会调用render()并忽略shouldComponentUpdate()
  • componentWillUpdate(): props或state改变之后,render之前调用
    • 不能在这里setState(),因为它本身就是state改变之后调用的,如果再在里面setState()将会导致无限循环。如果需要更新state,官方建议使用componentWillReceiveProps()代替
  • render(): 同上
  • componentDidUpdate(): 组件更新后立即调用
    • 在这里可以操作DOM,发请求等,也可以setState()

unmount(卸载)

  • componentWillUnmount(): 组件卸载之前调用
    • 在这里适合做一些收尾操作,比如清理定时器,取消网络请求,解绑DOM等
    • 不可以setState()

render()是react组件中必要的一个函数,其他钩子都不是必须的,记住:千万不能在render中setState()

触发render有以下途径:

  • 初次渲染
  • state发生改变(并不是setState()一次就render一次,多次setState可能会合并)
  • 父组件更新,即使props没有发生改变,或者父子组件没有数据交互
  • 调用this.forceUpdate() (还没试过这种方法)

jsx

react内部执行了 React.createElement(type, config, children)

  • type(类型:String) 是必须的:可以是HTML标签名,也可以是ReactClass
  • config(类型:Object)非必须:该元素的属性配置,比如className,id或者自定义属性
  • children(可以一个一个传,也可以在一个数组里一次性传,数组的话需要key)非必须:该元素的子元素,如果又是一个元素节点(非文本节点),会一直createElement 下去,形成一颗树

setState()

异步更新组件状态,将需要更新的状态更新到一个队列里面,并不是setState()一次就render一次,多次setState可能会合并,不要在render函数,componentwillUpdate()和componentWillUnMount()里使用setState

它接收两个参数:

  • 第一个参数是一个函数updater,它返回一个对象,就是我们平常书写的对象。updater函数接收两个参数:prevState和props;
  • 第二个参数是[callback]:可选的

当然,第一个参数也可以直接写成一个对象(大部分时间我们是这样写的),但是如果后面的状态取决于前一个状态,建议写成函数参数的形式,因为函数参数可以拿到之前的状态。

虚拟DOM

目前我所理解的就是

  • 用js对象去模拟DOM树,然后用这个对象构造真正的DOM树,并且插入到文档;
  • 当状态发生改变的时候重新构造一颗新的js对象树,然后用新的对象树与旧的树对比(diff算法),记录不同的地方。(只在同级对比,如果完全对比复杂度太高了),具体关于算法的我就不是很清楚了…深度优先,列表对比(因为这个所以我们遍历数组生成jsx时需要传key)什么的,,总之很牛逼就是了。
  • 旧的树和实际的DOM树是完全相同的,所以对上面找出的差异进行实际的DOM操作(patch)。只对有差异的地方进行操作

总之应该就是js运行很快,DOM操作太慢了,所以才有的虚拟DOM吧。。。

context

可以看做全局变量.如果需要跨很多组件传递props,并且中间的组件并不需要这个参数,只是充当一个传递的作用,就可以使用context传递。(当然你也可以使用简单粗暴的props层层传递,不过这样会损耗性能,并且代码也不易于维护)由于全局变量在开发中是很不受欢迎的,所以使用context需要严格的校验。我们使用PropTypes来做验证,早期版本PropTypes是集成在react库中的,现在的版本为了让react更轻量级,将它抽离出来,放在prop-types这个库中,这也是react官方的库。
使用:

  • 在父组件中使用getChildContext()函数,它返回你所需要传递的属性;

    1
    2
    3
    getChildContext() {
    return { user: this.state.user }
    }
  • 并且在父组件中要声明你所传递属性的类型;(在getChildContext中返回了哪些属性,这里必须全部声明,否则会报错)

    1
    2
    3
    static childContextTypes = {
    user: PropTypes.string
    }
  • 然后在你需要使用该属性的子组件中声明:(只有这里声明了才能用this.context获取到)

    1
    2
    3
    static contextTypes = {
    user: PropTypes.string
    }

我个人觉得我们应该很少会使用到context吧,一般项目中都会使用redux管理我们的全局状态,不过了解一下还是很有必要的,说不定react-redux的实现就是依靠context呢!虽然我没有看过源码。



先这样吧,溜了溜了