关于 slidev-components
slidev-components 是面向 Slidev 演示文稿作者的技能,介绍内置 Vue 组件及自定义交互组件的创建。内容涉及动画、动态内容、导航状态和可复用组件。
主要功能
- 01使用内置 Slidev 组件
- 02创建自定义 Vue 组件
- 03点击与顺序显示动画
- 04访问导航和幻灯片上下文
- 05设置全局顶部与底部组件
适用场景
- 01为幻灯片添加交互元素
- 02创建可复用的演示文稿组件
- 03按点击逐步显示内容
- 04根据演示模式控制内容呈现
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ yoanbernabeu/ slidev-skills --skill slidev-components安装后在 Claude Code 中输入/slidev-components确认已加载。
详细介绍
Slidev 组件
本技能介绍如何在 Slidev 中使用 Vue 组件,包括所有内置组件,以及如何为演示文稿创建自定义交互元素。
适用场景
- 为幻灯片添加交互元素
- 使用 Slidev 内置组件
- 创建自定义 Vue 组件
- 构建可复用的演示元素
- 添加动态内容
使用组件
可以直接在 Markdown 中使用组件:
# My Slide
<MyComponent />
<Counter :start="5" />
内置组件
Arrow
在两个点之间绘制箭头。
<Arrow x1="10" y1="20" x2="100" y2="200" />
<Arrow
x1="10"
y1="20"
x2="100"
y2="200"
color="#f00"
width="3"
/>
属性:
x1、y1:起点坐标x2、y2:终点坐标color:箭头颜色width:线宽
VDragArrow
可拖动的箭头(适用于演示)。
<VDragArrow />
AutoFitText
自动调整字体大小以适应容器。
<AutoFitText :max="200" :min="50" modelValue="My Text" />
属性:
max:最大字体大小min:最小字体大小modelValue:文本内容
LightOrDark
根据主题渲染不同内容。
<LightOrDark>
<template #light>
<img src="/logo-dark.png" />
</template>
<template #dark>
<img src="/logo-light.png" />
</template>
</LightOrDark>
Link
用于跳转到其他幻灯片的导航链接。
<Link to="42">Go to slide 42</Link>
<Link to="/intro">Go to intro</Link>
SlideCurrentNo / SlidesTotal
显示幻灯片页码。
Slide <SlideCurrentNo /> of <SlidesTotal />
Toc(目录)
生成目录。
<Toc />
<Toc maxDepth="2" />
<Toc mode="onlyCurrentTree" />
属性:
maxDepth:最大标题层级mode:显示模式(all、onlyCurrentTree、onlySiblings)
Transform
应用 CSS 变换。
<Transform :scale="1.5">
<div>Scaled content</div>
</Transform>
<Transform :scale="0.8" :rotate="10">
Rotated and scaled
</Transform>
属性:
scale:缩放比例rotate:旋转角度
Tweet
嵌入推文。
<Tweet id="1234567890" />
<Tweet id="1234567890" scale="0.8" />
Youtube
嵌入 YouTube 视频。
<Youtube id="dQw4w9WgXcQ" />
<Youtube id="dQw4w9WgXcQ" width="560" height="315" />
属性:
id:YouTube 视频 IDwidth、height:尺寸
SlidevVideo
嵌入视频文件。
<SlidevVideo v-click autoplay controls>
<source src="/video.mp4" type="video/mp4" />
</SlidevVideo>
属性:
autoplay:进入幻灯片时自动播放controls:显示视频控件loop:循环播放视频
RenderWhen
根据上下文进行条件渲染。
<RenderWhen context="slide">
Only visible in slide view
</RenderWhen>
<RenderWhen context="presenter">
Only visible in presenter view
</RenderWhen>
上下文选项:slide、presenter、previewNext、print
VDrag
使元素可拖动。
<VDrag>
<div class="p-4 bg-blue-500 text-white">
Drag me!
</div>
</VDrag>
<VDrag :initialX="100" :initialY="50">
Positioned draggable
</VDrag>
动画组件
VClick
点击后显示内容。
<v-click>
Revealed on first click
</v-click>
<v-click at="2">
Revealed on second click
</v-click>
VClicks
依次显示子元素。
<v-clicks>
- First item
- Second item
- Third item
</v-clicks>
属性:
depth:嵌套列表的深度every:每次点击显示的项目数
VAfter
与前一个元素同时显示。
<v-click>First</v-click>
<v-after>Appears with first</v-after>
VSwitch
根据点击切换内容。
<v-switch>
<template #1>Step 1 content</template>
<template #2>Step 2 content</template>
<template #3>Step 3 content</template>
</v-switch>
创建自定义组件
基础组件
创建 components/Counter.vue:
<script setup>
import { ref } from 'vue'
const props = defineProps({
start: {
type: Number,
default: 0
}
})
const count = ref(props.start)
</script>
<template>
<div class="counter">
<button @click="count--">-</button>
<span class="count">{{ count }}</span>
<button @click="count++">+</button>
</div>
</template>
<style scoped>
.counter {
display: flex;
align-items: center;
gap: 1rem;
}
button {
padding: 0.5rem 1rem;
font-size: 1.5rem;
cursor: pointer;
}
.count {
font-size: 2rem;
min-width: 3rem;
text-align: center;
}
</style>
用法:
# Interactive Counter
<Counter :start="10" />
带插槽的组件
<script setup>
defineProps({
title: String,
color: {
type: String,
default: 'blue'
}
})
</script>
<template>
<div :class="`card card-${color}`">
<h3 v-if="title">{{ title }}</h3>
<slot />
</div>
</template>
<style scoped>
.card {
padding: 1.5rem;
border-radius: 0.5rem;
margin: 1rem 0;
}
.card-blue { background: #3b82f6; color: white; }
.card-green { background: #22c55e; color: white; }
.card-red { background: #ef4444; color: white; }
</style>
用法:
<Card title="Important" color="red">
This is a red card with important content.
</Card>
带 Slidev 上下文的组件
<script setup>
import { computed } from 'vue'
import { useNav } from '@slidev/client'
const { currentSlideNo, total } = useNav()
const progress = computed(() =>
(currentSlideNo.value / total.value) * 100
)
</script>
<template>
<div class="progress-bar">
<div
class="progress"
:style="{ width: `${progress}%` }"
/>
</div>
</template>
<style scoped>
.progress-bar {
position: fixed;
top: 0;
left: 0;
right: 0;
height: 4px;
background: #e5e7eb;
}
.progress {
height: 100%;
background: #3b82f6;
transition: width 0.3s;
}
</style>
代码演示组件
<script setup>
import { ref, computed } from 'vue'
const props = defineProps({
code: String,
language: {
type: String,
default: 'javascript'
}
})
const output = ref('')
const error = ref('')
const run = () => {
try {
output.value = eval(props.code)
error.value = ''
} catch (e) {
error.value = e.message
output.value = ''
}
}
</script>
<template>
<div class="code-demo">
<pre><code>{{ code }}</code></pre>
<button @click="run">Run</button>
<div v-if="output" class="output">{{ output }}</div>
<div v-if="error" class="error">{{ error }}</div>
</div>
</template>
组合式函数
useNav
访问导航状态:
<script setup>
import { useNav } from '@slidev/client'
const {
currentSlideNo, // Current slide number
total, // Total slides
next, // Go to next
prev, // Go to previous
go // Go to specific slide
} = useNav()
</script>
useSlideContext
访问幻灯片上下文:
<script setup>
import { useSlideContext } from '@slidev/client'
const {
$slidev, // Global context
$clicks, // Current click count
$page // Current page number
} = useSlideContext()
</script>
全局组件
global-top.vue
显示在所有幻灯片上方:
<template>
<div class="absolute top-4 right-4">
<img src="/logo.png" class="h-8" />
</div>
</template>
global-bottom.vue
显示在所有幻灯片下方:
<template>
<footer class="absolute bottom-4 left-4 text-sm opacity-50">
© 2025 My Company
</footer>
</template>
组件模式
进度指示器
<div class="fixed bottom-4 right-4 text-sm">
<SlideCurrentNo /> / <SlidesTotal />
</div>
社交链接
<template>
<div class="flex gap-4">
<a href="https://twitter.com/..." target="_blank">
<carbon-logo-twitter class="text-2xl" />
</a>
<a href="https://github.com/..." target="_blank">
<carbon-logo-github class="text-2xl" />
</a>
</div>
</template>
二维码
<script setup>
import { ref, onMounted } from 'vue'
import QRCodeLib from 'qrcode'
const props = defineProps({
url: String,
size: { type: Number, default: 200 }
})
const qrDataUrl = ref('')
onMounted(async () => {
qrDataUrl.value = await QRCodeLib.toDataURL(props.url, {
width: props.size
})
})
</script>
<template>
<img :src="qrDataUrl" :width="size" :height="size" />
</template>
最佳实践
- 保持组件简单:专注于单一职责
- 使用属性:让组件可配置
- 使用局部样式:避免污染全局样式
- 记录用法:添加注释说明如何使用
- 测试交互性:确认组件在演示者模式下正常工作
输出格式
创建组件时,请提供:
COMPONENT: [name]
PURPOSE: [what it does]
FILE: components/[Name].vue
---
<script setup>
[script content]
</script>
<template>
[template content]
</template>
<style scoped>
[styles]
</style>
---
USAGE IN SLIDES:
```markdown
<[Name] prop="value" />
PROPS:
- [propName]: [type] - [description]
评论
支持 Markdown