css3 关闭按钮-《精通react/vue组件设计》与ReactPortals合作实现强大功能

2023-08-26 0 7,090 百度已收录

前言

本文是作者写的关于组件设计的第六篇文章。 内容由易到难。 明天我们将使用react的中间APIReactPortals,这也是很多复杂组件必须使用的技能之一。 通过组件设计过程,你将接触到一个完整的、强大的组件设计思想和方法,并在实现组件的过程中逐渐对react/vue的中间知识和方法有更深入的理解和掌握,并能够在企业开展实际工作游刃有余。

我之所以写组件设计相关的文章,是因为作为一名优秀的后端工程师,面对各种繁琐、重复的工作css3 关闭按钮,我们不应该按部就班地“苦干”,而应该根据现有的后端——最终开发经验,总结出一套自己的高效开发方法。

作为数据驱动的引领者,react/vue等MVVM框架的出现帮助我们在工作中减少了大量冗余代码,一切皆组件的理念深入人心。 为了让工程师有更多的时间考虑业务和产品迭代,我们必须掌握高质量组件设计的思路和技巧。 因此,笔者会花时间总结各种业务场景下组件的设计思路和方法,并利用原生框架的句型来实现各种常用组件的开发。 希望等待后端菜鸟或者有一定工作经验的同事能够有所收获。

如果你不熟悉react/vue组件的设计原理,可以参考我之前的组件设计系列文章:

文本

在开始组件设计之前,希望你有一定的css3和js基础,了解基本的react/vue句型。 我们来看看组件实现后的效果:

css3 关闭按钮-《精通react/vue组件设计》与ReactPortals合作实现强大功能

1. 组件设计思路

根据笔者之前总结的组件设计原则,我们第一步就是确认需求。 一个抽屉(Drawer)组件会有以下要求:

收集完需求后,作为一名追求者,你将得到如下线框图:

对于react玩家来说,如果你不会使用typescript,我建议大家都使用PropTypes,它是react的外部类型检查工具,我们可以直接在项目中导出。 Vue有自己的属性测量方法,这里就不一一介绍了。

通过上面的需求分析,你是不是觉得一个抽屉组件要实现这样的功能是不是很复杂呢? 确实有点复杂,不要害怕。 有了前面精准的需求分析,我们只需要按照功能点一步步实现即可。 对于我们常用的表格组件、模态组件等需要考虑很多使用场景和功能点。 例如antd的table组件就暴露了几十个属性。 如果具体需求没有明确清楚,那么实现这样的组件是非常麻烦的。 。 我们来看看具体的实现。

2.基于react 2.1实现一个Drawer组件。 抽屉组件框架设计

首先我们先根据需求写组件框架,这样前面写的业务逻辑会更清晰:

import PropTypes from 'prop-types'
import styles from './index.less'

/**
* Drawer 抽屉组件
* @param {visible} bool 抽屉是否可见
* @param {closable} bool 是否显示右上角的关闭按钮
* @param {destroyOnClose} bool 关闭时销毁里面的子元素
* @param {getContainer} HTMLElement 指定 Drawer 挂载的 HTML 节点, false 为挂载在当前 dom
* @param {maskClosable} bool 点击蒙层是否允许关闭抽屉
* @param {mask} bool 是否展示遮罩
* @param {drawerStyle} object 用来设置抽屉弹出层样式
* @param {width} number|string 弹出层宽度
* @param {zIndex} number 弹出层层级
* @param {placement} string 抽屉方向
* @param {onClose} string 点击关闭时的回调
*/

function Drawer(props) {
const {
closable = true,
destroyOnClose,
getContainer = document.body,
maskClosable = true,
mask = true,
drawerStyle,
width = '300px',
zIndex = 10,
placement = 'right',
onClose,
children
} = props

const childDom = (
<div className={styles.xDrawerWrap}>
<div className={styles.xDrawerMask} ></div>
<div
className={styles.xDrawerContent}
{
children
}
{
!!closable && X</span>
}
</div>
</div>

)
return childDom
}

export default Drawer

有了这个框架,我们就一步步实现内容吧。

2.2 实现visible、closeable、onClose、mask、maskClosable、width、zIndex、drawerStyle

之所以先实现这些功能,是因为它们实现起来比较简单,不涉及其他复杂的逻辑。 您只需要向外界公开和使用属性即可。 具体实现如下:

function Drawer(props) {
const {
closable = true,
destroyOnClose,
getContainer = document.body,
maskClosable = true,
mask = true,
drawerStyle,
width = '300px',
zIndex = 10,
placement = 'right',
onClose,
children
} = props

let [visible, setVisible] = useState(props.visible)

const handleClose = () => {
setVisible(false)
onClose && onClose()
}

useEffect(() => {
setVisible(props.visible)
}, [props.visible])

const childDom = (
<div
className={styles.xDrawerWrap}
style={{
width: visible ? '100%' : '0',
zIndex
}}
>
{ !!mask && <div className={styles.xDrawerMask} onClick={maskClosable ? handleClose : null}></div> }
<div
className={styles.xDrawerContent}
style={{
width,
...drawerStyle
}}>
{ children }
{
!!closable && <span className={styles.xCloseBtn} onClick={handleClose}>X</span>
}
</div>
</div>

)
return childDom
}

上述实现过程中值得注意的是,我们的组件设计采用了reacthooks技术,这里使用了useState和useEffect。 如果不懂的话可以去官网学习。 这很简单。 如果有不懂的可以和作者交流或者在评论区发帖提问。 抽屉动画是通过控制抽屉内容的长度来实现的。 对于overflow:hidden,我会单独附上css代码供大家参考。

2.3 实现destroyOnClose

destroyOnClose主要用于消除组件缓存。 更常见的场景是输入文本。 例如,当我的抽屉的内容是表单创建页面时,我们关闭抽屉,希望用户在表单中输入的内容被清除,以保证上次进入的用户可以重新创建它,而实际情况是,如果我们不销毁抽屉里的子组件,子组件的内容就不会被清除,而用户上次打开时又开始了之前的输入,这显然是不合理的。 如右图所示:

为了消除缓存,内部组件首先必须重新渲染,所以我们可以通过一个状态来控制它。 如果用户明确指定该组件在关闭时应该被销毁,那么我们就会更新状态,所以这个子元素不会有缓存。 具体实现如下:

css3 关闭按钮-《精通react/vue组件设计》与ReactPortals合作实现强大功能

function Drawer(props) {
// ...
let [isDesChild, setIsDesChild] = useState(false)

const handleClose = () => {
// ...
if(destroyOnClose) {
setIsDesChild(true)
}
}

useEffect(() => {
// ...
setIsDesChild(false)
}, [props.visible])

const childDom = (
<div className={styles.xDrawerWrap}>
<div className={styles.xDrawerContent}
{
isDesChild ? null : children
}

</div>
)
return childDom
}

上面的代码中,我们省略了一些不相关的代码css3 关闭按钮,主要关注isDesChild和setIsDesChild。 该属性用于根据用户传入的destroyOnClose属性来确定是否应该更新状态。 如果destroyOnClose为true,则表示需要更新。 这时当用户点击关闭按钮时,组件就会重新渲染。 当用户再次点击抽屉时,我们会根据props.visible的变化重新渲染子组件,从而实现组件卸载的完整过程。

2.4 实现getContainer

getContainer主要用于控制抽屉组件的渲染位置。 默认情况下,它将渲染在 body 下。 为了提供更灵活的配置,我们需要允许抽屉可以渲染在任何元素下。 如何才能实现这一目标? 我们可以使用ReactPortals来进行此实现,具体api介绍如下:

Portal 提供了一个优秀的解决方案,用于将子节点渲染到存在于父组件之外的 DOM 节点。 第一个参数 ( child ) 是任何可渲染的 React 子项,例如元素、字符串或片段。 第二个参数(容器)是一个 DOM 元素。

具体使用如下:

render() {
// `domNode` 是一个可以在任何位置的有效 DOM 节点。
return ReactDOM.createPortal(
this.props.children,
domNode
);
}

那么基于这个api,我们可以在任意元素下渲染抽屉。 具体实现如下:

const childDom = (
<div
className={styles.xDrawerWrap}
style={{
position: getContainer === false ? 'absolute' : 'fixed',
width: visible ? '100%' : '0',
zIndex
}}
>
{ !!mask && <div className={styles.xDrawerMask} onClick={maskClosable ? handleClose : null}></div> }
<div
className={styles.xDrawerContent}
style={{
width,
[placement]: visible ? 0 : '-100%',
...drawerStyle
}}>
{
isDesChild ? null : children
}
{
!!closable && <span className={styles.xCloseBtn} onClick={handleClose}>X</span>
}
</div>
</div>

)

return getContainer === false ? childDom
: ReactDOM.createPortal(childDom, getContainer)

css3 关闭按钮-《精通react/vue组件设计》与ReactPortals合作实现强大功能

因为这里getContainer要支持三种情况,一种是用户没有配置属性,所以默认挂载在body下面,而用户传递的值是false,所以是最近的父元素,如果他传递一个dom元素,这样就会挂载到这个元素下面,所以我们会根据情况考虑上面的代码,还有一点需要注意的是,当抽屉打开的时候,我们要让父元素溢出并隐藏它,所以我们需要在这里设置它:

useEffect(() => {
setVisible(() => {
if(getContainer !== false && props.visible) {
getContainer.style.overflow = 'hidden'
}
return props.visible
})
setIsDesChild(false)
}, [props.visible, getContainer])

关闭时恢复逻辑父级的溢出,防止外部样式受到影响:

const handleClose = () => {
onClose && onClose()
setVisible((prev) => {
if(getContainer !== false && prev) {
getContainer.style.overflow = 'auto'
}
return false
})
if(destroyOnClose) {
setIsDesChild(true)
}
}

2.5 实现放置

Placement主要用于控制抽屉的弹出方向。 它可以从左侧或右侧弹出。 实现过程比较简单。 我们主要需要用更多的属性来动态改变定位属性。 这里我们就要用到新版本es的新特性,对象的变量属性。 核心代码如下:

<div 
className={styles.xDrawerContent}
style={{
width,
[placement]: visible ? 0 : '-100%',
...drawerStyle
}}>

</div>

这样,无论是上、下、左、右,都可以完美实现。

2.6 强力支持,我们使用react提供的propTypes工具:

import PropTypes from 'prop-types'
// ...
Drawer.propTypes = {
visible: PropTypes.bool,
closable: PropTypes.bool,
destroyOnClose: PropTypes.bool,
getContainer: PropTypes.element,
maskClosable: PropTypes.bool,
mask: PropTypes.bool,
drawerStyle: PropTypes.object,
width: PropTypes.oneOfType([
PropTypes.string,
PropTypes.number
]),
zIndex: PropTypes.number,
placement: PropTypes.string,
onClose: PropTypes.func
}

关于prop-types的使用,官网上有非常详细的案例。 这里是oneOfType的用法。 它用于支持可能是多种类型之一的组件。 组件相关的css代码如下:

.xDrawerWrap {
top: 0;
height: 100vh;
overflow: hidden;
.xDrawerMask {
position: absolute;
left: 0;
right: 0;
top: 0;
bottom: 0;
background-color: rgba(0, 0, 0, .5);
}
.xDrawerContent {
position: absolute;
top: 0;
padding: 16px;
height: 100%;
transition: all .3s;
background-color: #fff;
box-shadow: 0 0 20px rgba(0,0,0, .2);
.xCloseBtn {
position: absolute;
top: 10px;
right: 10px;
color: #ccc;
cursor: pointer;
}
}
}

通过以上步骤,一个功能强大的抽屉组件就完成了。 你可以去官网了解代码中cssmodule和classnames的使用。 这很简单。 不懂的可以在评论区提问。 立即回答。

终于

未来,作者将继续实现

和其他组件一样,回顾一下笔者多年的组件化历程。

如果您想获取组件设计系列完整源码,或者想学习更多H5游戏、webpack、node、gulp、css3、javascript、nodeJS、canvas数据可视化等后端知识和实战。 ,欢迎关注公众号“趣谈后端”学习讨论,共同探索后端的边界。

收藏 (0) 打赏

感谢您的支持,我会继续努力的!

打开微信/支付宝扫一扫,即可进行扫码打赏哦,分享从这里开始,精彩与您同在
点赞 (0)

悟空资源网 css3 css3 关闭按钮-《精通react/vue组件设计》与ReactPortals合作实现强大功能 https://www.wkzy.net/game/159937.html

常见问题

相关文章

官方客服团队

为您解决烦忧 - 24小时在线 专业服务