基础版
用过vant的家人们肯定都知道其中的Dialog组件是支持函数调用和组件调用的,组件调用就不阐述了,其中函数调用的使用方式如下:
Dialog.alert({
title: '标题',
message: '弹窗内容',
})
.then(() => {
// on close
});
产生的效果图:

感觉Dialog的函数调用特别方便啊,而vant中有个跟Dialog很像(自认为)的组件,那就是ActionSheet动作面板这个组件。
为什么说像呢,因为他们其实都是触发了点击事件以后出来一个弹框,然后弹框上会有对应的事件,对此,提出:为什么ActionSheet这个组件为什么没有函数调用的方法呢?(其实是我导师提的,我发散性思维不好)于是就有了这篇文章!
先捋捋思路,想要封装成函数调用,也就是在业务代码中我们可以直接使用函数就可以渲染相关的页面,那怎么做到不在template中写对应的组件但却可以进行渲染呢?说明是需要使用到动态渲染操作的,也就是在触发某一事件的时候才会渲染相关的组件,那么我们得先把对应的组件写好先。
新建一个showActionSheet文件夹,再新建一个actionSheet.vue文件:
<template>
<van-action-sheet
v-model="visible"
:actions="actionsList"
@select="selectAction"
@closed="close"
get-container="body"
></van-action-sheet>
</template>
<script>
export default {
name: "actionSheet",
props: {
visible: {
type: Boolean,
default: false
},
actionsList: {
type: Array,
default: () => []
},
selectAction: Function
},
methods: {
close() {
console.log("close");
this.visible = false;
this.$emit("close");
}
}
};
</script>
然后再在该文件夹下新增一个showActionSheet.js文件,在这个文件里定义函数并抛出:
import Vue from "vue";
import actionSheet from "./actionSheet.vue";
// 定义showActionSheet函数,接收对象参数
function showActionSheet({actionsList, selectAction }) {
// 使用Vue.extend方法将actionSheet组件扩展为一个新的构造器,该构造器可以用来创建新的实例
const actionSheetConstructor = Vue.extend(actionSheet);
// 设置visible控制组件是否可见,初始值为true
let visible = true;
// 创建新的vue实例,并将对应的参数传递给这个实例
const instance = new actionSheetConstructor({
propsData: { visible, actionsList, selectAction }
});
// 挂在组件:使用$mount方法将该实例挂载到一个虚拟DOM节点上,若未指定挂载点,则会默认挂载到内存的虚拟DOM节点
instance.$mount();
// 将组件的根元素添加到页面的body中,使其在页面上可见
document.body.appendChild(instance.$el);
// 监听close事件
instance.$on("close", () => {
// 若close事件触发,则执行移除组件根DOM元素操作
document.body.removeChild(instance.$el);
// 销毁vue实例,释放资源
instance.$destroy();
});
// 返回vue实例,使得调用者可通过这一步获取这个vue实例
return instance;
}
export default showActionSheet;
在想要调用的代码中直接可以通过如下代码进行函数调用:
methods: {
test() {
const actionsList = [{ name: "选项一" }, { name: "选项二" }, { name: "选项三" }]
showActionSheet({
actionsList: actionsList,
selectAction: (selectObj) => {
console.log("我选择了", selectObj)
}
})
},
}
由此,成功实现,效果图:

进阶版(Promise调用)
第一版只是单纯通过传入对应的参数设置相关事件,但实际使用中,为了更符合开发规范,更贴合开发思路,决定结合Promise进行进一步的改造。
actionSheet.vue文件主要的修改点在于将对应的事件和参数都进行了接入,若用户在调用时传入对应参数,即刻修改组件上的对应方法。
<template>
<van-action-sheet
v-model="visible"
:title="title"
:description="description"
:actions="actionsList"
:cancel-text="cancelText"
:round="round"
:lock-scroll="lockScroll"
:lazy-render="lazyRender"
:close-on-popstate="closeOnPopstate"
:close-on-click-action="closeOnClickAction"
:close-on-click-overlay="closeOnClickOverlay"
:safe-area-inset-bottom="safeAreaInsetBottom"
get-container="body"
@select="onSelect"
@cancel="cancel"
@open="open"
@opened="opened"
@close="close"
@closed="onClosed"
@click-overlay="clickOverlay"
></van-action-sheet>
</template>
<script>
export default {
name: "actionSheet",
props: {
visible: {
type: Boolean,
default: false
},
title: {
type: String,
default: ""
},
description: {
type: String,
default: ""
},
actionsList: {
type: Array,
default: () => []
},
cancelText: {
type: String,
default: "取消"
},
duration: {
type: Number,
default: 0.3
},
round: {
type: Boolean,
default: true
},
overlay: {
type: Boolean,
default: true
},
lockScroll: {
type: Boolean,
default: true
},
lazyRender: {
type: Boolean,
default: true
},
closeOnPopstate: {
type: Boolean,
default: false
},
closeOnClickAction: {
type: Boolean,
default: true
},
closeOnClickOverlay: {
type: Boolean,
default: true
},
safeAreaInsetBottom: {
type: Boolean,
default: true
},
cancel: {
type: Function,
default: () => ({})
},
open: {
type: Function,
default: () => ({})
},
opened: {
type: Function,
default: () => ({})
},
close: {
type: Function,
default: () => ({})
},
closed: {
type: Function,
default: () => ({})
},
clickOverlay: {
type: Function,
default: () => ({})
},
select: {
type: Function,
default: () => ({})
}
},
methods: {
onSelect(selectObj) {
this.select(selectObj)
this.$emit("select", selectObj);
},
onClosed() {
this.closed()
this.$emit("closed");
},
}
};
</script>
showActionSheet.js的修改如下:
import Vue from "vue";
import actionSheet from "./actionSheet.vue";
// 全局定义一个对象
let instance;
// 全局创建一个新的构造函数
const actionSheetConstructor = Vue.extend(actionSheet);
// 定义初始化函数
const initInstance = () => {
// 创建一个新的 Vue 组件实例,并指定初始状态
instance = new actionSheetConstructor({
el: document.createElement("div"),
propsData: {
visible: false,
actionsList: [],
}
});
// 将组件添加到页面上
document.body.appendChild(instance.$el);
// 在注册对应实例后,监听浏览器返回事件,卸载组件
window.addEventListener("popstate", function(event) {
document.body.removeChild(instance.$el);
instance = null;
});
}
// 定义触发函数
const ActionSheet = (options) => {
if (!instance) {
initInstance();
}
Object.assign(instance, {
visible: true,
...options
});
return new Promise((resolve, reject) => {
let selectWatcher
let lastSelectObj;
const handleSelect = (selectObj) => {
lastSelectObj = selectObj
// 判断是否有传入closeOnClickAction这个选项,若未传入或传入为true,说明选择后立刻关闭弹窗,即马上调用resolve
if (options.closeOnClickAction == undefined || options.closeOnClickAction == true) {
resolve(selectObj)
instance.visible = false;
} else if (!selectWatcher) {
// 若为false,那就添加对visible的监听,一旦关闭,马上resolve
selectWatcher = instance.$watch("visible", (val) => {
if (!val) {
resolve(lastSelectObj);
selectWatcher();
}
});
}
};
// 使用on,防止用户若有选择后弹窗不消失,修改选项的情况
instance.$on("select", handleSelect);
使用once的原因是因为关闭只会存在一次操作
instance.$once("closed", () => {
instance.visible = false;
// 及时卸载对应select函数
instance.$off("select", handleSelect);
reject(new Error("closed"));
});
});
};
export default ActionSheet;
调用:
test() {
const actionsList = [{ code: "1", name: "选项一" }, { code: "2", name: "选项二" }, { code: "3", name: "选项三" }]
showActionSheet({
actionsList: actionsList
}).then((item) => {
console.log("User selected:", item.name);
}).catch((error) => {
console.log(error.message);
});
}
882

被折叠的 条评论
为什么被折叠?



