「我们一起做组件库🌻」虚拟消息队列?message组件有何不同?(VersakitUI开发实录)

Hi, 这里是和朋友一起开发组件库的JustHappy,今天我们来做个message组件,这和我们平时编写的一般的Vue组件可能有所不同,不能单纯的借助于template范式去构建,所以具体是怎么样的呢?我们来聊聊吧!

我们的组件会是什么样的?🧐

就像下面这样,是一个显示在最上层的消息提示组件,先进先出......

什么是消息队列?什么是虚拟消息队列?🔍

在这里我们提到一个概念:虚拟消息队列。这其实是一个很常见的设计模式,用于在前端界面中有序管理通知类UI组件的显示:

  • 虚拟:不是依赖真实的数据结构(比如链表、队列),而是通过状态和DOM元素的控制来模拟队列效果;
  • 先进先出(FIFO) :保证每条信息都是排好队的,前一条展示完才轮到下一条;
  • 并发控制:最多展示几条、展示多久、自动销毁等,都可以通过"虚拟消息队列"控制。

这样,用户体验就更加可控、整洁,也避免了多条消息同时展示导致的界面混乱。

template VS render()?

聊一聊编程范式👾

什么是编程范式?如果你有编写过C语言 那你一定听过 面向过程编程 或者说是 命令式编程,如果你学习过 JavaC++ 那你对 面向对象编程 一定不陌生。

那难道我只能使用C语言编写 面向过程编程 的程序吗?我只能使用 面向对象编程 去编写Java 或者 C++ 吗?

答案当然是否定的!

以上这些其实都是我们说的 编程范式(Programming Paradigm) ,可以简单理解为编程的方法论,是解决问题的一种思想方式。

声明式 ? 函数式?

Vue 是一个以"声明式"编程为主导的前端框架,而template 模板是这种范式的代表:

html 复制代码
<template>
  <div class="msg">{{ text }}</div>
</template>

我们只需要"描述"希望界面显示成什么样,Vue 会帮我们处理后续的渲染、更新等。这个体验几乎上和编写HTML没什么区别,这也是Vue渐进式的一种体现

template 完美吗?🙃

当然也不是完美的,比如我觉得至少是有一下几点的:

  1. 语法限制:对于复杂的条件渲染、动态逻辑、嵌套结构来说,template 有点吃力;
  2. 灵活性不足:和 JSX 比较起来,template 更加"死板",不利于灵活构建动态组件结构;
  3. 功能隔离不明显:逻辑代码和视图容易耦合在一起,稍复杂点维护起来很头疼。

那我们还有别的选择吗?🛠

当然有,Vue 提供了另一套思路 ------ render()函数 + h()函数去编写JSX

Vue官网也提供了相对应的简单教程:渲染函数 & JSX | Vue.js

总得来说会像下面这样

js 复制代码
import { h } from 'vue'

export default {
  render() {
    return h('div', { class: 'msg' }, 'Hello Render')
  },
}

就是 "函数式组件" 的雏形,我们通过代码逻辑生成虚拟DOM结构,灵活性非常强!

配合 Composition API、动态挂载、VNode 创建等功能,我们就可以实现那些"不写在 template 中"的组件。

传统Vue组件和"函数式调用组件"??

什么是服务式组件?

服务式组件(Service-based Component)是一种 "用函数调用来使用组件"的模式,它是 Vue 组件使用的一种程序式调用方式(Programmatic usage)

它不依赖于 <template> 中的标签,也不会在 .vue 文件里声明组件引用,而是:

  • 通过 createVNode() 创建组件实例
  • render() 手动挂载到 DOM
  • 控制生命周期、传参、展示与销毁

适用于全局唯一、轻量弹出、无需响应父子通信的组件,比如:

  • Message 消息提示
  • Notification 通知弹窗
  • Modal 弹窗确认框
  • Toast 提示气泡

一个简单的示例实现Message的逻辑

我们也是需要依托template模板去构建插槽

html 复制代码
<!-- Message.vue -->
<template>
  <div class="message" :class="type">{{ message }}</div>
</template>
<script setup>
defineProps({ type: String, message: String })
</script>
<style scoped>
.message {
  padding: 10px;
  margin: 8px;
  border-radius: 4px;
  color: white;
  background-color: gray;
}
.message.success {
  background-color: green;
}
</style>

但是核心的虚拟消息队列会是像下面这样的

js 复制代码
// message.ts
import { h, render } from 'vue'
import Message from './Message.vue'

const queue: HTMLElement[] = []

export function showMessage({ type, message, duration = 2000 }) {
  const container = document.createElement('div')
  document.body.appendChild(container)
  const vnode = h(Message, { type, message })
  render(vnode, container)

  queue.push(container)

  setTimeout(() => {
    render(null, container)
    container.remove()
    queue.shift()
  }, duration)

如果您想看实际的实现,可以点击这里去到源码🚀🚀

最后......

欢迎大家 star✨ 支持,您的反馈是我们前进的动力!✨

相关推荐
风无雨1 小时前
react antd 项目报错Warning: Each child in a list should have a unique “key“prop
前端·react.js·前端框架
人无远虑必有近忧!1 小时前
video标签播放mp4格式视频只有声音没有图像的问题
前端·video
记得早睡~4 小时前
leetcode51-N皇后
javascript·算法·leetcode·typescript
安分小尧6 小时前
React 文件上传新玩法:Aliyun OSS 加持的智能上传组件
前端·react.js·前端框架
编程社区管理员6 小时前
React安装使用教程
前端·react.js·前端框架
小小鸭程序员6 小时前
Vue组件化开发深度解析:Element UI与Ant Design Vue对比实践
java·vue.js·spring·ui·elementui
拉不动的猪6 小时前
vue自定义指令的几个注意点
前端·javascript·vue.js
yanyu-yaya6 小时前
react redux的学习,单个reducer
前端·javascript·react.js
陌路物是人非6 小时前
SpringBoot + Netty + Vue + WebSocket实现在线聊天
vue.js·spring boot·websocket·netty
skywalk81636 小时前
OpenRouter开源的AI大模型路由工具,统一API调用
服务器·前端·人工智能·openrouter