@affino/datagrid-vue

中文文档
0.17.0发布于 2026/10/26MITTypeScriptES Module近 30 天下载 2051

Vue DataGrid component and headless adapter for Affino DataGrid

原始使用文档
文档目录17 节
  1. Documentation
  2. Canonical Feature Catalog
  3. Formula engine boundary
  4. Pivot boundary
  5. Runtime mode decision (Main thread vs Worker vs Server-side)
  6. Feature overview (adapter surface)
  7. Performance snapshot (how to read it)
  8. Stable API ( @affino/datagrid-vue )
  9. DataGrid component
  10. Spreadsheet Fill Integration For Custom Renderers
  11. Advanced API ( @affino/datagrid-vue/advanced )
  12. Quick start
  13. Framework independent reads and cell values
  14. Managed wheel scroll (advanced)
  15. Orchestration-heavy viewport integration
  16. 60-second integration (junior-friendly)
  17. Legacy sugar removal

@affino/datagrid-vue

The package combines the ready-made Vue DataGrid with the lower-level Vue adapter. Import the component from the package root and use /headless when building a custom renderer:

import { DataGrid } from "@affino/datagrid-vue"
import { useDataGridRuntime } from "@affino/datagrid-vue/headless"

The app implementation lives under src/app-layer; the adapter source and /headless build remain separate. See the Vue package migration guide for the old-package migration map.

@affino/datagrid-vue provides the declarative DataGrid and the Vue adapter layer over @affino/datagrid-core and @affino/datagrid-orchestration.

Documentation

Canonical Feature Catalog

Single source of truth for platform capabilities:

Formula engine boundary

If a Vue app needs direct formula-engine APIs, import them from:

Do not import formula compile/graph helpers from @affino/datagrid-core directly in new integrations.

Pivot boundary

If a Vue app needs direct pivot contracts, import them from:

Do not import pivot contracts from @affino/datagrid-core directly in new integrations.

Runtime mode decision (Main thread vs Worker vs Server-side)

ScenarioRecommended modeWhy
Up to ~10k rows, simple table, low patch ratemain-threadLowest setup complexity, good baseline latency
20k+ rows, interactive editing, heavy sort/group/filter under user actionsworker-ownedKeeps UI thread responsive, significantly lower synchronous dispatch cost
Very large datasets, remote paging/aggregation, backend-owned queryingserver-side row modelMoves data shaping and heavy compute to backend, minimizes client memory pressure

Practical default:

  • Start with main-thread.
  • Switch to worker-owned when UI responsiveness under patch pressure becomes the bottleneck.
  • Move to server-side when dataset size and query shape are primarily backend concerns.

Feature overview (adapter surface)

Out of the box (through this package):

  • Client row model: sorting, filtering, grouping, pagination, viewport range.
  • Pivot: row/column/value axes, generated pivot columns, subtotals and grand totals, layout export/import, drilldown.
  • Selection/range engine: anchor/focus/range, fill handle, drag-fill, double-click fill-down, drag-move, clipboard workflows.
  • Editing flow: patch-based updates, Excel-like freeze/reapply control (applyEdits, reapplyView, autoReapply).
  • Context menu and keyboard orchestration primitives.
  • Runtime snapshots/revisions for deterministic integration and tests.
  • Worker-owned row model support for off-main-thread compute path.
  • Server/data-source row model contracts for backend pull/push flows.

Cell-range coordinates are bound to the active row projection and visible column order. The Vue app layer stamps those revisions for its selection service and clears stale positional ranges when rows change. Record selection uses row IDs; mode: "all" is scoped to the current projection and becomes stale after a projection revision change.

Performance snapshot (how to read it)

Representative pressure benchmark trend from artifacts/performance/worker-pressure-matrix-scaled:

  • At 20k rows: worker path reduced end-to-end pressure time by roughly ~5.4x vs main-thread.
  • At 100k rows: worker path remained ahead at roughly ~1.6x.
  • At 200k rows (with heavier patch size): worker path remained ahead at roughly ~1.34x.

Interpretation for integrators:

  • Worker mode gives the strongest value when synchronous UI-thread patch dispatch becomes expensive.
  • Main-thread can be enough for smaller tables and simpler interaction profiles.
  • Server-side model is the next step when data volume/query cost dominates client constraints.

Stable API (@affino/datagrid-vue)

  • createDataGridVueRuntime
  • useDataGridRuntime
  • useDataGridSettingsStore
  • createDataGridSettingsAdapter
  • buildDataGridOverlayTransform
  • buildDataGridOverlayTransformFromSnapshot
  • mapDataGridA11yGridAttributes
  • mapDataGridA11yCellAttributes
  • useDataGridContextMenu
  • DATA_GRID_SELECTORS
  • DATA_GRID_DATA_ATTRS
  • dataGridCellSelector
  • dataGridHeaderCellSelector
  • dataGridResizeHandleSelector

Stable selector contract (for parity tests/integration-safe querying):

import {
  DATA_GRID_SELECTORS,
  dataGridCellSelector,
} from "@affino/datagrid-vue"

const viewportSelector = DATA_GRID_SELECTORS.viewport
const ownerCellSelector = dataGridCellSelector("owner")

DataGrid component

The package root exports the component-facing surface:

  • DataGrid

DataGrid is also the supported out-of-the-box fill-handle integration:

  • drag-fill from the selection corner
  • double-click fill-down to the last row in the current projection
  • post-fill Series / Copy reapply menu pinned to the visible viewport area

No legacy sugar wrapper is required for this path.

Spreadsheet Fill Integration For Custom Renderers

If you are building your own renderer instead of using DataGrid, use the current app-layer hooks exported from @affino/datagrid-vue.

Recommended composition:

  • useDataGridAppSelection for selection ownership
  • useDataGridAppClipboard for copy/cut/paste state and fill source matrices
  • useDataGridAppFill for applying fill ranges with copy / series behavior
  • useDataGridAppInteractionController for the integrated pointer/keyboard flow, including fill-handle drag, double-click fill-down, and reapplying the last fill behavior
import {
  useDataGridAppClipboard,
  useDataGridAppFill,
  useDataGridAppInteractionController,
  useDataGridAppSelection,
} from "@affino/datagrid-vue"

Use this path instead of the removed useAffinoDataGrid* wrappers.

Advanced API (@affino/datagrid-vue/advanced)

Advanced entrypoint. For new integrations prefer domain entrypoints:

  • @affino/datagrid-vue/advanced/layout
  • @affino/datagrid-vue/advanced/pointer
  • @affino/datagrid-vue/advanced/selection
  • @affino/datagrid-vue/advanced/editing
  • @affino/datagrid-vue/advanced/clipboard
  • @affino/datagrid-vue/advanced/filtering
  • @affino/datagrid-vue/advanced/history

Example:

import {
  useDataGridManagedWheelScroll,
  useDataGridColumnLayoutOrchestration,
} from "@affino/datagrid-vue/advanced/layout"
  • createDataGridViewportController
  • useDataGridCellPointerDownRouter
  • useDataGridCellPointerHoverRouter
  • useDataGridDragSelectionLifecycle
  • useDataGridDragPointerSelection
  • useDataGridFillSelectionLifecycle
  • useDataGridFillHandleStart
  • useDataGridRangeMoveLifecycle
  • useDataGridRangeMoveStart
  • useDataGridSelectionMoveHandle
  • useDataGridTabTargetResolver
  • useDataGridCellNavigation
  • useDataGridClipboardValuePolicy
  • useDataGridCellDatasetResolver
  • useDataGridCellRangeHelpers
  • useDataGridNavigationPrimitives
  • useDataGridMutationSnapshot
  • useDataGridCellVisualStatePredicates
  • useDataGridRangeMutationEngine
  • useDataGridA11yCellIds
  • useDataGridColumnUiPolicy
  • useDataGridEditableValuePolicy
  • useDataGridMoveMutationPolicy
  • useDataGridInlineEditorSchema
  • useDataGridInlineEditOrchestration
  • useDataGridInlineEditorTargetNavigation
  • useDataGridInlineEditorKeyRouter
  • useDataGridHeaderContextActions
  • useDataGridHeaderLayerOrchestration
  • useDataGridCopyRangeHelpers
  • useDataGridHeaderSortOrchestration
  • useDataGridHeaderResizeOrchestration
  • useDataGridHeaderInteractionRouter
  • useDataGridColumnFilterOrchestration
  • useDataGridEnumTrigger
  • useDataGridGroupValueLabelResolver
  • useDataGridGroupMetaOrchestration
  • useDataGridGroupBadge
  • useDataGridGroupingSortOrchestration
  • useDataGridViewportMeasureScheduler
  • useDataGridVisibleRowsSyncScheduler
  • useDataGridColumnLayoutOrchestration
  • useDataGridSelectionOverlayOrchestration
  • useDataGridRowsProjection
  • useDataGridRowSelectionOrchestration
  • useDataGridRowSelectionInputHandlers
  • useDataGridVirtualRangeMetrics
  • useDataGridContextMenuAnchor
  • useDataGridContextMenuActionRouter
  • useDataGridViewportContextMenuRouter
  • useDataGridViewportBlurHandler
  • useDataGridViewportScrollLifecycle
  • useDataGridLinkedPaneScrollSync
  • useDataGridResizeClickGuard
  • useDataGridInitialViewportRecovery
  • useDataGridManagedWheelScroll
  • useDataGridManagedTouchScroll
  • useDataGridScrollIdleGate
  • useDataGridScrollPerfTelemetry
  • useDataGridClearSelectionLifecycle
  • useDataGridGlobalPointerLifecycle
  • useDataGridPointerAutoScroll
  • useDataGridPointerPreviewRouter
  • useDataGridPointerCellCoordResolver
  • useDataGridAxisAutoScrollDelta
  • useDataGridCellVisibilityScroller
  • useDataGridGlobalMouseDownContextMenuCloser
  • useDataGridKeyboardCommandRouter
  • useDataGridQuickFilterActions
  • useDataGridCellCoordNormalizer
  • useDataGridSelectionComparators
  • useDataGridRowSelectionModel
  • useDataGridPointerModifierPolicy
  • useDataGridHistoryActionRunner
  • useDataGridInlineEditorFocus
  • useDataGridClipboardBridge
  • useDataGridClipboardMutations
  • useDataGridIntentHistory

Quick start

import { ref } from "vue"
import { useDataGridRuntime } from "@affino/datagrid-vue"

const rows = ref([])
const columns = ref([
  { key: "service", label: "Service", initialState: { width: 220 } },
])

const { api, columnSnapshot } = useDataGridRuntime({
  rows,
  columns,
})

useDataGridRuntime also exposes patchRows(updates, options?) for partial row updates without mandatory sort/filter/group recompute on every cell change:

const runtime = useDataGridRuntime({ rows, columns })
runtime.patchRows(
  [{ rowId: "r-42", data: { tested_at: "2026-02-21T10:15:00Z" } }],
  { recomputeSort: false, recomputeFilter: false, recomputeGroup: false },
)

Use setRows for full data replacement; use patchRows for interactive/streaming cell updates when you want to avoid immediate projection jumps. In no-recompute mode (recomputeSort/filter/group = false), row order/filter/group visibility can remain temporarily stale by design until a recompute pass is requested.

For app-level editing flows, prefer the higher-level applyEdits() + reapplyView() API:

const runtime = useDataGridRuntime({ rows, columns })

// default Excel-style behavior: update values, keep current view stable
runtime.applyEdits([{ rowId: "r-42", data: { tested_at: "2026-02-21T10:15:00Z" } }])

// explicit reapply when the user clicks "Reapply" or leaves edit mode
runtime.reapplyView()

// optional live-reapply mode
runtime.autoReapply.value = true

Pivot can be controlled directly from runtime API (without direct core/orchestration imports):

runtime.setPivotModel({
  rows: ["team"],
  columns: ["year"],
  values: [{ field: "revenue", agg: "sum" }],
  rowSubtotals: true,
  grandTotal: true,
})

const activePivot = runtime.getPivotModel()
runtime.setPivotModel(null)

When pivot is active, runtime automatically reconciles snapshot.pivotColumns into the column model so adapters render pivot columns without manual setColumns wiring. Base columns remain the source of truth.

Framework independent reads and cell values

The Core API exposed by runtime.api provides read.captureView() for one synchronous, revision-labelled capture of requested row, column, computed, and selection snapshots. Inspect current-projection coverage before exporting or processing datasource-backed rows. Use cells.resolveValue({ rowId, columnKey, purpose }) when a workflow needs source, accessor/computed, or formatted display values for a logical cell. Pass snapshot: { row, column } from a captured view when resolving values across an asynchronous operation; the resolver then uses only those captured inputs. Inline editing and selection summaries use the computed/accessor purpose. XLSX export resolves and detaches worksheet cell values before handing the matrix to the worker, honors explicit export-value callbacks first, and refuses incomplete or pivot projections. Its preparing progress callback fires after matrix preparation; the export does not require a row-cloning callback. These methods are framework independent; Vue owns editor drafts and DOM focus.

State imports use runtime.api.state.validate() for preflight diagnostics and runtime.api.state.set() for an applied/skipped section report. Failures include the failed section and rollback status. The app's manual row resizing stores overrides by stable row ID; index overrides remain reserved for virtualizer measurements.

Managed wheel scroll (advanced)

Use useDataGridManagedWheelScroll when you want deterministic wheel ownership (axis lock, preventDefault policy, and consistent header/body horizontal sync).

import { useDataGridManagedWheelScroll } from "@affino/datagrid-vue/advanced"

const managedWheelScroll = useDataGridManagedWheelScroll({
  resolveWheelMode: () => "managed", // "managed" | "native"
  resolveWheelAxisLockMode: () => "dominant",
  resolvePreventDefaultWhenHandled: () => true,
  resolveBodyViewport: () => viewportRef.value,
  resolveMainViewport: () => viewportRef.value
    ? {
        scrollLeft: viewportRef.value.scrollLeft,
        scrollWidth: viewportRef.value.scrollWidth,
        clientWidth: viewportRef.value.clientWidth,
      }
    : null,
  setHandledScrollTop: (nextTop) => {
    if (viewportRef.value) {
      viewportRef.value.scrollTop = nextTop
    }
  },
  setHandledScrollLeft: (nextLeft) => {
    if (viewportRef.value) {
      viewportRef.value.scrollLeft = nextLeft
    }
  },
})

function onViewportWheel(event: WheelEvent) {
  managedWheelScroll.onBodyViewportWheel(event)
}
<div ref="viewportRef" @wheel="onViewportWheel" @scroll="onViewportScroll" />
  • Call managedWheelScroll.reset() on unmount.
  • Keep DOM reads/writes in adapter/demo/UI layer; do not move wheel DOM handling into core.

Orchestration-heavy viewport integration

Use orchestration primitives from @affino/datagrid-vue/advanced to keep component code thin while preserving high-fidelity interaction behavior.

import {
  useDataGridLinkedPaneScrollSync,
  useDataGridResizeClickGuard,
  useDataGridInitialViewportRecovery,
  useDataGridRowSelectionModel,
} from "@affino/datagrid-vue/advanced"

const linkedPaneSync = useDataGridLinkedPaneScrollSync({
  resolveSourceScrollTop: () => viewportRef.value?.scrollTop ?? 0,
  mode: "css-var",
  resolveCssVarHost: () => gridRootRef.value,
})

const resizeGuard = useDataGridResizeClickGuard()

const viewportRecovery = useDataGridInitialViewportRecovery({
  resolveShouldRecover: () => totalRows.value > 1 && renderedRowsCount.value <= 1,
  runRecoveryStep: () => syncViewportMetrics(),
})

const rowSelectionModel = useDataGridRowSelectionModel({
  resolveFilteredRows: () => filteredRows.value,
  resolveRowId: row => String(row.rowId),
  resolveAllRows: () => allRows.value,
})

Recommended ownership:

  • @affino/datagrid-core: deterministic data/runtime contracts.
  • @affino/datagrid-orchestration: interaction policies and state orchestration.
  • @affino/datagrid-vue: refs/template wiring and DOM lifecycle integration.

60-second integration (junior-friendly)

import { ref } from "vue"
import { DataGrid } from "@affino/datagrid-vue"

const rows = ref([
  { rowId: "1", service: "edge-gateway", owner: "NOC" },
  { rowId: "2", service: "billing-api", owner: "Payments" },
])

const columns = [
  { key: "service", label: "Service", initialState: { width: 220 } },
  { key: "owner", label: "Owner", initialState: { width: 180 } },
]

const columnState = ref(null)
const gridState = ref(null)
<DataGrid
  :rows="rows"
  :columns="columns"
  :features="['selection', 'sorting', 'filterDsl', 'filterBuilderUI', 'columnMenu', 'cellEditors']"
  v-model:column-state="columnState"
  v-model:state="gridState"
/>
  • Uses the built-in app component with declarative feature wiring.
  • Supports state ownership with v-model:column-state and v-model:state.
  • Best fit when you want one canonical high-level component instead of separate "simple" and "advanced" component tiers.

Legacy sugar removal

The legacy useAffinoDataGrid, useAffinoDataGridUi, and useAffinoDataGridMinimal wrappers were removed.

For new integrations:

  • Use DataGrid from @affino/datagrid-vue for the canonical component path.
  • Use useDataGridRuntime from @affino/datagrid-vue/headless when you need a headless runtime.
  • Use @affino/datagrid-vue/advanced/* and app-layer hooks for custom interaction flows.

For end-to-end UI behavior, use the public guides:

版本与发布记录

@affino/datagrid-vue 共发布 70 个版本,首次发布于 2026/02/13;过去 12 个月发布了 70 个版本。

版本发布日期距今
0.17.0latest2026/10/2612 天前
0.16.32026/10/2613 天前
0.16.22026/10/2613 天前
0.16.12026/10/2315 天前
0.16.02026/10/2315 天前
0.11.02026/10/2117 天前

关于 @affino/datagrid-vue 的常见问题

@affino/datagrid-vue 是什么?

Vue DataGrid component and headless adapter for Affino DataGrid

如何安装 @affino/datagrid-vue?

在项目目录运行 npm install @affino/datagrid-vue,或使用 pnpm add / yarn add / bun add。它还需要宿主项目提供 peer 依赖:vue(^3.3.0)、pinia(^2.1.0 || ^3.0.0)。npm 7 及以上与 pnpm 8 及以上默认会自动安装必需的 peer 依赖,Yarn 需要手动安装。

@affino/datagrid-vue 支持 TypeScript 吗?

支持。包内自带 TypeScript 类型声明(package.json 声明了 types 入口),安装后即可获得类型提示,无需再安装 @types 包。

@affino/datagrid-vue 支持 ESM 还是 CommonJS?

以 ES Module 形式发布,推荐使用 import 引入。在 CommonJS 项目中,Node.js 20.19+ / 22.12+ 可以直接 require() 不含顶层 await 的 ES Module,更早的版本需要改用动态 import()。

@affino/datagrid-vue 可以商用吗?

@affino/datagrid-vue 使用 MIT 许可。宽松许可:允许商业使用、修改和再分发,通常只需在分发时保留原版权与许可声明。以上为概括,不构成法律意见,请以许可原文为准。

@affino/datagrid-vue 的下载量有多少?

根据 npm 官方下载统计,@affino/datagrid-vue 在 2026/10/05 – 2026/11/03 这 30 天内被下载 2,051 次,日均约 68 次。下载量会把 CI 构建、镜像同步和作为其他包依赖的安装一并计入,适合横向比较热度,不能直接等同于使用人数。

@affino/datagrid-vue 还在维护吗?

最新版本 0.17.0 发布于 2026/10/26(12 天前);至今共发布 70 个版本,其中过去 12 个月发布了 70 个;首次发布于 2026/02/13。发布频率只是参考信号:功能稳定的工具可能很少需要更新,建议结合源码仓库的 issue 和提交记录判断。