「我们一起做组件库🌻」虚拟消息队列?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✨ 支持,您的反馈是我们前进的动力!✨

相关推荐
秋天的一阵风几秒前
Vue3探秘系列— 路由:vue-router的实现原理(十六-上)
前端·vue.js·面试
秋天的一阵风1 分钟前
Vue3探秘系列— 路由:vue-router的实现原理(十六-下)
前端·vue.js·面试
海底火旺21 分钟前
JavaScript中的Object方法完全指南:从基础到高级应用
前端·javascript·面试
海底火旺22 分钟前
JavaScript中的Symbol:解锁对象属性的新维度
前端·javascript·面试
天天扭码22 分钟前
一文吃透 ES6新特性——解构语法
前端·javascript·面试
Kagerou23 分钟前
组件测试
前端
JustHappy26 分钟前
啥是Hooks?为啥要用Hooks?Hooks该怎么用?像是Vue中的什么?React Hooks的使用姿势(上)
前端·vue.js·react.js
张可40 分钟前
历时两年半开发,Fread 项目现在决定开源,基于 Kotlin Multiplatform 和 Compose Multiplatform 实现
android·前端·kotlin
培根芝士1 小时前
electron-updater实现自动更新
javascript·electron
嘻嘻嘻嘻嘻嘻ys1 小时前
《Spring Boot 3 + GraalVM原生镜像实战:云原生时代的毫秒启动与性能调优》
前端·后端