React组件库封装初探--Modal

Madal组件实现基本简介

  • 类似于antd实现的modal组件,首先基本结构分析:

    1. modal-mask遮罩层
    2. modal-warp内容包装层
    3. modal主体内容层,包含:titlecontentfooterclose-btn
    • 固定定位布局,全屏遮盖显示,所以内容自定义
  • 实现功能目标:

    1. 两种调用方式一些内容Modal.confirm({...props})
    2. 遮罩层、footerclose-btn的显示与否,单击是否可关闭
    3. 其他必备功能

结构布局攻克

  • 基本布局
// 遮罩层需要实现全屏遮罩 // 内容层高度可自定义
// 右上角关闭按钮
+
} // 主内容
//title标题
{title}
//body用户输入内容
{children}
// footer底部按钮
  1. 遮罩层全屏覆盖

    • position: fixed定位
    • 全屏实现
        top: 0;
        right: 0;
        bottom: 0;
        left: 0;
        z-index: 1000;
  2. 内容层

    • position: fixed定位(modal-warp层)
    • warp层的布局大小考虑
    1. 全屏:如果warp层实现全屏,由于和mask层为兄弟组件,导致warp层位于mask层之上,后面对mask层单击可关闭功能易出现单击不到,因为被全屏的warp层遮挡(可考虑使用事件委托,将单击事件绑定至第一个父组件,通过判断去除modal层的单击,虽然单击的还是warp层);
    2. 大小跟随modal:及设置warp层的大小刚好为其内容modal,这样就不会覆盖全部mask层,但是,后期对传入设置是否显示mask层的功能有所影响(因为warp层不全屏,如果mask设置不显示,会导致用户可以操作到底下主内容),可考虑mask的显隐通过visibility: hidden控制.

基本功能逻辑实现

  • 基本对外接口(函数式)
const Modal = ({
    visible=false,
    style,
    width= 520,
    zIndex=1000,
    centered=false,
    title='title',
    footer,
    wrapClassName='',
    okText='确定',
    okType='primary',
    cancelText='取消',
    cancelType='default',
    closable= true,
    onOk=() => {},
    onCancel=() => {},
    mask=true,
    maskClosable= true,
    children='Basic body'
}) => {
    return (
        visible ?
        ReactDOM.createPortal(
....
,document.querySelector('body')) : null ) }
  • 组件采用函数无状态编程,Modal的显隐由外部控制,内部不控制;
  • 组件的挂载使用ReactDOM.createPortal(child,container)挂载至body
  • 基本使用形式
import React,{ PureComponent } from 'react';
import { Modal,Button } from 'lwh_react';

export default class baseModal extends PureComponent {
    state = {
        visible: false
    }

    showModal = () => {
        this.setState({
            visible: true
        })
    }
    onCancel = () => {
        console.log('cancel')
        this.setState({
            visible: false
        })
    }
    onOk = () => {
        console.log('ok')
        this.setState({
            visible: false
        })
    }

    render() {
        const { visible } = this.state;
        return (
            
简单基本用法:
modal提示内容
) } }
  • 效果


升级篇Modal.method()的攻克

  • 如何实现类似antd中modal.method的方法调用弹窗形式(且调用后返回一个引用包含{update, destroy}可控制弹窗):

    • Modal.info({...})
    • Modal.success({...})
    • Modal.error({...})
    • Modal.warning({...})
    • Modal.confirm({...})
  1. method()是Modal的方法即先给组件Modal增加对应方法,返回一个对象;

    • 通过在method(props)方法中将其方法参数作为组件Modal的props传入,并render(Modal);
    • 需要返回一个对象包含{update, destroy}基本代码如下:
['confirm','info','success','error','warning'].forEach(item => {
    // eslint-disable-next-line react/no-multi-comp
    Modal[item] = ({ ...props}) => {
        let div = document.createElement('div');
        let currentConfig = Object.assign({}, props);
        document.body.appendChild(div);
        // 使用高阶组件剔除Method()调用形式不可配置的props和默认值
        const FunModal = HOCModal(Modal);
        // 关闭
        const destroy = () => {
            const unmountResult = ReactDOM.unmountComponentAtNode(div);
            if (unmountResult && div.parentNode) {
                div.parentNode.removeChild(div);
            }
        }
        const render = (config) => {
            //name传入调用的方法名,用于区分使用不同footer和Icon
            ReactDOM.render(, div);
        }
        // 更新
        const update = (newConfig) => {
            currentConfig = Object.assign({}, currentConfig,newConfig);
            render(currentConfig);
        }
        render(currentConfig);
        return {
            destroy: destroy,
            update: update
        }
    }
});
  1. 因为Modal.method()调用形式可使用的配置props与中的配置项和默认值有所不同;
  2. Modal.confirm({})中不可配置footer;Modal.info({})footer底部默认应该为一个button,且默认值为我知道了
  3. 再如Modal.method()不需要传递visible,而形式需要传入;
  4. 再比如Modal.method()中没有children,而使用content作为内容的传递,所以需要适配下;
  • 所以这里考虑使用一个高阶组件HocModal对传给Modal的props进行部分剔除和默认值修改
const HOCModal = (Component) => {
    //剔除出visible,footer,closable,使其不可配,赋予永久默认值
    return ({
        visible,
        footer,
        closable,
        okText='知道了',
        okType='primary',
        onOk=() => {},
        onCancel=() => {},
        maskClosable= false,
        content='Basic body',
        name,
        destroy,
        ...props
    }) => {
        // 修改onOk方法传入关闭Modal方法destroy();
        const onOk_1 = () => {
            onOk();
            destroy();
        }
        // 修改onCancel方法传入关闭Modal方法destroy();
        const onCancel_1 = () => {
            onCancel();
            destroy();
        }
        // Modal底部footer固定使用这里为默认值,且不可自定义footer,如果调用的是confirm返回undefined走Modal的默认配置,其他则只显示一个OK、button
        // eslint-disable-next-line react/no-multi-comp
        const Footer = () => (
            name == 'confirm' ? undefined : 
        )
        return (
        
    )
    }
}
  • 使用测试
const ModalConfirm = () => {
    const onInfo = () => {
        Modal.info({
        title: 'Info',
        content: (
          

some messages...some messages...

some messages...some messages...

), onOk() {} }); } const showDeleteConfirm = () => { const modal = Modal.confirm({ title: '你确定需要删除该项么?', content: '一些删除提示内容', okText: '删除', okType: 'danger', cancelText: '取消', onOk() { console.log('OK'); }, onCancel() { console.log('Cancel'); } }); console.log(modal); } return (
) }
  • 结果展示

其他优化

  1. 显隐的动画过渡;
  2. 组件的保留,这里只实现了关闭即摧毁;优化为可选择不摧毁只是隐藏;
  3. 支持异步加载关闭
“积跬步、行千里”—— 持续更新中~,喜欢的话留下个赞和关注哦!
  • 往期经典好文:

    • 你不知道的CORS跨域资源共享
    • 性能优化篇---Webpack构建速度优化
    • 团队合作必备的Git操作
    • 使用pm2部署node生产环境
  • 下期考虑Carousel走马灯封装

你可能感兴趣的:(前端,react.js,html5,javascript,html)