PrimeVue 完全指南

PrimeVue 是 PrimeTek 出品的 Vue 3 UI 组件库,提供 90+ 开箱即用的组件。v4 在 v3 基础上进行了重大架构调整:以 CSS 变量和设计令牌体系替代了 SASS 主题,并引入了 PassThrough(PT)API 实现完全无样式模式。 PrimeVue v4 提供三种开箱即用的主题预设: CSS 变量命名规则:--p-{组件}-{属性},如 --p-button-background。 在 main.ts 中引用: PassThrough API 允许将自定义属性、类名、样式注入到组件内部任意 DOM 节点,无需覆盖 C

分享

官方文档:https://primevue.org/
适用版本:PrimeVue 4.x(2026-05-07 核实)

PrimeVue 是 PrimeTek 出品的 Vue 3 UI 组件库,提供 90+ 开箱即用的组件。v4 在 v3 基础上进行了重大架构调整:以 CSS 变量和设计令牌体系替代了 SASS 主题,并引入了 PassThrough(PT)API 实现完全无样式模式。


安装与配置

Vite 项目安装

npm install primevue @primevue/themes
# 图标库(可选,但常用)
npm install primeicons

main.ts 配置

import { createApp } from 'vue'
import PrimeVue from 'primevue/config'
import Aura from '@primevue/themes/aura'
import ToastService from 'primevue/toastservice'
import ConfirmationService from 'primevue/confirmationservice'
import App from './App.vue'

const app = createApp(App)

app.use(PrimeVue, {
  theme: {
    preset: Aura,
    options: {
      prefix: 'p',              // CSS 变量前缀,默认 'p'
      darkModeSelector: 'system', // 'system' | '.dark' | 'media' | false
      cssLayer: false           // 是否注入 @layer 以降低样式优先级(与 Tailwind 配合时设 true)
    }
  },
  ripple: true,                 // 全局启用水波纹效果
  inputVariant: 'outlined',    // 'outlined' | 'filled',全局输入框变体
  locale: {                     // 可选:本地化配置
    accept: '确认',
    reject: '取消',
    // ... 其余语言包字段
  }
})

app.use(ToastService)
app.use(ConfirmationService)

app.mount('#app')

配置参数总表

参数 类型 默认值 说明
theme.preset Object 主题预设对象(Aura/Lara/Nora 或自定义)
theme.options.prefix string 'p' CSS 变量前缀,改为其他值可避免命名冲突
theme.options.darkModeSelector string | false 'system' 暗色模式触发方式:'system'(媒体查询)、自定义 CSS 类(.dark)、false(禁用暗色)
theme.options.cssLayer boolean false 将 PrimeVue 样式包裹在 CSS @layer 中,便于 Tailwind 覆盖
ripple boolean false 全局启用点击水波纹效果
inputVariant string 'outlined' 所有输入组件的默认变体
unstyled boolean false 完全去除内置样式,配合 PassThrough API 使用
pt Object 全局 PassThrough 配置,对所有组件生效
ptOptions Object PT 合并策略选项
locale Object 国际化文本配置
nonce string Content Security Policy nonce 值

主题系统

内置预设

PrimeVue v4 提供三种开箱即用的主题预设:

预设 安装路径 风格
Aura @primevue/themes/aura PrimeTek 自研现代风格,色调中性
Lara @primevue/themes/lara Bootstrap 风格衍生,亲切感强
Nora @primevue/themes/nora 企业级扁平风格,结构感强

设计令牌三层架构

Primitive 令牌(基础色板)
  └── 如 blue-500、surface-200
Semantic 令牌(语义映射)
  └── 如 primary.color → {blue.500}
Component 令牌(组件专属)
  └── 如 button.background → {primary.color}

CSS 变量命名规则:--p-{组件}-{属性},如 --p-button-background

definePreset — 扩展自定义主题

import { definePreset } from '@primevue/themes'
import Aura from '@primevue/themes/aura'

const MyPreset = definePreset(Aura, {
  // 覆盖 Semantic 令牌:将主色改为靛蓝色系
  semantic: {
    primary: {
      50:  '{indigo.50}',
      100: '{indigo.100}',
      200: '{indigo.200}',
      300: '{indigo.300}',
      400: '{indigo.400}',
      500: '{indigo.500}',
      600: '{indigo.600}',
      700: '{indigo.700}',
      800: '{indigo.800}',
      900: '{indigo.900}',
      950: '{indigo.950}'
    },
    colorScheme: {
      light: {
        primary: {
          color: '{primary.500}',
          contrastColor: '#ffffff',
          hoverColor: '{primary.600}',
          activeColor: '{primary.700}'
        },
        surface: {
          0:   '#ffffff',
          50:  '{zinc.50}',
          100: '{zinc.100}',
          200: '{zinc.200}',
          300: '{zinc.300}',
          400: '{zinc.400}',
          500: '{zinc.500}',
          600: '{zinc.600}',
          700: '{zinc.700}',
          800: '{zinc.800}',
          900: '{zinc.900}',
          950: '{zinc.950}'
        }
      },
      dark: {
        primary: {
          color: '{primary.400}',
          contrastColor: '{surface.900}',
          hoverColor: '{primary.300}',
          activeColor: '{primary.200}'
        }
      }
    }
  },
  // 覆盖 Component 令牌:自定义按钮样式
  components: {
    button: {
      colorScheme: {
        light: {
          root: {
            background: '{primary.500}',
            hoverBackground: '{primary.600}',
            color: '#ffffff'
          }
        }
      }
    }
  }
})

export default MyPreset

main.ts 中引用:

app.use(PrimeVue, {
  theme: { preset: MyPreset }
})

运行时切换主题

import { usePreset, updatePreset } from '@primevue/themes'
import Lara from '@primevue/themes/lara'

// 切换到 Lara 预设
usePreset(Lara)

// 动态更新部分令牌(不切换预设)
updatePreset({
  semantic: {
    primary: {
      500: '#22c55e'
    }
  }
})

暗色模式

// 方式一:跟随系统
app.use(PrimeVue, {
  theme: { preset: Aura, options: { darkModeSelector: 'system' } }
})

// 方式二:手动切换 CSS 类
app.use(PrimeVue, {
  theme: { preset: Aura, options: { darkModeSelector: '.dark' } }
})

// 在组件中切换
document.documentElement.classList.toggle('dark')

PassThrough (PT) API

PassThrough API 允许将自定义属性、类名、样式注入到组件内部任意 DOM 节点,无需覆盖 CSS 变量。

全局 PT 配置

app.use(PrimeVue, {
  theme: { preset: Aura },
  pt: {
    button: {
      root: { class: 'font-bold' },
      label: { class: 'tracking-wide' }
    },
    datatable: {
      root: { class: 'border rounded-lg' },
      thead: { class: 'bg-gray-50' }
    }
  }
})

组件级 PT

<Button
  label="提交"
  :pt="{
    root: { class: 'my-custom-btn', 'data-testid': 'submit-btn' },
    label: { style: 'letter-spacing: 0.1em' }
  }"
/>

PT 合并策略

当同时存在全局 PT 和组件级 PT 时,默认后者覆盖前者。可通过 ptOptions 配置合并策略:

app.use(PrimeVue, {
  ptOptions: {
    mergeSections: true,    // 合并各 section 的类名(而非覆盖)
    mergeProps: true        // 合并属性(而非覆盖)
  }
})

无样式(Unstyled)模式

完全移除 PrimeVue 内置样式,结合 Tailwind CSS 使用:

app.use(PrimeVue, {
  unstyled: true,
  pt: {
    button: {
      root: { class: 'px-4 py-2 bg-blue-500 text-white rounded hover:bg-blue-600 transition-colors' }
    }
  }
})

Nuxt 3 集成

npm install primevue @primevue/themes
npm install -D @primevue/nuxt-module
// nuxt.config.ts
import Aura from '@primevue/themes/aura'

export default defineNuxtConfig({
  modules: ['@primevue/nuxt-module'],

  primevue: {
    autoImport: true,  // 自动注册所有组件(默认开启,支持 Tree-shaking)
    options: {
      theme: {
        preset: Aura,
        options: {
          darkModeSelector: '.dark'
        }
      },
      ripple: true
    },
    // 仅引入指定组件(autoImport: false 时生效)
    components: {
      include: ['Button', 'DataTable', 'Dialog', 'InputText', 'Select']
    }
  }
})

常用组件 API

Button

<Button
  label="提交"
  icon="pi pi-check"
  icon-pos="right"
  severity="primary"
  :loading="isLoading"
  @click="handleClick"
/>
属性 类型 默认值 说明
label string 按钮文字
icon string PrimeIcons 图标类名,如 pi pi-check
iconPos string 'left' 图标位置:'left' | 'right' | 'top' | 'bottom'
severity string 'primary' 风格:'primary' | 'secondary' | 'success' | 'info' | 'warn' | 'danger' | 'contrast'
raised boolean false 添加阴影
rounded boolean false 圆角全圆
text boolean false 文字按钮,无背景
outlined boolean false 描边按钮
link boolean false 链接样式
size string 'small' | 'large'
loading boolean false 显示加载状态
loadingIcon string 加载状态图标类名
disabled boolean false 禁用
fluid boolean false 撑满父容器宽度
badge string 徽标内容
badgeSeverity string 徽标风格
as string/Component 'button' 渲染元素类型,可传 'a' 或路由组件
asChild boolean false 将样式传给子元素(headless)
插槽 说明
default 自定义内容,替换 label
icon 自定义图标
loadingicon 自定义加载图标

InputText

<InputText v-model="value" placeholder="请输入..." size="large" fluid />
属性 类型 默认值 说明
modelValue string v-model 绑定值
size string 'small' | 'large'
variant string 'outlined' | 'filled',覆盖全局 inputVariant
fluid boolean false 宽度 100%
invalid boolean false 错误状态样式
disabled boolean false 禁用
事件 说明
update:modelValue 值变化

Select(v3 的 Dropdown)

<Select
  v-model="selected"
  :options="cities"
  option-label="name"
  option-value="code"
  placeholder="请选择城市"
  filter
  fluid
/>
属性 类型 默认值 说明
modelValue any 选中值
options array 选项数组
optionLabel string/Function 选项显示字段名或取值函数
optionValue string/Function 选项实际值字段名
optionDisabled string/Function 禁用项字段名
optionGroupLabel string 分组标签字段名
optionGroupChildren string 分组子项字段名
placeholder string 占位文字
filter boolean false 启用搜索过滤
filterPlaceholder string 搜索框占位文字
loading boolean false 加载状态
disabled boolean false 禁用
editable boolean false 允许手动输入
showClear boolean false 显示清除按钮
fluid boolean false 宽度 100%
invalid boolean false 错误状态样式
checkmark boolean false 选中项显示勾选标记
highlightOnSelect boolean true 选中项高亮
maxSelectedLabels number 3 最多显示几个标签(MultiSelect)
panelClass string 下拉面板额外 CSS 类
appendTo string/Element 'body' 下拉挂载点
virtualScrollerOptions object 虚拟滚动配置(大数据量)
插槽 说明
value 自定义选中值显示区域
option 自定义选项渲染
optiongroup 自定义分组标题
header 下拉头部
footer 下拉底部
empty 无选项时的提示内容
事件 说明
change 选项改变,payload: { value }
before-show 面板显示前
show 面板显示后
before-hide 面板隐藏前
hide 面板隐藏后
filter 搜索过滤时,payload: { value }

DatePicker(v3 的 Calendar)

<DatePicker
  v-model="date"
  date-format="yy-mm-dd"
  :min-date="minDate"
  :max-date="maxDate"
  show-icon
  show-button-bar
/>
属性 类型 默认值 说明
modelValue Date/Date[]/object 绑定值
selectionMode string 'single' 'single' | 'multiple' | 'range'
dateFormat string 'mm/dd/yy' 日期格式字符串
showTime boolean false 显示时间选择
timeOnly boolean false 仅时间模式
showIcon boolean false 显示日历图标
showButtonBar boolean false 显示今天/清除按钮
minDate Date 最小可选日期
maxDate Date 最大可选日期
disabledDates Date[] 禁用日期列表
disabledDays number[] 禁用星期几(0=周日)
numberOfMonths number 1 同时显示月份数
inline boolean false 内联展示,不作为弹层
fluid boolean false 宽度 100%
disabled boolean false 禁用

DataTable

DataTable 是功能最丰富的组件,支持排序、筛选、分页、行选择、行展开、懒加载、虚拟滚动等。

<DataTable
  :value="products"
  v-model:selection="selectedProducts"
  v-model:filters="filters"
  selection-mode="multiple"
  data-key="id"
  :paginator="true"
  :rows="10"
  :rows-per-page-options="[5, 10, 25, 50]"
  sort-mode="multiple"
  filter-display="row"
  :loading="loading"
  :lazy="true"
  :total-records="totalRecords"
  @page="onPage"
  @sort="onSort"
  @filter="onFilter"
>
  <template #header>
    <div class="flex justify-between">
      <span>产品列表</span>
      <InputText v-model="filters['global'].value" placeholder="全局搜索" />
    </div>
  </template>

  <Column selection-mode="multiple" style="width: 3rem" />
  <Column field="name" header="名称" sortable />
  <Column field="price" header="价格" sortable>
    <template #body="{ data }">
      ¥{{ data.price.toFixed(2) }}
    </template>
  </Column>
  <Column field="status" header="状态" filter-field="status" :show-filter-menu="false">
    <template #filter="{ filterModel, filterCallback }">
      <Select
        v-model="filterModel.value"
        :options="statuses"
        placeholder="筛选状态"
        @change="filterCallback()"
      />
    </template>
  </Column>
</DataTable>

DataTable 属性(常用)

属性 类型 默认值 说明
value array 数据源
dataKey string 行唯一标识字段(选择/展开等功能必填)
selection any 选中行,配合 v-model:selection
selectionMode string 'single' | 'multiple'
metaKeySelection boolean false 是否需要按 Meta 键多选
paginator boolean false 启用分页
rows number 每页行数
rowsPerPageOptions number[] 每页行数选项
paginatorPosition string 'bottom' 'top' | 'bottom' | 'both'
currentPageReportTemplate string 分页报告模板,如 '第 {currentPage} 页,共 {totalPages} 页'
totalRecords number 总记录数(懒加载必填)
lazy boolean false 懒加载模式(分页/排序/筛选由外部处理)
loading boolean false 加载状态
sortField string 初始排序字段
sortOrder number 初始排序方向:1 升序,-1 降序
sortMode string 'single' 'single' | 'multiple'
removableSort boolean false 允许取消排序
multiSortMeta array 多列排序元数据
filters object 筛选对象,配合 v-model:filters
filterDisplay string 'menu' | 'row'
globalFilterFields string[] 全局搜索覆盖字段
expandedRows array 展开的行,配合 v-model:expandedRows
rowExpansionTemplate 行展开内容(使用插槽)
frozenColumns number 冻结左侧列数
scrollable boolean false 启用滚动
scrollHeight string 内容区滚动高度,如 '400px' | 'flex'
virtualScrollerOptions object 虚拟滚动配置
editMode string 'cell' | 'row' 行内编辑
size string 'small' | 'large'
showGridlines boolean false 显示表格线
stripedRows boolean false 隔行变色
tableStyle string table 元素 style
tableClass string table 元素额外 CSS 类

DataTable 事件

事件 说明
page 翻页,payload: PageState
sort 排序,payload: SortEvent
filter 筛选,payload: FilterEvent
row-click 点击行,payload: { originalEvent, data, index }
row-dblclick 双击行
row-select 选中行
row-unselect 取消选中行
row-expand 展开行
row-collapse 折叠行
cell-edit-complete 单元格编辑完成
row-edit-save 行编辑保存
row-edit-cancel 行编辑取消

DataTable 插槽

插槽 说明
header 表格头部
footer 表格底部
loading 加载状态内容
empty 无数据时内容
paginatorstart 分页器左侧内容
paginatorend 分页器右侧内容
expansion 行展开内容,接收 { data, index }
groupheader 行分组头部
groupfooter 行分组尾部

Column 属性

属性 类型 默认值 说明
field string 对应数据字段
header string 列标题
sortable boolean false 允许排序
sortField string 排序使用的字段(与 field 不同时)
filter boolean false 启用该列过滤
filterField string 过滤使用的字段
showFilterMenu boolean true 显示过滤菜单图标
filterMatchMode string 默认匹配模式
selectionMode string 'single' | 'multiple' 选择列
frozen boolean false 冻结该列
alignFrozen string 'left' 冻结位置:'left' | 'right'
expander boolean false 行展开按钮列
rowspan number 行合并
colspan number 列合并
style string 列样式
class string 列 CSS 类
bodyStyle string body 单元格样式
bodyClass string body 单元格 CSS 类
headerStyle string 表头单元格样式
headerClass string 表头单元格 CSS 类
pt object PassThrough 配置

Column 插槽

插槽 说明
body 自定义单元格内容,接收 { data, field, index }
header 自定义列标题
footer 自定义列底部
filter 自定义过滤元素
filterclear 清除过滤按钮内容
filterapply 应用过滤按钮内容
editor 行内编辑元素

筛选初始化示例

import { FilterMatchMode } from '@primevue/core/api'

const filters = ref({
  global:   { value: null, matchMode: FilterMatchMode.CONTAINS },
  name:     { value: null, matchMode: FilterMatchMode.STARTS_WITH },
  status:   { value: null, matchMode: FilterMatchMode.EQUALS },
  price:    { value: null, matchMode: FilterMatchMode.GREATER_THAN }
})

可用 FilterMatchMode 值:STARTS_WITHCONTAINSNOT_CONTAINSENDS_WITHEQUALSNOT_EQUALSINLESS_THANLESS_THAN_OR_EQUAL_TOGREATER_THANGREATER_THAN_OR_EQUAL_TOBETWEENDATE_ISDATE_IS_NOTDATE_BEFOREDATE_AFTER


Dialog

<Button label="打开" @click="visible = true" />

<Dialog
  v-model:visible="visible"
  header="确认操作"
  :modal="true"
  :draggable="true"
  :closable="true"
  :style="{ width: '30rem' }"
  :breakpoints="{ '960px': '75vw', '641px': '100vw' }"
>
  <p>确认要执行此操作吗?</p>
  <template #footer>
    <Button label="取消" text @click="visible = false" />
    <Button label="确认" @click="onConfirm" />
  </template>
</Dialog>
属性 类型 默认值 说明
visible boolean 显示状态,配合 v-model:visible
header string 标题文字
modal boolean true 是否显示遮罩
closable boolean true 显示关闭按钮
closeOnEscape boolean true 按 Esc 关闭
dismissableMask boolean false 点击遮罩关闭
draggable boolean true 允许拖拽
resizable boolean true 允许调整大小
maximizable boolean false 显示最大化按钮
position string 'center' 位置:'center' | 'top' | 'bottom' | 'left' | 'right' | 四角
style object 对话框 style
class string 对话框额外 CSS 类
contentStyle object 内容区 style
contentClass string 内容区 CSS 类
breakpoints object 响应式宽度断点,如 { '960px': '75vw' }
appendTo string/Element 'body' 挂载目标
blockScroll boolean false 打开时锁定 body 滚动
keepInViewport boolean true 拖拽时保持在视口内
插槽 说明
default 对话框内容
header 自定义标题区域
footer 底部按钮区域
closeicon 自定义关闭图标
maximizeicon 自定义最大化图标
container 完全接管对话框外层容器
事件 说明
show 对话框显示后
hide 对话框关闭后
after-hide 关闭动画完成后
maximize 最大化切换
dragend 拖拽结束

Drawer(v3 的 Sidebar)

<Drawer v-model:visible="drawerVisible" header="菜单" position="left">
  <nav>
    <ul>
      <li>首页</li>
      <li>关于</li>
    </ul>
  </nav>
</Drawer>
属性 类型 默认值 说明
visible boolean 显示状态
header string 标题
position string 'left' 'left' | 'right' | 'top' | 'bottom'
modal boolean true 显示遮罩
dismissable boolean true 点击遮罩关闭
showCloseIcon boolean true 显示关闭按钮
closeOnEscape boolean true Esc 关闭
style object 面板 style
blockScroll boolean false 打开时锁定滚动
full-screen boolean false 全屏模式
插槽 说明
default 内容
header 自定义标题
container 接管整个容器

Toast 服务

注册服务

// main.ts
import ToastService from 'primevue/toastservice'
app.use(ToastService)

放置 Toast 容器(通常在 App.vue)

<template>
  <Toast />
  <RouterView />
</template>

在组件中使用

import { useToast } from 'primevue/usetoast'

const toast = useToast()

// 各种类型
toast.add({ severity: 'success', summary: '成功', detail: '操作已完成', life: 3000 })
toast.add({ severity: 'info',    summary: '提示', detail: '这是一条信息' })
toast.add({ severity: 'warn',    summary: '警告', detail: '请注意此操作' })
toast.add({ severity: 'error',   summary: '错误', detail: '操作失败,请重试', sticky: true })
toast.add({ severity: 'secondary', summary: '次要', detail: '次要消息' })
toast.add({ severity: 'contrast', summary: '对比', detail: '对比色消息' })

// 清除
toast.remove(toastInstance)  // 移除指定
toast.removeGroup('myGroup') // 移除指定分组
toast.removeAllGroups()      // 移除所有

Toast 消息选项

选项 类型 默认值 说明
severity string 'info' 'success' | 'info' | 'warn' | 'error' | 'secondary' | 'contrast'
summary string 标题
detail string 详细内容
life number 3000 自动关闭时间(毫秒),0 或不设表示不自动关闭
sticky boolean false 持久显示,不自动关闭
closable boolean true 显示关闭按钮
group string 分组名,配合 Toast 组件的 group 属性
icon string 自定义图标类名

Toast 组件属性

属性 类型 默认值 说明
position string 'top-right' 'top-left' | 'top-center' | 'top-right' | 'bottom-left' | 'bottom-center' | 'bottom-right' | 'center'
group string 对应特定分组消息
baseZIndex number 0 基础层叠 z-index
breakpoints object 响应式宽度断点
pt object PassThrough 配置

ConfirmDialog / ConfirmPopup 服务

注册服务

// main.ts
import ConfirmationService from 'primevue/confirmationservice'
app.use(ConfirmationService)

放置容器

<ConfirmDialog />
<!-- 或气泡确认 -->
<ConfirmPopup />

在组件中使用

import { useConfirm } from 'primevue/useconfirm'

const confirm = useConfirm()

// 对话框确认
confirm.require({
  message:  '确定要删除吗?此操作不可逆。',
  header:   '删除确认',
  icon:     'pi pi-exclamation-triangle',
  rejectLabel:   '取消',
  acceptLabel:   '确认删除',
  rejectProps:   { label: '取消', severity: 'secondary', outlined: true },
  acceptProps:   { label: '确认删除', severity: 'danger' },
  accept: () => {
    toast.add({ severity: 'info', summary: '已删除', life: 3000 })
  },
  reject: () => {
    toast.add({ severity: 'warn', summary: '已取消', life: 3000 })
  }
})

// 气泡确认(需要目标元素)
confirm.require({
  target:  event.currentTarget,
  message: '确认删除?',
  icon:    'pi pi-info-circle',
  accept:  () => doDelete()
})

Popover(v3 的 OverlayPanel)

<Button label="更多信息" @click="pop.toggle($event)" />
<Popover ref="pop">
  <div class="p-4">
    <p>这是弹出内容</p>
  </div>
</Popover>
const pop = ref()

// 方法
pop.value.show(event)   // 显示
pop.value.hide()        // 隐藏
pop.value.toggle(event) // 切换
属性 类型 默认值 说明
dismissable boolean true 点击外部关闭
closeOnEscape boolean true Esc 关闭
appendTo string/Element 'body' 挂载目标
style object 面板 style
class string 面板额外 CSS 类
pt object PassThrough 配置

Tabs(v3 的 TabView)

<Tabs value="0">
  <TabList>
    <Tab value="0">首页</Tab>
    <Tab value="1">关于</Tab>
    <Tab value="2">联系</Tab>
  </TabList>
  <TabPanels>
    <TabPanel value="0">
      <p>首页内容</p>
    </TabPanel>
    <TabPanel value="1">
      <p>关于内容</p>
    </TabPanel>
  </TabPanels>
</Tabs>
属性(Tabs) 类型 默认值 说明
value string/number 当前激活 Tab 的值,配合 v-model:value
lazy boolean false 懒渲染非激活 Tab 内容
scrollable boolean false Tab 列表可横向滚动
selectOnFocus boolean false 键盘聚焦时即激活

Accordion

<Accordion value="0">
  <AccordionPanel value="0">
    <AccordionHeader>标题一</AccordionHeader>
    <AccordionContent>
      <p>内容一</p>
    </AccordionContent>
  </AccordionPanel>
  <AccordionPanel value="1">
    <AccordionHeader>标题二</AccordionHeader>
    <AccordionContent>
      <p>内容二</p>
    </AccordionContent>
  </AccordionPanel>
</Accordion>
属性(Accordion) 类型 默认值 说明
value string/string[] 展开项,multiple 为 true 时可传数组
multiple boolean false 允许同时展开多项
lazy boolean false 懒渲染折叠内容
collapseIcon string 折叠状态图标
expandIcon string 展开状态图标

InputNumber

<InputNumber
  v-model="price"
  prefix="¥"
  :min="0"
  :max="9999"
  :min-fraction-digits="2"
  :max-fraction-digits="2"
  :use-grouping="true"
  locale="zh-CN"
  mode="currency"
  currency="CNY"
  fluid
/>
属性 类型 默认值 说明
modelValue number v-model 绑定值
mode string 'decimal' 'decimal' | 'currency'
locale string 使用的区域设置
currency string 货币代码,如 'CNY'(mode=currency 时)
prefix string 前缀文本
suffix string 后缀文本
min number 最小值
max number 最大值
step number 1 步进值
showButtons boolean false 显示加减按钮
buttonLayout string 'stacked' 'stacked' | 'horizontal' | 'vertical'
useGrouping boolean true 使用千位分隔符
minFractionDigits number 最少小数位数
maxFractionDigits number 最多小数位数
disabled boolean false 禁用
readonly boolean false 只读
invalid boolean false 错误状态
fluid boolean false 宽度 100%

AutoComplete

<AutoComplete
  v-model="value"
  :suggestions="items"
  @complete="search"
  option-label="name"
  force-selection
  :multiple="false"
  fluid
>
  <template #option="{ option }">
    <div class="flex items-center gap-2">
      <img :src="option.avatar" class="w-6 h-6 rounded-full" />
      <span>{{ option.name }}</span>
    </div>
  </template>
</AutoComplete>
const search = (event: { query: string }) => {
  items.value = allItems.filter(item =>
    item.name.toLowerCase().includes(event.query.toLowerCase())
  )
}
属性 类型 默认值 说明
modelValue any v-model 绑定值
suggestions array 匹配建议列表
optionLabel string/Function 显示字段
optionGroupLabel string 分组标签字段
optionGroupChildren string 分组子项字段
multiple boolean false 多选 tag 模式
typeahead boolean true 自动完成打字;设 false 可做纯 tag 输入
forceSelection boolean false 只允许从建议中选择
minLength number 1 触发搜索的最少字符数
delay number 300 输入到触发 complete 事件的延迟(毫秒)
disabled boolean false 禁用
fluid boolean false 宽度 100%
dropdown boolean false 显示下拉触发按钮
dropdownMode string 'blank' 点击下拉按钮的搜索模式
virtualScrollerOptions object 大数据量虚拟滚动
事件 说明
complete 输入时触发,接收 { query }
item-select 选中建议项,接收 { value }
item-unselect 取消选中(多选模式)
change 值变化
focus / blur 聚焦/失焦

FileUpload

<FileUpload
  name="file"
  url="/api/upload"
  :multiple="true"
  accept="image/*"
  :max-file-size="1000000"
  :auto="false"
  @upload="onUpload"
  @before-upload="onBeforeUpload"
  @error="onError"
/>
属性 类型 默认值 说明
name string 表单字段名
url string 上传接口地址
method string 'POST' HTTP 方法
multiple boolean false 允许多选
accept string 允许的文件类型,如 'image/*,.pdf'
disabled boolean false 禁用
auto boolean false 选文件后自动上传
maxFileSize number 最大文件大小(字节)
fileLimit number 最多文件数
withCredentials boolean false 携带凭证(Cookie)
mode string 'advanced' 'advanced'(完整 UI)| 'basic'(简单按钮)
customUpload boolean false 使用自定义上传逻辑
showUploadButton boolean true 显示上传按钮
showCancelButton boolean true 显示取消按钮
previewWidth number 50 预览图宽度(px)
chooseLabel string '选择' 选择按钮文字
uploadLabel string '上传' 上传按钮文字
cancelLabel string '取消' 取消按钮文字
事件 说明
before-upload 上传前,可修改 XmlHttpRequest
upload 上传完成,接收 { files, xhr }
error 上传失败
clear 文件列表清空
select 文件选择,接收 { files, originalEvent }
remove 移除文件
uploader 自定义上传处理(customUpload: true 时)

<!-- Menu(弹出式) -->
<Button label="操作" @click="menu.toggle($event)" />
<Menu ref="menu" :model="menuItems" :popup="true" />

<!-- Menubar(顶部导航) -->
<Menubar :model="navItems">
  <template #start>
    <img src="/logo.svg" class="h-8" />
  </template>
  <template #end>
    <Button label="登录" />
  </template>
</Menubar>

<!-- ContextMenu(右键菜单) -->
<ContextMenu ref="cm" :model="contextItems" />
<div @contextmenu="cm.show($event)">右键点击此区域</div>

菜单 model 的数据结构:

const menuItems = ref([
  {
    label: '文件',
    icon: 'pi pi-file',
    items: [
      { label: '新建', icon: 'pi pi-plus', command: () => createFile() },
      { label: '打开', icon: 'pi pi-folder-open', command: () => openFile() },
      { separator: true },
      { label: '退出', icon: 'pi pi-sign-out', command: () => exit() }
    ]
  },
  {
    label: '外部链接',
    icon: 'pi pi-external-link',
    url: 'https://example.com',
    target: '_blank'
  },
  {
    label: '路由',
    icon: 'pi pi-home',
    route: '/home'  // 配合 vue-router 使用
  }
])

菜单项字段:

字段 类型 说明
label string 菜单项文字
icon string 图标类名
command Function 点击回调
url string 外部链接地址
target string 链接打开方式
route string/object vue-router 路由路径或对象
items array 子菜单(TieredMenu/Menubar 支持多层)
separator boolean 是否为分隔线
disabled boolean 禁用该菜单项
visible boolean 是否可见
badge string 徽标文字
badgeSeverity string 徽标风格
class string 额外 CSS 类
style object 内联样式
shortcut string 快捷键提示文字(仅显示,不绑定)

Stepper(v3 的 Steps)

<Stepper v-model:value="activeStep" linear>
  <StepList>
    <Step value="1">账户信息</Step>
    <Step value="2">个人资料</Step>
    <Step value="3">完成</Step>
  </StepList>
  <StepPanels>
    <StepPanel value="1" v-slot="{ activateCallback }">
      <div>步骤一内容</div>
      <Button label="下一步" @click="activateCallback('2')" />
    </StepPanel>
    <StepPanel value="2" v-slot="{ activateCallback }">
      <div>步骤二内容</div>
      <Button label="上一步" @click="activateCallback('1')" />
      <Button label="下一步" @click="activateCallback('3')" />
    </StepPanel>
    <StepPanel value="3">
      <div>完成!</div>
    </StepPanel>
  </StepPanels>
</Stepper>

Chart

基于 Chart.js,需额外安装:

npm install chart.js
<Chart type="bar" :data="chartData" :options="chartOptions" style="height: 400px" />
const chartData = ref({
  labels: ['一月', '二月', '三月', '四月'],
  datasets: [
    {
      label: '销售额',
      data: [65, 59, 80, 81],
      backgroundColor: ['rgba(255, 99, 132, 0.2)', 'rgba(255, 159, 64, 0.2)']
    }
  ]
})

const chartOptions = ref({
  responsive: true,
  plugins: {
    legend: { position: 'top' }
  }
})
属性 类型 默认值 说明
type string 图表类型:'bar' | 'line' | 'pie' | 'doughnut' | 'radar' | 'polarArea' | 'bubble' | 'scatter'
data object Chart.js data 对象
options object Chart.js options 对象
plugins array Chart.js 插件数组
width number 300 画布宽度
height number 150 画布高度

表单验证集成

与 VeeValidate + Zod 集成

npm install vee-validate zod @vee-validate/zod
<script setup lang="ts">
import { useForm } from 'vee-validate'
import { toTypedSchema } from '@vee-validate/zod'
import { z } from 'zod'

const schema = toTypedSchema(z.object({
  username: z.string().min(3, '用户名至少 3 位').max(20),
  email:    z.string().email('邮箱格式不正确'),
  age:      z.number({ required_error: '请填写年龄' }).min(18, '须年满 18 岁')
}))

const { handleSubmit, defineField, errors, isSubmitting } = useForm({
  validationSchema: schema
})

const [username, usernameAttrs] = defineField('username')
const [email, emailAttrs] = defineField('email')
const [age, ageAttrs] = defineField('age')

const onSubmit = handleSubmit(async (values) => {
  console.log('提交值:', values)
})
</script>

<template>
  <form @submit.prevent="onSubmit">
    <div class="flex flex-col gap-1 mb-4">
      <label>用户名</label>
      <InputText v-model="username" v-bind="usernameAttrs" :invalid="!!errors.username" />
      <small class="text-red-500">{{ errors.username }}</small>
    </div>

    <div class="flex flex-col gap-1 mb-4">
      <label>邮箱</label>
      <InputText v-model="email" v-bind="emailAttrs" :invalid="!!errors.email" />
      <small class="text-red-500">{{ errors.email }}</small>
    </div>

    <div class="flex flex-col gap-1 mb-4">
      <label>年龄</label>
      <InputNumber v-model="age" v-bind="ageAttrs" :invalid="!!errors.age" fluid />
      <small class="text-red-500">{{ errors.age }}</small>
    </div>

    <Button type="submit" label="提交" :loading="isSubmitting" />
  </form>
</template>

与 Tailwind CSS 配合

启用 cssLayer 使 Tailwind 的工具类可以覆盖 PrimeVue 样式:

// main.ts
app.use(PrimeVue, {
  theme: {
    preset: Aura,
    options: {
      cssLayer: {
        name: 'primevue',
        order: 'tailwind-base, primevue, tailwind-utilities'
      }
    }
  }
})
/* tailwind.css */
@layer tailwind-base {
  @tailwind base;
}
@layer tailwind-utilities {
  @tailwind utilities;
}

组件完整清单

v3 → v4 更名对照

v3 名称 v4 名称 说明
Calendar DatePicker 日期/时间选择器
Dropdown Select 下拉选择
InputSwitch ToggleSwitch 开关
OverlayPanel Popover 弹出层
Sidebar Drawer 侧边抽屉
TabView Tabs 标签页
InlineMessage Message 内联消息
Steps Stepper 步骤条
TabMenu Tabs(无 Panel) 标签导航

v4 废弃组件

废弃组件 替代方案
Chips AutoCompletemultiple: truetypeahead: false
TriStateCheckbox Checkboxindeterminate 属性)
DataViewLayoutOptions SelectButton

按分类列举全部组件

表单

组件 说明
InputText 单行文本输入
Textarea 多行文本
InputNumber 数字输入,支持格式化
Password 密码输入,强度指示
InputMask 格式掩码输入
InputOtp OTP 验证码输入
AutoComplete 自动完成
Select 下拉选择(原 Dropdown)
MultiSelect 多选下拉
DatePicker 日期/时间选择(原 Calendar)
CascadeSelect 级联下拉
Checkbox 复选框
RadioButton 单选按钮
ToggleSwitch 开关(原 InputSwitch)
ToggleButton 切换按钮
SelectButton 多按钮单选/多选
Slider 滑块
ColorPicker 颜色选择器
Knob 旋钮
Rating 评分
TreeSelect 树形选择
FileUpload 文件上传
Editor 富文本编辑器(基于 Quill)

数据展示

组件 说明
DataTable 数据表格
Column 表格列(DataTable 子组件)
ColumnGroup 列分组
TreeTable 树形表格
DataView 数据视图(列表/网格切换)
VirtualScroller 虚拟列表
OrderList 可排序列表
PickList 穿梭框
Tree 树形控件
Timeline 时间线
Paginator 分页器(独立组件)
Chart 图表(基于 Chart.js)

面板

组件 说明
Panel 可折叠面板容器
Fieldset 字段集分组
Card 卡片
Accordion 手风琴
Tabs 标签页(原 TabView)
Divider 分割线
Splitter 可拖拽分割布局
ScrollPanel 自定义滚动条面板
DeferredContent 延迟渲染内容
Toolbar 工具栏

覆盖层

组件 说明
Dialog 对话框
Drawer 侧边抽屉(原 Sidebar)
Popover 弹出层(原 OverlayPanel)
Tooltip 文字提示(指令)
ConfirmDialog 确认对话框
ConfirmPopup 确认气泡
DynamicDialog 动态对话框

消息

组件 说明
Toast 轻提示
Message 内联消息(原 InlineMessage)

菜单

组件 说明
Menu 简单菜单
Menubar 顶部菜单栏
TieredMenu 多级菜单
ContextMenu 右键菜单
MegaMenu 超级菜单
BreadCrumb 面包屑
PanelMenu 侧边多级菜单
Dock macOS 风格 Dock 栏
SpeedDial 快速拨号浮动按钮组
SplitButton 分裂按钮
Steps 步骤导航(建议迁移到 Stepper)

其他

组件/指令 说明
Avatar 头像,支持文字/图标/图片
AvatarGroup 头像组
Badge 徽标数字
OverlayBadge 覆盖徽标包装器(原 BadgeDirective)
Tag 标签
Chip 芯片
ProgressBar 进度条
ProgressSpinner 加载旋转圈
Skeleton 骨架屏
ScrollTop 返回顶部按钮
Image 图片(支持预览/旋转/缩放)
Galleria 图片画廊
Carousel 轮播图
BlockUI 区域遮罩
Inplace 内联编辑
MeterGroup 分段进度条
Terminal 终端模拟
v-tooltip Tooltip 指令
v-badge Badge 指令
v-ripple 水波纹指令
v-styleclass 样式类切换指令
v-focustrap 焦点捕获指令
v-animateonscroll 滚动动画指令

无障碍(Accessibility)

PrimeVue v4 按照 WAI-ARIA 1.2 规范构建。

  • 所有交互组件均内置 aria-* 属性
  • 支持键盘导航(Tab、方向键、Enter、Space、Esc)
  • 提供 ariaLabelariaLabelledBy 等属性供自定义
  • 使用 <PrimeVuePassThrough> 或 PT API 可进一步定制 aria 属性

常见踩坑

1. v4 不再引入旧版 CSS

// v3 写法(v4 中删除)
import 'primevue/resources/themes/lara-light-blue/theme.css'
import 'primevue/resources/primevue.min.css'

v4 通过 @primevue/themes 的 JS 对象统一管理主题,无需引入任何 CSS 文件。

2. API 路径变更

// v3
import { FilterMatchMode } from 'primevue/api'

// v4
import { FilterMatchMode } from '@primevue/core/api'

3. p-fluid 类废弃

v3 中包裹 <div class="p-fluid"> 可使内部组件撑满宽度;v4 中改为在组件上使用 fluid prop:

<!-- v3 -->
<div class="p-fluid">
  <InputText v-model="val" />
</div>

<!-- v4 -->
<InputText v-model="val" fluid />

4. Dark Mode 与 SSR 水合不匹配

Nuxt 3 中使用 darkModeSelector: '.dark' 并配合 @nuxtjs/color-mode 时,确保服务端和客户端的初始类名一致,否则会出现 Hydration Mismatch。

5. DataTable 懒加载 totalRecords 必须提前设置

lazy: true 时,分页器需要 totalRecords 才能计算页数。初始加载时应在获取第一页数据的同时设置 totalRecords

6. Tooltip 指令注册

使用 v-tooltip 前需注册:

import Tooltip from 'primevue/tooltip'
app.directive('tooltip', Tooltip)
// 或在 Nuxt 中通过 directives 配置自动注册

最佳实践

选用 Unstyled 模式 + Tailwind,而非 Styled 模式:PrimeVue v4 的 Unstyled 模式(unstyled: true)允许完全自定义样式,配合 tailwindcss-primeui 插件可以用 Tailwind 类名控制组件外观,避免主题覆盖战。Styled 模式适合快速原型,Unstyled 模式适合需要严格设计系统的项目。

使用 PassThrough API 为单个组件实例添加 class,而非全局覆盖 CSS:通过 :pt prop 可以精确控制组件内部 DOM 节点的类名,比写全局 CSS 选择器更可预期且不影响其他组件实例。

<DataTable :pt="{ root: { class: 'border rounded-lg' }, header: { class: 'bg-gray-50' } }">

DataTable 分页用受控模式(:rows + @page),而非非受控:非受控模式下,分页状态由组件内部管理,无法与 URL、路由或外部状态同步。受控模式将 firstrows 状态提升到外部,便于持久化和服务端分页。

表单组件与 Vee-Validate 或 Vue-Formkit 集成时,使用 v-bind 传递错误状态而非包装层:PrimeVue v4 的表单组件接受 invalid prop,直接绑定校验库的错误状态即可,无需额外包装组件。

<InputText v-model="email" :invalid="!!errors.email" />
<small class="text-red-500">{{ errors.email }}</small>

图标使用 primeicons 类名时检查版本兼容性:PrimeIcons 在大版本更新时会重命名部分图标(如 pi-homepi-house),升级后应全量搜索项目中的 pi- 类名进行核对。


常见陷阱

陷阱:Styled 模式下自定义 CSS 被覆盖

现象: 通过全局 CSS 修改 DataTable 行高或颜色,在开发环境生效,生产构建后失效;或某些状态(hover、selected)样式无法覆盖。

原因: PrimeVue Styled 模式的主题 CSS 使用了较高特异性的选择器,全局 class 覆盖时特异性不够;生产构建中 CSS 加载顺序可能与开发环境不同。

解决: 升级到 Unstyled 模式使用 PT API 控制样式;或用 :deep() 穿透 Scoped CSS;或在主题 CSS 后引入自定义 CSS 以保证顺序。

<style scoped>
:deep(.p-datatable-row-selected) {
  background: #e0f2fe !important;
}
</style>

陷阱:v-model 在 Dialog 内的组件数据不同步

现象: Dialog 打开后修改了表单数据,关闭再打开时,数据没有重置或显示了上次的值。

原因: Dialog 组件默认在关闭时不销毁内部 DOM(仅隐藏),Vue 组件状态保留,下次打开时仍是上次的值。

解决: 使用 :visible 控制显隐时,在 @hide 事件中手动重置表单数据;或使用 v-if 替代 v-show 控制 Dialog,每次打开都重新挂载组件。

<Dialog v-model:visible="showDialog" @hide="resetForm">

陷阱:DataTable 懒加载时 totalRecords 未同步导致分页异常

现象: 服务端分页时,分页器显示的总页数不对,或跳转到最后一页后无数据。

原因: lazy: true 时,DataTable 分页器完全依赖 totalRecords prop 计算总页数,若首次加载时未设置或设置了错误的值,分页计算会出错。

解决: 在获取第一页数据的同时从接口返回 total 并赋值给 totalRecords,确保与数据同步更新。

<DataTable :value="rows" :lazy="true" :totalRecords="total" @page="onPage">

参见

阅读更多

Web 安全基础

1. HTML 转义(服务端渲染必须): 2. CSP(Content Security Policy): 3. HttpOnly Cookie:防止 JS 读取会话 Cookie: 4. 前端框架防护: 攻击者在第三方网站构造一个表单,诱导已登录用户提交,浏览器会自动携带目标站的 Cookie。 触发条件: 1. 用户已登录目标网站(Cookie 有效) 2. 目标 API 仅凭 Cookie 识别用户身份 3. 请求来源未验证 1. CSRF Token(推荐): 2. SameSite Cookie: 3. 验证 Origin/Referer 头:

By yellowdog

HTTP 协议深度指南

HTTP(HyperText Transfer Protocol)是 Web 的基础传输协议,基于 TCP/IP,采用请求/响应模型。 相关文档:Web安全基础(/web-an-quan-ji-chu/) FastAPI完全指南(/fastapi-wan-quan-zhi-nan/) Nginx完全指南(/nginx-wan-quan-zhi-nan/) 幂等性:多次执行相同请求,服务器状态结果相同。PUT /users/1 多次执行结果一致;POST /users 每次创建新资源,非幂等。 浏览器直接从本地缓存读取,不向服务器发送请求。 缓存命中时,状

By yellowdog

系统设计基础

SLA 对照表: 选择建议:无状态服务(Web 层、API 层)优先水平扩展;数据库初期垂直扩展,达到瓶颈后考虑分库分表或读写分离。 缓存穿透(查询不存在的 key,每次都打到 DB): 缓存击穿(热点 key 过期,瞬间大量请求打到 DB): 缓存雪崩(大量 key 同时过期,或缓存服务宕机): 令牌桶 Python 实现: Redis 实现分布式限流(滑动窗口): URL 命名规则: Cursor 分页响应格式: 雪花算法结构(64 bit): 定义:分布式系统不能同时满足以下三个特性: 在分布式环境中 P 是必须保证的,所以实际是 CP vs AP

By yellowdog

算法思路与模板

二分查找要求序列有序,每次将搜索范围缩减一半,时间复杂度 O(log n)。 两个指针从两端向中间收缩,常用于有序数组。 滑动窗口维护一个满足条件的区间 left, right,right 不断向右扩张,条件不满足时收缩 left。 滑动窗口通用框架: 1. 确定"子问题":原问题可以分解为哪些规模更小的同类问题 2. 定义 dpi 或 dpij 的含义,要足够清晰 3. 推导状态转移方程 4. 确定初始状态(边界条件) 5. 确定计算顺序(确保依赖的子问题先计算) 每件物品最多选一次。dpj = 容量为 j 时的最大价值,逆序遍历容量防止重复选取。 每

By yellowdog