全局提示组件在前端中算是比较重要的,在开发业务时候肯定能用的上,毕竟任何报错只要提示“服务器异常”就可以完美把锅扔给后台(手动滑稽)
全局提示组件在人气比较🔥的 UI 组件库必有他身影,可能叫法不太相同,有叫 message、toast、alert 等,但就是这么一玩意。
拿 ant-design-vue 组件库为例
其核心代码
message.info('This is a normal message')
他是API的方式进行调用组件,以平常使用components注册组件,之后在template中使用的方式不太相同
想要实现这个,其实并不困难
但在之前我想声明一下:ant-design-vue 的 message 做个type分类,有info、success、error等。但为了读者方便理解,我们统一就用message来进行调用,读者可以根据本文提供的demo源码自行进行调整(贴一下 ant-design-vue 源码)
首先,我们在 components 目录下创建 message 目录,同时创建 Message.vue 和 index.js 文件
然后在 Message.vue 中写
<!-- Message.vue --> <template> <div>{{ content }}</div> </template> <script> export default { name: 'Message', props: { content: { type: String, default: '' } } } </script>
再者在 index.js 中写
// index.js import { render, createVNode } from 'vue' import Message from './Message.vue' export default function message (content) { const div = document.createElement('div') const vm = createVNode(Message, { content }) render(vm, div) document.body.appendChild(div) }
最后在 app.vue 引入使用
<!-- app.vue --> <template> </template> <script> import message from './components/message' export default{ setup() { message('消息组件1') message('消息组件2') } } </script>
效果:
ok,这样就实现了以API的方式进行调用组件
其核心代码其实只有以下
const div = document.createElement('div') const vm = createVNode(Message, { content }) render(vm, div) document.body.appendChild(div)
createVNode 可以认为就是h函数,支持直接转成虚拟dom对象
再去调用 render,渲染至div下,再将div插入body中
由于我们做的是全局组件,应该不被任何因素干扰,比如 vue-router
但是上面例子我们会发现,我们只要调用一个 message 方法,就要创建一个div插入至body,这显然不是符合vue数据驱动视图的理念
那,接下里进行改造
先从 Message.vue 文件开始
我们先定义一个消息列表messages,之后提供添加 add 和删除delete消息列两方法,再暴露add方法出去
当然,删除消息是需要根据id进行删除,可不能瞎删
<!-- Message.vue --> <script> import { ref, unref } from 'vue' export default { name: 'Message', setup (props, { expose }) { // 消息列表 const messages = ref([]) let id = 0 // 生成id const uuid = () => `message_${id++}` // 添加消息对象 const add = (message) => { const id = uuid() const _message = { ...message, id } unref(messages).push(_message) const { duration = 3 } = message // 设置定时器 const timer = setTimeout(() => { clearTimeout(timer) remove(id) }, duration * 1000) } // 根据 id 删除消息对象 const remove = (id) => { messages.value = unref(messages).filter(message => message.id !== id) } // 暴露出add 和 remove expose({ add }) return { messages } } } </script>
用v-for把消息列表渲染出来,同时使用transition-group做一些列表过渡动画
<!-- Message.vue --> <template> <transition-group class="message" tag="div" > <div class="message-content" v-for="message in messages" :key="message.id" > {{ message.content }} </div> </transition-group> </template>
再编写一点消息列表样式和其弹出动画样式
<!-- Message.vue --> <style scoped> .message { position: fixed; z-index: 999; top: 10px; left: 50%; transform: translateX(-50%); } .message-content { padding: 8px 16px; border-radius: 3px; box-shadow: 0 1px 6px rgba(0, 0, 0, .2); background: #fff; margin-bottom: 20px; } .v-enter-active, .-leave-active { transition: all 200ms ease-in; } .v-enter-from, .v-leave-to { opacity: 0; transform: translateY(-30px); } </style>
再改造一下入口文件
之前我们发现在 app.vue 中调用一次 message方法,就会一次 dom 操作,那我们使用闭包写一个单例模式进行改造
再者我们在 Message.vue 暴露出add的方法可以直接进行操作消息列表
// index.js import { render, createVNode } from 'vue' import Message from './Message.vue' let vm // 使用单例模式,不再重新插入body function getMessageInstance () { if (vm) return const div = document.createElement('div') vm = createVNode(Message) render(vm, div) document.body.appendChild(div) } export default function message (content = '', duration) { getMessageInstance() vm.component.exposed.add({ content, duration }) }
最后在 app.vue 文件中随便编写一些测试代码
<!-- app.vue --> <template> <button @click="onClick">测试</button> </template> <script> import message from './components/message' export default{ setup() { message('消息组件1', 4) message('消息组件2', 2) let index = 3 const onClick = () => { message(`消息组件${index++}`) } return { onClick } } } </script>
看看最终效果
好,这就是全局提示组件设计大致思路。
回顾一下会发现,其实Vue的组件开发依旧绕不开 JavaScript,可见 JavaScript 在前端的份量。
总结
到此这篇关于Vue全局提示组件的文章就介绍到这了,更多相关Vue全局提示组件内容请搜索阿兔在线工具以前的文章或继续浏览下面的相关文章希望大家以后多多支持阿兔在线工具!