漫谈 React 组件库开发(一):多层嵌套弹层组件
引言UI 组件中有很多弹出式组件,常见的如 我们都知道 React App 的顶层某个地方肯定有这么一行代码: 在 React 的这种管理模式下,会发现使用弹层似乎不太方便,因为组件树是逐层往下生长的,但React 的 API 中并没有直接提供跳出这棵组件树的方法[注1]。 所以,为了实现弹层组件,我们需要先实现一个 Portal 组件
// 简化的 Portal 实现 class Portal extends Component { static propTypes = { children: PropTypes.node.isRequired,container: PropTypes.object.isRequired }; render() { return null; } componentDidMount() { const { children,container } = this.props; mountChildrenAtNode(children,container); } componentWillUnmount() { const { container } = this.props; unmountChildrenAtNode(container); } } 剩下唯一的问题是 那么真的这么简单吗? 是,但也不是。 说是,是因为逻辑上这代码并没有什么问题,而且大部分场景下是确实可以完美工作。 说不是,是因为剩下的小部分场景下这段代码确实存在很严重的问题。 那么问题是什么呢?别急,我们先聊点别的。 相信大部分 React 开发者都用过 redux(至少听过吧),
ReactDOM.render( element,container,[callback] ) 解决这个问题的方法也很简单,这里也不卖关子了,React 提供了另一个非公开 API: ReactDOM.unstable_renderSubtreeIntoContainer( parentComponent,element,[callback] ) 想更好的了解 Portal 组件的可扩展性不同的 UI 组件对弹层可能会有不同的功能需求,举个例子, 这些很细节的功能点往往会出现需要不同组合的使用场景,例如只需要禁止滚动,或者同时需要禁止滚动和 ESC 键关闭。 一个很自然的想法是在 更好的方式是通过高阶组件(HOC)的方式让使用者自己去组合这些功能,这样子没有用到的功能并不会出现在最终的代码中。 说了这么多关于 弹层组件有了 在 Zent 里面有一个叫
有了 这里已经能够看出一个层次化的弹层组件设计了: 在组件库的设计中,这种对能力的抽象封装是很重要的,在提高开发效率的同时也保证了各个组件行为的一致性。 干货:弹层组件的嵌套处理上面介绍的弹层组件实现细节上并没有特别之处,成熟的组件库基本都是用类似方式实现的。但是 Zent 的 如果你还没有明白这里的弹层嵌套是什么意思,没关系,给你举个例子就明白了。 如下图,点击按钮之后会弹出一个气泡,这个气泡中又有一个时间选择器,所谓的弹层嵌套指的就是这种弹层之中又嵌了弹层的场景。正常的操作逻辑是鼠标点击位置1的时候气泡和时间选择器同时关闭,但是点击位置2的时候应该只有时间选择器关闭。
上面提到的点击两个不同位置的不同行为其实就是弹层嵌套最主要的问题:上级的弹层组件应该知道哪个区域是属于下级弹层组件的。 由于弹层组件的特殊性,它们在 DOM 树中的位置跟它们实际的层次以及包含关系是没有必然联系的,上图中的两个弹层是 通常来说,弹层的层次结构也是一个树状结构,那么处理嵌套问题最直接的想法就是每个弹层组件都各自维护一个子弹层的列表。当需要判断点击是否在弹层外面时,不光要考虑当前弹层对应的 DOM 节点,还要考虑它的下级弹层对应的 DOM 节点。 这种方式处理的话需要手动维护这棵弹层的层级关系树,包括树中节点的插入/删除,这些操作都不是很难。这个方法最大的问题在于,在 React 的体系内一个弹层组件很难跟不是它直接孩子(direct child)的子弹层交互。 Zent 的 * context context * ------> ------> * Popover Root Popover child Popover grand-child ...... * <------ <------ * isOutsideQuery isOutsideQuery 就是这么一个很简单的设计解决了 Zent 中弹层组件的层级嵌套问题,想了解实现细节的同学可以看 总结弹层组件是 UI 组件库中很重要的部分,一个逐层抽象的结构可以极大简化这些组件的开发和维护成本。 合理利用 React 的 如果觉得有所收获,请给 Zent 点个 star 吧。
本文由 李晨 首发于 有赞技术博客。 (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |