vue的封装组件大家都知道,通过props,event,slot 即可实现一个特殊的弹框组件。代码如下:
<template> <div> <Alert v-if="show">这是一条提示信息</Alert> <button @click="show = true">显示</button> </div> </template> <script>import Alert from '../component/alert.vue'; export default { components: { Alert }, data () { return { show: false } } } </script>
但是这种弹框通常都有以下缺点:
1. 每个使用的地方都必须要先引用注册。
2. 需要预先将封装好的 组件放置在模板中。
3. 需要额外的data来控制 弹框 的显示状态。
4. 弹框的位置,是当前组件位置,并非在body下。并且有可能会被其它组件遮挡。
基于以上问题,所以说,普通的组件封装是对使用者来说不是很友好,如果我们在类似于全局封装的函数中(例如:axios 响应中)引入,则当前这种就比较不好的了。那么如何能实现一个优雅的组件呢?
其实对比原生的 window.alert("这是一个弹框")
我们就可以知道,一个优雅的组件莫过于,能用JavaScript随时随地调用的组件。
类似于:
它可以在任何位置进行使用,无需单独引入,并且接收两个参数。
content: 提示内容
duration: 持续时间,默认1.5秒
最终样子如下:
this.$alert.info({ content: '我是一个弹窗', duration: 3 });
下来我们就一起看看如何实现这个全局弹框组件。
一:我们可以通过最终调用值发现,this.$alert。 很明显指的就是vue的实例。也就是说我们声明完成的一个alert则会是一个js文件,并且我们将它挂载在了 vue.prototype之上。
由此我们可以断定。这个vue文件的main.js中是这样写的
// ... 其余代码省略 import alert from '@/components/alert.js' Vue.prototype.$alert = alert new Vue({ ...其余代码, render: h => h(App) }).$mount('#app')
而我们在调用 this,$alert 的时候初始化了一个info方法,那么这个方法一定是存在于 alert这个js文件中。并且是一个初始化弹窗方法, 接收两个参数,一个是弹窗内容,一个是弹窗显示时间。
// alert.js ... 省略代码 const notice = ({ content = '', duration = 1.5 }) =>{ // 传递给弹窗的实际组件方法中 // 此处暂不编写。 } export default { info(options){ return notice(options) } }
到目前为止,我们根据最后弹窗的使用方法,简单的推断出了 alert.js这个文件。但是有这个文件可不够,我们还是需要一个页面。也就是说,我们也还是需要一个dom 展示页面的,不然我们的弹窗样式无法定义。
所以我们基于 alert.js的同级目录,新增一个页面,alert.vue 用来展示 弹框的样式
<template> <div class="alert"> <div class="alert-main" v-for="item in notices" :key="item.name"> <div class="alert-content">{{ item.content }}</div> </div> </div> </template> <script>export default { data () { return { notices: [] } } } </script> <style>.alert{ position: fixed; width: 100%; top: 16px; left: 0; text-align: center; pointer-events: none; } .alert-content{ display: inline-block; padding: 8px 16px; background: #fff; border-radius: 3px; box-shadow: 0 1px 6px rgba(0, 0, 0, .2); margin-bottom: 8px; } </style>
因为我们通过已知的业务,通知可以是多条的,并且每一条都可以有不同的展示时间,所以在弹窗展示这里,我们用notices 数组来保存一个个的单内容。
细心的朋友会发现,这个页面与以往的组件页面不一致,那是因为,我们本次的组件是需要JavaScript来调用的,而非传递数据改变其状态调用。所以我们不需要props 以及 event事件。
接下来,只要给数组 notices 增加数据,这个提示组件就能显示内容了,我们先假设,最终会通过 JS 调用 Alert 的一个方法 add,并将 content 和 duration 传入进来:
所以我们修改 alert.vue这个页面的内容
<script> let seed = 0; function getUuid() { return 'alert_' + (seed++); } export default { data () { return { notices: [] } }, methods: { add (notice) { const name = getUuid(); let _notice = Object.assign({ name: name }, notice); this.notices.push(_notice); // 定时移除,单位:秒 const duration = notice.duration; setTimeout(() => { this.remove(name); }, duration * 1000); }, remove (name) { const notices = this.notices; for (let i = 0; i < notices.length; i++) { if (notices[i].name === name) { this.notices.splice(i, 1); break; } } } } } </script>
add方法为,我们每一条传递进来的提示数据,我们通过添加一个name字段,来表示这条数据是唯一的值。在每一次add的时候都push到notices 的数组中,并且根据传递进来的时间,利用 setTimeout 设置其展示时间。
同时,当传递进来的时间到期后,我们通过remove 方法将其从 notices 的数组中进行移除。
所以在这一步,我们就必须将当前的弹窗进行实例化,因为我们不是常规使用组件方法,所以这是哈哈我们使用Vue.extend 或者 newVue 实例化后,利用$mount 挂载到body下。
// notification.js import alert from './alert.vue'; import Vue from 'vue'; alert.newInstance = properties => { const props = properties || {}; const Instance = new Vue({ data: props, render (h) { return h(alert, { props: props }); } }); const component = Instance.$mount(); document.body.appendChild(component.$el); const alerts = Instance.$children[0]; return { add (noticeProps) { alerts.add(noticeProps); }, remove (name) { alerts.remove(name); } } }; export default alert;
notification.js 并不是最终的文件,它只是对 alert.vue 添加了一个方法 newInstance,这个方法就是实例化后的 alert 方法。我们通过实例化alert后,获取到alerts。它就是组件alert的组件实例,在newInstance里,我们使用闭包暴漏了两个方法,一个add,一个 remove。
最后我们就可以补全我们最开始预留的代码 alert.js
/ alert.js import Notification from './notification.js'; let messageInstance; function getMessageInstance () { messageInstance = messageInstance || Notification.newInstance(); return messageInstance; } function notice({ duration = 1.5, content = '' }) { let instance = getMessageInstance(); instance.add({ content: content, duration: duration }); } export default { info (options) { return notice(options); } }
这就是我们本期所说的,vue之函数式弹窗组件的生成。