# Vue

> Fabric.js Vue 3 用法：用 shallowRef 和 toRaw 避开深层响应式，用状态 store 驱动工具栏。

Source: https://fabricjs-document-engine.jscrate.dev/zh/docs/frameworks/vue
Last updated: 2026-09-28

Fabric.js Vue 3 项目有一个经典 bug：把 Fabric.js 画布放进深层 `ref`。Vue 会把它包成响应式代理，Fabric.js 就会出现各种怪问题，比如 `loadFromJSON` 之后不渲染。把画布和引擎放在 `shallowRef` 或普通变量里，工具栏状态用 `createDocumentStateStore`。

## 组件

```vue
<script setup lang="ts">
import { Canvas, Rect } from "fabric";
import {
  createDocumentEngine,
  createDocumentStateStore,
  type DocumentEngine,
  type DocumentState,
} from "fabricjs-document-engine";
import { onBeforeUnmount, onMounted, shallowRef, useTemplateRef } from "vue";

const element = useTemplateRef<HTMLCanvasElement>("element");
const engine = shallowRef<DocumentEngine>();
const state = shallowRef<DocumentState>();
let cleanup = () => {};

onMounted(() => {
  const canvas = new Canvas(element.value!, { width: 800, height: 500 });
  const created = createDocumentEngine({ canvas });
  const store = createDocumentStateStore(created);

  state.value = store.getSnapshot();
  const unsubscribe = store.subscribe(() => {
    state.value = store.getSnapshot();
  });
  engine.value = created;

  cleanup = () => {
    unsubscribe();
    store.destroy();
    created.destroy();
    canvas.dispose();
  };
});

onBeforeUnmount(() => cleanup());

function addSquare() {
  engine.value?.canvas.add(
    new Rect({ width: 80, height: 60, fill: "#2a9d8f" })
  );
}
</script>

<template>
  <div role="toolbar">
    <button @click="addSquare">Square</button>
    <button :disabled="!state?.canUndo" @click="engine?.undo()">
      Undo {{ state?.undoLabel }}
    </button>
    <button :disabled="!state?.canRedo" @click="engine?.redo()">Redo</button>
  </div>
  <canvas ref="element" aria-label="Drawing canvas" />
</template>
```

## 为什么用 shallowRef

`shallowRef` 只在你替换 `.value` 时触发更新，不会包装里面的 Fabric.js 对象，所以 Fabric.js 拿到的是真实对象。`state` 快照每次变化都是新对象，所以 `shallowRef` 照样能更新模板。

## 画布已经是响应式的

如果 Fabric.js 画布已经放进了 `reactive()` 或深层 `ref`，用 `toRaw` 取出原始对象再传入：

```ts
import { toRaw } from "vue";

const engine = createDocumentEngine({ canvas: toRaw(canvasRef.value) });
```

Pinia store 也一样：不要把 Fabric.js 对象放进 store 的 state，或者用 `markRaw` 标记它们。

Fabric.js Vue 3 的其余配置和[原生 JavaScript](https://fabricjs-document-engine.jscrate.dev/zh/docs/frameworks/vanilla-js) 一样。要保存，请加上[存储适配器](https://fabricjs-document-engine.jscrate.dev/zh/docs/storage/storage-adapters)。
