Vue 3 用 Pinia 持久化存储(persist)之后,存储的组件无法渲染。

Vue 3 用 Pinia 持久化存储(persist)之后,存的组件(比如 component: markRaw(SomeComponent))无法渲染。

这其实是一个常见的坑,大概总结一下原因:


为什么持久化后组件无法渲染?#

因为:

  • Pinia persist 插件是通过 localStorage / sessionStorage 持久化数据的。

  • 存进去的时候,会把你的对象转成 JSON 字符串。

  • 但是 Vue 组件(比如 Connections.vueLocalFolder.vue 这些)是函数对象,不能被 JSON 化

  • 所以,你 markRaw () 后的组件,在持久化存进去的时候,其实丢失了!
    变成了 undefined、空对象 {} 或者直接没了。

→ 然后你在页面重新刷新、读取持久化数据后,就发现 component 是空的,自然报 missing template or render function 的错误。


直接总结:#

原因 解释
JSON.stringify 不能序列化 Vue 组件 组件是函数 / 对象,不能持久化
Pinia persist 插件默认是 JSON 存储 持久化后 component 消失了
结果 页面重新渲染时找不到组件,直接爆炸

解决方案(正式版)#

最标准的处理是:

不要把组件本身持久化到 Storage。

而是只持久化 idtitleicon 这些轻量信息component 应该在内存里动态注入

怎么做?

比如改你的 MenuItem 类型:

interface MenuItem {
  id: string;
  icon: string;
  title: string;
  active: boolean;
  component?: any; // 不持久化
}

但是存到 localStorage 时,只存:

{
  id: 'files',
  icon: 'mdi:folder',
  title: '文件',
  active: false
}

然后在 store 初始化的时候,根据 id 再补充上对应的 component。


实战例子:修改你的 useLeftMenuStore#

import { defineStore } from 'pinia';
import { markRaw, ref } from 'vue';

import LocalFolder from '@/components/LocalFolder.vue';
import Connections from '@/components/terminal/Connections.vue';
import ScriptList from '@/components/scripts/ScriptList.vue';

const componentMap = {
  terminal: markRaw(Connections),
  files: markRaw(LocalFolder),
  scripts: markRaw(ScriptList)
};

export const useLeftMenuStore = defineStore(
  'leftMenu',
  () => {
    const menus = ref([
      { id: 'terminal', icon: 'mdi:console', title: '终端', active: true },
      { id: 'files', icon: 'mdi:folder', title: '文件', active: false },
      { id: 'scripts', icon: 'mdi:script-text', title: '脚本', active: false }
    ]);

    const activeMenu = ref(menus.value[0]);

    // 在应用启动时,给每个菜单动态加上 component
    menus.value.forEach(menu => {
      (menu as any).component = componentMap[menu.id];
    });

    const setActiveComponent = (item) => {
      if (!item.active) {
        menus.value.forEach((menu) => {
          menu.active = menu.id === item.id;
        });
        activeMenu.value = item;
      }
    };

    return {
      menus,
      activeMenu,
      setActiveComponent
    };
  },
  { persist: true }
);

🔵 重点就是:

  • 持久化只存 idtitleicon

  • 刷新后,根据 id 自动重新补充 component


小结一句话#

Pinia Persist 不适合直接存 Vue 组件,要通过 ID 或 Key 去恢复。

本作品采用《CC 协议》,转载必须注明作者和本文链接
比特莱布斯(bit-labs) 是一家专注于前沿软件开发的科技团队,致力于为企业和创业者提供高性能、可扩展的数字化解决方案。我们擅长:移动应用,公众号,小程序,网站,桌面端等应用开发.