以下仅是我个人一些笔记合集,如理解有误,还望帮忙及时指出,方便及时更正。
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()函数,它返回你所需要传递的属性;
123getChildContext() {return { user: this.state.user }}并且在父组件中要声明你所传递属性的类型;(在getChildContext中返回了哪些属性,这里必须全部声明,否则会报错)
123static childContextTypes = {user: PropTypes.string}然后在你需要使用该属性的子组件中声明:(只有这里声明了才能用this.context获取到)
123static contextTypes = {user: PropTypes.string}
我个人觉得我们应该很少会使用到context吧,一般项目中都会使用redux管理我们的全局状态,不过了解一下还是很有必要的,说不定react-redux的实现就是依靠context呢!虽然我没有看过源码。
先这样吧,溜了溜了