npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

vue-gridstack

v1.0.1

Published

A Vue 3 draggable responsive grid layout component built on Gridstack.js, designed for quickly creating draggable, resizable, and configurable dashboard/workbench interfaces.

Readme

链接

为什么使用 vue-gridstack?

vue-gridstackGridstack.js 封装成更适合 Vue 的受控组件模型。应用通过 v-model:layout 持有布局数据,GridstackItem 负责保持 Vue slot 内容稳定渲染,拖拽、缩放、碰撞检测和响应式布局则交给 Gridstack 处理。

这种模型让仪表盘布局更容易持久化、恢复、对比和同步到业务数据。

特性

  • 支持 Vue 3 组件导入和插件式全局安装。
  • 通过 v-model:layout 管理受控布局。
  • 任意 Vue 组件都可以作为 widget 内容稳定渲染。
  • 支持拖拽、缩放、只读、锁定、最小/最大尺寸、自动落位和子网格配置。
  • 内置 heightMode="fill",适合固定高度仪表盘、视频墙和控制台。
  • 转发 Gridstack 常用事件,并在 change 中提供计算后的最新布局。
  • 导出完整 TypeScript 类型。
  • 可获取底层 GridStack 实例,用于高级能力扩展。

安装

安装 Vue 封装组件:

npm install vue-gridstack
pnpm add vue-gridstack
yarn add vue-gridstack

vue-gridstack 的入口会引入组件样式和 Gridstack 样式。如果你的构建环境需要显式样式入口,可以在应用入口额外引入:

import "vue-gridstack/style.css";

运行时依赖:

| 包 | 版本 | | --- | --- | | gridstack | 已随 vue-gridstack 内置 |

Peer dependencies:

| 包 | 版本 | | --- | --- | | vue | ^3.5.0 |

快速开始

<template>
  <Gridstack v-model:layout="layout" :options="gridOptions">
    <GridstackItem id="sales">
      <SalesCard />
    </GridstackItem>

    <GridstackItem id="traffic">
      <TrafficChart />
    </GridstackItem>
  </Gridstack>
</template>

<script setup lang="ts">
import { ref } from "vue";
import { Gridstack, GridstackItem } from "vue-gridstack";
import type { GridstackLayoutItem, GridstackOptions } from "vue-gridstack";

const gridOptions = ref<GridstackOptions>({
  column: 12,
  margin: 8,
  disableDrag: false,
  disableResize: false
});

const layout = ref<GridstackLayoutItem[]>([
  { id: "sales", x: 0, y: 0, w: 4, h: 3 },
  { id: "traffic", x: 4, y: 0, w: 8, h: 3 }
]);
</script>

也可以全局注册:

import { createApp } from "vue";
import VueGridstack from "vue-gridstack";
import App from "./App.vue";

createApp(App).use(VueGridstack).mount("#app");

核心模型

新代码推荐始终使用下面的模型:

  • Gridstack 接收容器级配置,例如 columnmarginstaticGriddisableDragdisableResize
  • Gridstack 通过 v-model:layout 管理每个 widget 的位置和尺寸。
  • GridstackItem 只接收稳定的 id,并渲染 Vue slot 内容。
  • 每个 GridstackItem id 都应与 layout 中的某一项对应。
<Gridstack v-model:layout="layout" :options="{ column: 12 }">
  <GridstackItem v-for="widget in widgets" :key="widget.id" :id="widget.id">
    <WidgetCard :widget="widget" />
  </GridstackItem>
</Gridstack>
const layout = ref<GridstackLayoutItem[]>([
  { id: "cpu", x: 0, y: 0, w: 3, h: 2 },
  { id: "memory", x: 3, y: 0, w: 3, h: 2, minW: 2 },
  { id: "logs", w: 6, h: 3, autoPosition: true }
]);

API

Gridstack Props

| Prop | 类型 | 默认值 | 说明 | | --- | --- | --- | --- | | layout | GridstackLayoutItem[] | [] | 受控布局数据,推荐使用 v-model:layout。 | | options | GridstackOptions | {} | Gridstack 容器级配置,会传给 GridStack.init()grid.updateOptions()。 | | heightMode | "native" \| "fill" | "native" | 控制组件如何处理网格高度和 cellHeight。 |

GridstackItem Props

| Prop | 类型 | 默认值 | 说明 | | --- | --- | --- | --- | | id | string \| number | 必填 | 稳定的 widget 唯一标识,需要和 layout 中对应项一致。 |

不要把 xywhnoResizenoMove 等几何或行为配置传给 GridstackItem。这些配置应统一放在 layout 中。

<!-- 推荐 -->
<Gridstack v-model:layout="layout">
  <GridstackItem id="chart">
    <ChartPanel />
  </GridstackItem>
</Gridstack>
const layout = ref([{ id: "chart", x: 0, y: 0, w: 4, h: 3 }]);

Layout Item 字段

每一项都需要稳定的 id。组件会把下面的 widget 字段透传给 Gridstack:

| 字段 | 类型 | 说明 | | --- | --- | --- | | id | string \| number | widget 唯一标识,内部会统一转为字符串。 | | x | number \| string | 起始列,从 0 开始。 | | y | number \| string | 起始行,从 0 开始。 | | w | number \| string | 宽度,占用列数,最小会规范为 1。 | | h | number \| string | 高度,占用行数,最小会规范为 1。 | | autoPosition | boolean \| string | 交给 Gridstack 自动落位。为 true 时,组件会忽略 xy,直到落位完成。 | | minW / maxW | number \| string | 最小宽度和最大宽度。 | | minH / maxH | number \| string | 最小高度和最大高度。 | | noResize | boolean \| string | 禁止当前 widget 缩放。 | | noMove | boolean \| string | 禁止当前 widget 拖拽。 | | locked | boolean \| string | 按 Gridstack 行为锁定当前 widget。 | | content | string | Gridstack 原生内容字段。Vue 场景推荐使用 slot。 | | sizeToContent | boolean \| number \| string | 透传 Gridstack 内容自适应能力。 | | lazyLoad | boolean \| string | Gridstack 原生懒加载字段。 | | resizeToContentParent | string | Gridstack 内容自适应时使用的父级选择器。 | | subGridOpts | object | Gridstack 原生子网格配置。 |

布局同步规则:

  • 父组件修改 layout 后,组件会同步到 Gridstack。
  • 用户拖拽或缩放后,组件会触发 update:layout
  • 组件回写 xywh 时会保留已有业务字段。
  • autoPosition: true 在 Gridstack 完成落位后会被移除。
  • 未列在上表中的业务字段可以放在 layout 中,但不会传给 Gridstack。

options

options 接收 Gridstack 原生容器配置。组件只补充一个默认值:

{
  auto: false
}

auto: false 用于避免 Gridstack 在 Vue slot 渲染完成前扫描 DOM。组件会根据 layout 和已渲染的 GridstackItem 主动创建、更新 widget。

常用配置示例:

const gridOptions = computed<GridstackOptions>(() => ({
  column: 12,
  margin: 8,
  float: false,
  animate: true,
  disableDrag: !editing.value,
  disableResize: !editing.value,
  draggable: {
    handle: ".grid-stack-item-content",
    appendTo: "body"
  },
  resizable: {
    handles: "all",
    autoHide: true
  }
}));

heightMode

heightMode="native"

默认模式。网格高度遵循 Gridstack 原生行为,以及你传入的 options.cellHeight

<Gridstack v-model:layout="layout" height-mode="native" :options="{ column: 12, cellHeight: 80 }">
  <GridstackItem id="a">
    <Widget />
  </GridstackItem>
</Gridstack>

适合普通页面流、长仪表盘和需要自然增长的布局。

heightMode="fill"

组件会填满父容器高度,并根据父容器高度、行数和间距计算 cellHeight

<template>
  <section class="dashboard">
    <Gridstack v-model:layout="layout" height-mode="fill" :options="{ row: 6, column: 12 }">
      <GridstackItem id="camera">
        <CameraPlayer />
      </GridstackItem>
    </Gridstack>
  </section>
</template>

<style scoped>
.dashboard {
  height: 100%;
  min-height: 560px;
}
</style>

适合固定画布、视频墙、监控大屏和需要铺满指定容器的工作台。父元素必须有明确高度,建议设置 options.rowoptions.minRow

事件

Gridstack 会转发 Gridstack 常用事件,并在 change 事件中额外提供计算后的 nextLayout

| 事件 | 参数 | 说明 | | --- | --- | --- | | ready | (grid) | Gridstack 实例初始化完成。 | | change | (items, event, grid, nextLayout) | 布局发生变化,nextLayout 是计算后的受控布局。 | | added | (items, event, grid) | widget 被添加。 | | removed | (items, event, grid) | widget 被移除。 | | dragstart | (element, event, grid) | 开始拖拽。 | | drag | (element, event, grid) | 拖拽中。 | | dragstop | (element, event, grid) | 拖拽结束。 | | resizestart | (element, event, grid) | 开始缩放。 | | resize | (element, event, grid) | 缩放中。 | | resizestop | (element, event, grid) | 缩放结束。 | | resizecontent | (items, event, grid) | Gridstack 内容自适应事件。 | | dropped | (previousNode, newNode, event, grid) | 外部元素拖入网格。 | | enable | (event, grid) | Gridstack 被启用。 | | disable | (event, grid) | Gridstack 被禁用。 |

<Gridstack v-model:layout="layout" @ready="onReady" @change="onChange">
  <GridstackItem id="a">
    <Widget />
  </GridstackItem>
</Gridstack>
function onReady(grid: GridStack) {
  grid.compact();
}

function onChange(items, event, grid, nextLayout) {
  console.log(nextLayout);
}

大多数场景不需要在 change 中手动给 layout 赋值,因为 v-model:layout 已经会接收组件回写。change 更适合用于保存、埋点或联动其它状态。

暴露方法

可以通过模板 ref 访问公开方法:

<template>
  <Gridstack ref="gridRef" v-model:layout="layout">
    <GridstackItem id="a">
      <Widget />
    </GridstackItem>
  </Gridstack>
</template>

<script setup lang="ts">
import { ref } from "vue";
import type { GridstackExpose } from "vue-gridstack";

const gridRef = ref<GridstackExpose | null>(null);

function saveCurrentLayout() {
  const nextLayout = gridRef.value?.getLayout() ?? [];
  return api.saveLayout(nextLayout);
}

function compact() {
  gridRef.value?.getGridStack()?.compact();
}
</script>

| 方法 | 返回值 | 说明 | | --- | --- | --- | | getGridStack() | GridStack \| null | 获取 Gridstack 原生实例。 | | getGridElement() | HTMLElement \| null | 获取内部 .grid-stack-inner 元素。 | | getShellElement() | HTMLElement \| null | 获取外层 shell 元素。 | | syncWidgets() | void | 手动同步已渲染 widget 到 Gridstack。 | | updateGridCellHeight() | void | 在 heightMode="fill" 下重新计算 cellHeight。 | | getLayout() | GridstackLayoutItem[] | 从当前 Gridstack 实例读取布局。 | | getOptions() | GridstackOptions | 获取组件处理后的 Gridstack 配置。 |

常见场景

只读仪表盘

<Gridstack v-model:layout="layout" :options="{ column: 12, staticGrid: true }">
  <GridstackItem v-for="item in widgets" :key="item.id" :id="item.id">
    <WidgetCard :data="item" />
  </GridstackItem>
</Gridstack>

也可以使用 disableDragdisableResize

<Gridstack
  v-model:layout="layout"
  :options="{ column: 12, disableDrag: true, disableResize: true }"
>
  <GridstackItem id="a">
    <Widget />
  </GridstackItem>
</Gridstack>

编辑布局

<template>
  <button type="button" @click="editing = !editing">切换编辑</button>

  <Gridstack v-model:layout="layout" :options="gridOptions" @change="dirty = true">
    <GridstackItem v-for="item in widgets" :key="item.id" :id="item.id">
      <WidgetCard :data="item" />
    </GridstackItem>
  </Gridstack>
</template>

<script setup lang="ts">
import { computed, ref } from "vue";
import type { GridstackOptions } from "vue-gridstack";

const editing = ref(false);
const dirty = ref(false);

const gridOptions = computed<GridstackOptions>(() => ({
  column: 12,
  margin: 8,
  disableDrag: !editing.value,
  disableResize: !editing.value,
  resizable: { handles: "all" }
}));
</script>

动态添加 widget

新增时建议同时追加业务数据和 layout 项。使用 autoPosition: true 可以让 Gridstack 自动寻找位置。

function addWidget() {
  const id = `widget-${Date.now()}`;

  widgets.value.push({
    id,
    title: "New widget"
  });

  layout.value.push({
    id,
    w: 3,
    h: 2,
    autoPosition: true
  });
}

Gridstack 完成落位后,组件会回写真实的 xywh

删除 widget

保持渲染列表和 layout 同步:

function removeWidget(id: string) {
  widgets.value = widgets.value.filter(item => item.id !== id);
  layout.value = layout.value.filter(item => String(item.id) !== id);
}

锁定单个 widget

const layout = ref<GridstackLayoutItem[]>([
  {
    id: "fixed-panel",
    x: 0,
    y: 0,
    w: 4,
    h: 3,
    noMove: true,
    noResize: true
  }
]);

铺满父容器

<template>
  <section class="video-wall">
    <Gridstack v-model:layout="layout" height-mode="fill" :options="gridOptions">
      <GridstackItem v-for="camera in cameras" :key="camera.id" :id="camera.id">
        <CameraPlayer :camera="camera" />
      </GridstackItem>
    </Gridstack>
  </section>
</template>

<script setup lang="ts">
import { ref } from "vue";
import type { GridstackOptions } from "vue-gridstack";

const gridOptions = ref<GridstackOptions>({
  row: 2,
  column: 4,
  margin: 6,
  float: true,
  disableDrag: true,
  disableResize: true
});
</script>

<style scoped>
.video-wall {
  height: 100%;
  min-height: 560px;
}
</style>

使用 Gridstack 高级 API

<Gridstack ref="gridRef" v-model:layout="layout" @ready="onReady">
  <GridstackItem id="a">
    <Widget />
  </GridstackItem>
</Gridstack>
function onReady(grid: GridStack) {
  grid.compact();
}

function batchUpdate() {
  const grid = gridRef.value?.getGridStack();
  if (!grid) return;

  grid.batchUpdate();
  // 在这里调用 Gridstack 原生 API
  grid.batchUpdate(false);

  layout.value = gridRef.value?.getLayout() ?? layout.value;
}

原生 DOM 兼容

Vue 项目推荐使用 GridstackItem。如果你需要迁移 Gridstack 原生 DOM 写法,也可以直接传入原生节点:

<Gridstack :options="{ column: 12 }">
  <div class="grid-stack-item" gs-id="native-a" gs-x="0" gs-y="0" gs-w="4" gs-h="2">
    <div class="grid-stack-item-content">Native A</div>
  </div>

  <div class="grid-stack-item" gs-id="native-b" gs-w="3" gs-h="2" gs-auto-position="true">
    <div class="grid-stack-item-content">Native B</div>
  </div>
</Gridstack>

组件可以读取这些属性:gs-idgs-xgs-ygs-wgs-hgs-min-wgs-max-wgs-min-hgs-max-hgs-auto-positiongs-no-resizegs-no-movegs-lockedgs-size-to-content

TypeScript 导出

import type {
  GridstackExpose,
  GridstackHeightMode,
  GridstackItemId,
  GridstackLayoutItem,
  GridstackOptions
} from "vue-gridstack";

本地开发

npm install
npm run dev
npm run typecheck
npm run build
npm run build:demo
npm run build:all
npm run pack:check

脚本说明:

| 脚本 | 说明 | | --- | --- | | npm run dev | 启动本地 demo。 | | npm run typecheck | 执行 Vue 和 TypeScript 类型检查。 | | npm run build | 构建 npm 组件库产物到 dist。 | | npm run build:demo | 构建可部署 demo 到 dist-demo。 | | npm run build:all | 同时构建组件库和 demo。 | | npm run pack:check | dry-run 检查 npm 包内容。 |

常见问题

DOM 中有 widget,但位置不符合预期

确认 GridstackItem id 是否能在 layout 中找到对应项。如果没有对应 layout,组件会使用 { id, w: 1, h: 1, autoPosition: true } 作为兜底。

heightMode="fill" 高度异常

heightMode="fill" 依赖父元素有真实高度。请为父元素设置 heightmin-height,或通过布局规则让父元素拥有稳定高度。

Vue 内容被意外替换

Vue 场景推荐通过 slot 渲染内容,不建议使用 Gridstack 原生 content 字段。

持久化布局时业务字段丢失或未生效

组件回写时会保留已有 layout 项上的自定义业务字段,但只有文档列出的 Gridstack widget 字段会透传给 Gridstack。

许可证

MIT