Hi, 这里是和朋友一起开发组件库的JustHappy,今天我们来做个message组件,这和我们平时编写的一般的Vue组件可能有所不同,不能单纯的借助于template范式去构建,所以具体是怎么样的呢?我们来聊聊吧!
我们的组件会是什么样的?🧐
就像下面这样,是一个显示在最上层的消息提示组件,先进先出......
什么是消息队列?什么是虚拟消息队列?🔍
在这里我们提到一个概念:虚拟消息队列。这其实是一个很常见的设计模式,用于在前端界面中有序管理通知类UI组件的显示:
- 虚拟:不是依赖真实的数据结构(比如链表、队列),而是通过状态和DOM元素的控制来模拟队列效果;
- 先进先出(FIFO) :保证每条信息都是排好队的,前一条展示完才轮到下一条;
- 并发控制:最多展示几条、展示多久、自动销毁等,都可以通过"虚拟消息队列"控制。
这样,用户体验就更加可控、整洁,也避免了多条消息同时展示导致的界面混乱。
template VS render()?
聊一聊编程范式👾
什么是编程范式?如果你有编写过C语言
那你一定听过 面向过程编程
或者说是 命令式编程
,如果你学习过 Java
、C++
那你对 面向对象编程
一定不陌生。
那难道我只能使用C语言
编写 面向过程编程
的程序吗?我只能使用 面向对象编程
去编写Java
或者 C++
吗?
答案当然是否定的!
以上这些其实都是我们说的 编程范式(Programming Paradigm) ,可以简单理解为编程的方法论,是解决问题的一种思想方式。
声明式 ? 函数式?
Vue 是一个以"声明式"编程为主导的前端框架,而template 模板是这种范式的代表:
html
<template>
<div class="msg">{{ text }}</div>
</template>
我们只需要"描述"希望界面显示成什么样,Vue 会帮我们处理后续的渲染、更新等。这个体验几乎上和编写HTML没什么区别,这也是Vue渐进式的一种体现
template 完美吗?🙃
当然也不是完美的,比如我觉得至少是有一下几点的:
- 语法限制:对于复杂的条件渲染、动态逻辑、嵌套结构来说,template 有点吃力;
- 灵活性不足:和 JSX 比较起来,template 更加"死板",不利于灵活构建动态组件结构;
- 功能隔离不明显:逻辑代码和视图容易耦合在一起,稍复杂点维护起来很头疼。
那我们还有别的选择吗?🛠
当然有,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)
如果您想看实际的实现,可以点击这里去到源码🚀🚀
最后......
- 官方文档:versakit.github.io/Versakit
- GitHub 仓库:github.com/lenran659/V...
- 技术交流:添加微信
jannik1337741710
或者JustHappy-_-
备注【Versakit】 - 我们一起共同进步!
欢迎大家 star✨ 支持,您的反馈是我们前进的动力!✨