如何使用Vue 3渲染vis.js自定义item模板
Vue 3 实现vis.js自定义item及group模板方案
核心实现思路
vis.js的模板配置项支持返回原生DOM元素,因此我们可以利用Vue3的runtime渲染能力,将Vue组件编译为真实DOM后传入vis.js即可,无需适配vis官方的React示例逻辑。
前置依赖安装
npm install vis-timeline # 如使用其他vis模块按需安装即可
步骤1:封装Vue组件转为DOM的通用工具函数
import { createVNode, render } from 'vue' /** * 将Vue组件转为真实DOM节点,供vis.js模板使用 * @param {Component} component Vue组件 * @param {Object} props 传给组件的参数(item/group的原始数据等) * @returns {HTMLElement} 渲染完成的DOM节点 */ export function renderVueComponentToDom(component, props) { // 创建虚拟节点 const vnode = createVNode(component, props) // 创建挂载容器 const container = document.createElement('div') // 渲染虚拟节点到容器 render(vnode, container) // 返回渲染后的真实DOM return container.firstElementChild }
步骤2:编写自定义模板组件
自定义项模板示例 CustomItem.vue
<template> <div class="custom-item"> <p>*{{ item.content }}*</p> <span class="time">{{ item.start.toLocaleTimeString() }}</span> </div> </template> <script setup> const props = defineProps({ // 接收vis.js传入的item原始数据 item: { type: Object, required: true }, // 可自定义传其他参数,比如编辑回调等 onEdit: { type: Function, default: () => {} } }) </script>
自定义分组模板示例 CustomGroup.vue
<template> <div class="custom-group"> <img :src="group.avatar" alt="头像" class="group-avatar" /> <span>{{ group.content }}</span> <button @click="addItem(group.id)">新增项</button> </div> </template> <script setup> const props = defineProps({ group: { type: Object, required: true }, addItem: { type: Function, required: true } }) </script>
步骤3:初始化timeline时配置模板
<template> <div ref="timelineRef" class="timeline-container"></div> </template> <script setup> import { ref, onMounted, onBeforeUnmount } from 'vue' import { Timeline } from 'vis-timeline' import 'vis-timeline/styles/vis-timeline-graph2d.min.css' import { renderVueComponentToDom } from './utils' import CustomItem from './CustomItem.vue' import CustomGroup from './CustomGroup.vue' const timelineRef = ref(null) let timelineInstance = null // 模拟数据 const items = [ { id: 1, content: '项1', start: new Date(), group: 1 }, { id: 2, content: '项2', start: new Date(Date.now() + 3600000), group: 2 } ] const groups = [ { id: 1, content: '分组1', avatar: 'xxx.png' }, { id: 2, content: '分组2', avatar: 'yyy.png' } ] const addItem = (groupId) => { // 新增项逻辑 console.log('给分组新增项', groupId) } onMounted(() => { const options = { // 其他配置按需加 template: { // 自定义item模板 item: (item) => { return renderVueComponentToDom(CustomItem, { item, onEdit: () => console.log('编辑项', item.id) }) }, // 自定义group模板,解决groupTemplate不生效问题 group: (group) => { return renderVueComponentToDom(CustomGroup, { group, addItem }) } } } // 初始化timeline,必须传入groups配置,group模板才会生效 timelineInstance = new Timeline(timelineRef.value, items, groups, options) }) // 销毁时卸载组件避免内存泄漏 onBeforeUnmount(() => { if (timelineInstance) { timelineInstance.destroy() timelineInstance = null } }) </script>
groupTemplate不生效常见排查点
- 必须在初始化Timeline时传入第三个参数
groups,未传分组数据时group模板不会触发 - 模板函数必须返回DOM节点,不能返回字符串/Vue虚拟节点
- 如果自定义组件存在多个根节点,需要在外层套一个单独的div容器,避免渲染时取到undefined
- 确保timeline挂载的容器已经渲染到DOM树中再初始化实例
内容的提问来源于stack exchange,提问作者Fanna1119
相关产品推荐
相关产品推荐

