ES6
- 不使用ES6
class
关键字继承React.Component
类- 使用 create-react-class 模块来创建
- 声明默认属性
- 自定义属性对象写到类的 defaultProps 属性
- createReactClass 方法创建组件,那就需要在参数对象中定义 getDefaultProps 方法,并且在这个方法中返回包含自定义属性的对象
- 设置初始状态
- class 关键字创建组件,在 constructor 中给 this.state 赋值
- 使用 createReactClass 方法创建组件,要写一个 getInitialState 方法,并让这个方法返回你要定义的初始属性
- 自动绑定
- 通过 ES6 class 生成的实例,实例上的方法不会绑定 this
- 使用 class 关键字创建的 React 组件,组件中的方法是不会自动绑定 this
- 因此,需要在 constructor 中为方法手动添加 .bind(this) [建议]
- 使用 createReactClass 方法创建组件,组件中的方法会自动绑定至实例
- 目前还处于实验性阶段的 Babel 插件 Class Properties(注通常意义上的箭头函数属性)
- Mixin混入
- ES6本身是不包含混入支持
- 相似功能-> 横切关注点
- createReactClass创建React组件时,可引入混入
- 配置类似
mixins:[SetIntervalMixin]
选项
- 配置类似
- 多个混入定义相同生命周期方法,执行顺序与定义顺序一致
JSX
- 不使用jsx
- 每一个JSX元素都只是 React.createElement(component, props, …children) 的语法糖
- react组件类型
- 可以是一个字符串
- 亦可以是
React.Component
的子类 - 当组件是无关的时候,它可以是一个普通的函数
React.createElement
- jsx语法
class Hello extends React.Component { render() { return <div>Hello {this.props.toWhat}</div>; } } ReactDOM.render( <Hello toWhat="World" />, document.getElementById('root') );
协调(Reconciliation)
- 目的
- 在单一时间点你可以考虑render()函数作为创建React元素的树
- React需要算出如何高效更新UI以匹配最新的树,React基于两点假设
- 两个不同类型的元素将产生不同的树
- 通过过渲染器附带key属性,开发者可以示意哪些子元素可能是稳定的
- 对比算法
- 不同类型的元素
- 每当根元素有不同类型,React将卸载旧树并重新构建新树
- 组件实例会调用
componentWillUnmount() -> componentWillMount() -> componentDidMount()
- 任何与旧树有关的状态都将丢弃
- 相同类型的DOM元素
- React则会观察二者的属性[即树的状态],保持相同的底层DOM节点,并仅更新变化的属性。
- 在处理完DOM元素后,React递归其子元素。
- 相同类型的组件元素
- 当组件更新时,实例仍保持一致,以让状态能够在渲染之间保留
- React通过更新底层组件实例的props来产生新元素
- 并在底层实例上依次调用componentWillReceiveProps() -> componentWillUpdate() -> render()
- 递归子节点
- 默认时,React仅在同一时间点递归两个子节点列表,并在有不同时产生一个变更
- Keys
- React使用key来匹配原本树的子节点和新树的子节点
- key必须在其兄弟节点中是唯一,但当索引用作key时,组件状态在重新排序时会有问题。
- 万不得已,你可以传递他们在数组中的索引作为key,建议元素在没有重排情况下使用
- 不同类型的元素
- 协调
- 当前实现,子树在其兄弟节点中移动,是无法告知其移动位置
- React依赖于该启发式算法(即合理假设),若其背后的假设没得到满足,则其性能将会受到影响
- 算法无法尝试匹配不同组件类型的子元素。
- Keys应该是稳定的,可预测的,且唯一的。
- 非稳定的key会使得大量组件实例和DOM节点进行不必要的重建,且丢失子组件的状态,性能下降。
Context
- 应用场景(数据共享)
- React.createContext
- Context 提供了一种在组件之间共享props值的方式,而不必通过组件树的每个层级显式地传递 props
- Context 设计目的是为共享那些被认为对于一个组件树而言是“全局”的数据,当前认证的用户、主题或首选语言
- React应用数据是通过 props 属性由上向下(由父及子)的进行传递,使用context可以避免通过中间元素传递
- 应用于多个层级的多个组件需要访问相同数据的情景。
- 相关API
const {Provider, Consumer} = React.createContext(defaultValue);
<Provider value={/* some value */}>
- Comsumer 一个可以订阅 context 变化的 React 组件。
- 父子耦合
- 可以通过 context 向下传递一个函数,以允许 Consumer 更新 contex
Fragment
- 目的
- Fragments 聚合一个子元素列表,并且不在DOM中增加额外节点
- 常见模式是为一个组件返回一个子元素列表
- React.Fragment 组件
- <></> 是 <React.Fragment/> 的语法糖
- <></>语法不接受键值或属性
Portals(弹窗对话框)
- 目的
- Portals 提供一种将子节点渲染到父组件以外的DOM节点的方式
ReactDOM.createPortal(child, container)
- 第一个参数(child)是任何可渲染的 React 子元素, 如 一个元素,字符串或碎片
- 数(container)则是一个 DOM 元素。
- 应用
- 通常从组件的 render 方法返回一个元素,该元素仅能装配 DOM 节点中离其最近的父元素
- 典型用例
- 当父组件有 overflow: hidden 或 z-index 样式
- 需要子组件能够在视觉上“跳出(break out)”其容器,如 对话框、hovercards以及提示框
- 通过 Portals 进行事件冒泡
- portal 仍存在于 React 树中,而不用考虑其在 DOM 树中的位置
- 一个从 portal 内部会触发的事件会一直冒泡至包含 React 树 的祖先
- 错误边界仅可以捕获其子组件的错误
Web Components
- React vs web
- Web组件为可重用组件提供了强大的封装能力
- React则是提供了保持DOM和数据同步的声明式库
- React组件与web组件内都可使用对方
- 由web组件触发的事件可能无法通过React渲染树来正确冒泡,可能需要手动捕获
高阶组件(hoc)
- 定义
const EnhancedComponent = higherOrderComponent(WrappedComponent)
- 高阶组件就是一个没有副作用的纯函数,且该函数接受一个组件作为参数,并返回一个新的组件
- 对比组件将props属性转变成UI,高阶组件则是将一个组件转换成另一个新组件
- 应用场景
- 使用高阶组件(HOC)解决交叉问题
- 移除混入,mixins)技术产生的问题要比带来的价值大
- 与混入向组件内部注入,高阶组件更像是变更组件外部生存环境
- 比如Redux的connect方法和Relay的createContainer.
- 注意点
- 不要改变原始组件,使用组合技术
- 高阶组件就是参数化的容器组件定义
- 容器组件是专注于在高层次和低层次关注点之间进行责任划分的策略的一部分
- 将不相关的props属性传递给包裹组件
- 高阶组件给组件添加新特性,不大幅修改原组件的接口(props属性)
- 最大化使用组合,高阶组件会接收额外的参数
- 高阶组件签名
- 函数签名如下
const ConnectedComment = connect(commentSelector, commentActions)(Comment)
- connect是一个返回函数的函数(高阶函数)
- React中组件可以是函数
- 约定俗成
- 包装显示名字以便于调试
- 不要在render函数中使用高阶组件
- 必须将静态方法做拷贝
- 理由:当使用高阶组件包装组件,原始组件被容器组件包裹,也就意味着新组件会丢失原始组件的所有静态方法
- Refs属性不能传递(理由refs是一个伪属性,React对它进行了特殊处理)