前两天有给大家分享一个 svelte自定义Tabbar+Navbar组件。
明天给大家带来的是最新开发的svelte自定义手机端模态框组件SveltePopup。

如下图:在lib目录下新建一个Popup组件目录。

引入svelte-popup组件

弹窗组件反对组件式(Popup)+函数式(svPopup)两种调用形式。

import Popup, {svPopup} from '$lib/Popup'
  • 组件式调用
<Popup     bind:open={isShowDialog}    xclose    shadeClose="false"    title="题目"    content="显示内容信息"    btns={[        {text: '确认', style: 'color:#f60;', click: () => isShowDialog=false},    ]}    on:open={handleOpen}    on:close={handleClose}>    <svelte:fragment slot="content"><h3>自定义slot插槽显示内容</h3></svelte:fragment></Popup>
  • 函数式调用
let el = svPopup({    title: '题目信息',    content: '<p style='color:#09f;'>展现内容信息</p>',    xclose: true,    shadeClose: false,    btns: [        {text: '勾销', click: () => { el.$set({open: false}) }},        {text: '确认', style: 'color:#f90;', click: () => handleOK},    ],    onOpen: () => {},    onClose: () => {}})

一些简略的弹窗成果通过函数调用更加不便,对于一些功能丰富的弹窗展现,能够应用组件式slot自定义插槽形式展现。

<Popup bind:open={showConfirm} shadeClose="false" title="正告信息" xclose zIndex="2001"    content="<div style='color:#00e0a1;padding:20px 40px;'>确认框(这里是确认框提示信息,这里确认框提示信息,这里是确认框提示信息)</div>"    btns={[        {text: '勾销', click: () => showConfirm=false},        {text: '确定', style: 'color:#e63d23;', click: handleInfo},    ]}/><!-- 底部对话框 --><Popup bind:open={showFooter} anim="footer" type="footer" shadeClose="false" zIndex="1001"    content="确定删除该条数据吗?删除后可在7天之内复原数据,超过7天后数据就无奈复原啦!"    btns={[        {text: '复原', style: 'color:#00e0a1;', click: handleInfo},        {text: '删除', style: 'color:#ee0a24;', click: () => null},        {text: '勾销', style: 'color:#a9a9a9;', click: () => showFooter=false},    ]}/><!-- ActionSheet底部弹出式菜单 --><Popup bind:open={showActionSheet} anim="footer" type="actionsheet" zIndex="2020"    content="弹窗内容,告知以后状态、信息和解决办法,形容文字尽量管制在三行内"    btns={[        {text: '拍照', style: 'color:#09f;', disabled: true, click: handleInfo},        {text: '从手机相册抉择', style: 'color:#00e0a1;', click: handleInfo},        {text: '保留图片', style: 'color:#e63d23;', click: () => null},        {text: '勾销', click: () => showActionSheet=false},    ]}/><!-- ActionSheet底部弹出式菜单(仿微信weui-picker顶部按钮) --><Popup bind:open={showActionPicker} anim="footer" type="actionsheetPicker" round title="题目内容"    btns={[        {text: '勾销', click: () => showActionPicker=false},        {text: '确定', style: 'color:#00e0a1;', click: () => null},    ]}>    <!-- 自定义内容 -->    <ul class="goods-list" style="padding:50px;text-align:center;">        <li>双肩包</li>        <li>鞋子</li>        <li>运动裤</li>    </ul></Popup>

还反对函数式 多层混合 形式调用。

function handleInfo(e) {    console.log(e)    console.log('通过函数形式调用弹窗...')        let el = svPopup({        title: '题目',        content: `<div style="padding:20px;">            <p>函数式调用:<em style="color:#999;">svPopup({...})</em></p>                    </div>`,        btns: [            {                text: '勾销',                click: () => {                    // 敞开弹窗                    el.$set({open: false})                }            },            {                text: '确认',                style: 'color:#09f;',                click: () => {                    svPopup({                        type: 'toast',                        icon: 'loading',                        content: '加载中...',                        opacity: .2,                        time: 2                    })                }            },        ]    })}

同样也反对组件式和函数式混合调用。

svelte-popup参数配置

反对如下 20+ 参数混合调用。

<script>    // 是否关上弹窗bind:open={showDialog}    export let open = false    // 弹窗标识符    // export let id = 'svpopup-' + Math.random().toString(32)    export let id = undefined    // 题目    export let title = ''    // 内容    export let content = ''    // 弹窗类型    export let type = ''    // 自定义弹窗款式    export let popupStyle = undefined    // toast图标    export let icon = ''    // 是否显示遮罩层    export let shade = true    // 点击遮罩层是否敞开    export let shadeClose = true    // 遮罩层透明度    export let opacity = ''    // 是否显示圆角    export let round = false    // 是否显示敞开图标    export let xclose = false    // 敞开图标地位    export let xposition = 'right'    // 敞开图标色彩    export let xcolor = '#333'    // 弹窗动画    export let anim = 'scaleIn'    // 弹窗地位    export let position = ''    // 长按/右键弹窗    export let follow = null    // 弹窗主动敞开工夫    export let time = 0    // 弹窗层级    export let zIndex = 202203    // 弹窗按钮组    export let btns = null    /* export let btns = [        { text: '勾销', style: 'color:#aaa', disabled: true, click: null },        { text: '确定', style: 'color:#f90', click: null }    ] */    // 函数式关上|敞开回调    export let onOpen = undefined    export let onClose = undefined    // 接管函数式移除指令    export let remove = undefined    // ...</script>

弹窗模板及js解决局部。

<div class="sv__popup" class:opened class:sv__popup-closed={closeCls} id={id} style="z-index: {zIndex}" bind:this={el}>    {#if bool(shade)}<div class="vui__overlay" on:click={shadeClicked} style:opacity></div>{/if}    <div class="vui__wrap">        <div class="vui__wrap-section">            <div class="vui__wrap-child {type&&'popupui__'+type} anim-{anim} {position}" class:round style="{popupStyle}">                {#if title}<div class="vui__wrap-tit">{@html title}</div>{/if}                {#if icon&&type=='toast'}<div class="vui__toast-icon">{@html toastIcon[icon]}</div>{/if}                {#if $$slots.content}                    <div class="vui__wrap-cnt"><slot name="content" /></div>                {:else}                    {#if content}<div class="vui__wrap-cnt">{@html content}</div>{/if}                {/if}                <slot />                {#if btns}                    <div class="vui__wrap-btns">                        {#each btns as btn,index}                            <span class="btn"style="{btn.style}" on:click={e => btnClicked(e, index)}>{@html btn.text}</span>                        {/each}                    </div>                {/if}                {#if xclose}<span class="vui__xclose {xposition}" style="color: {xcolor}" on:click={hide}></span>{/if}            </div>        </div>    </div></div>/** * @Desc     svelte自定义挪动端弹窗组件 * @Time     andy by 2022/3/15 * @About    Q:282310962  wx:xy190310 */<script>    // ...    import { onMount, afterUpdate, createEventDispatcher, tick } from 'svelte'    const dispatch = createEventDispatcher()    let opened = false    let closeCls = undefined    let toastIcon = {        loading: '',        success: '',        fail: '',    }    const bool = (boolean) => JSON.parse(boolean) ? true : false    onMount(() => {        console.log('监听弹窗开启...')        return () => {            console.log('监听弹窗敞开...')        }    })    afterUpdate(() => {        // console.log('监听弹窗更新...')        /* if(opened) {            if(!open) {                opened = false                dispatch('close')            }        }else if(open) {            opened = true            dispatch('open')        } */    })    $: if(open) {        show()    }else {        hide()    }    /**     * 关上弹窗     */    async function show() {        if(opened) return        opened = true        dispatch('open')        typeof onOpen == 'function' && onOpen()        zIndex = getZIndex() + 1        // 倒计时敞开        if(time) {            index++            if(timer[index] != null) clearTimeout(timer[index])            timer[index] = setTimeout(() => {                hide()            }, parseInt(time)*1000)        }        // 长按|右键菜单        if(follow) {            // ...        }    }   /**     * 敞开弹窗     */    function hide() {        if(!opened) return        closeCls = true        setTimeout(() => {            opened = false            closeCls = false            open = false            // ...        }, 200)    }    // 点击遮罩层    function shadeClicked() {        if(bool(shadeClose)) {            hide()        }    }        // ...// 临界坐标点    function getPos(x, y, ow, oh, winW, winH) {        let l = (x + ow) > winW ? x - ow : x        let t = (y + oh) > winH ? y - oh : y        return [l, t]    }</script>

一开始开发的时候只反对组件式调用。想着如果能反对函数式调用(插入组件至body,敞开即移除)就好了。

起初在svelte官网发现 new Component 能够传入 props 参数,试了下,发现果然能够实现这种成果。
const component = new Component(options)

如下是官网给的例子
https://svelte.dev/docs#run-t...

import App from './App.svelte';const app = new App({    target: document.body,    props: {        // assuming App.svelte contains something like        // `export let answer`:        answer: 42    }});

于是新建一个popup.js。

import Popup from './Popup.svelte'let uuid = function() {    return 'svpopup-' + Math.floor(Math.random() * 10000)}export function svPopup(options = {}) {    options.id = uuid()    const mountNode = document.createElement('div')    document.body.appendChild(mountNode)    const app = new Popup({        target: mountNode,        props: {            ...options,            open: true,            // 传入函数移除指令            remove() {                document.body.removeChild(mountNode)            }        }    })    return app}export default Popup

通过如上形式就完满解决了导出 组件式+函数式 的调用形式了。

通过一系列的学习,发现svelte还是挺不错的,尤其编译运行够快,体积够小。不过惟一有点遗憾的是还没有找到相似vue全局引入组件的办法。