状态管理与数据持久化

任务数据目前存在 App.vue 里,页面一多(列表页、统计页)就没法共享。本节引入 Pinia 集中管理状态,再用 localStorage + watch 实现"刷新页面数据不丢"的持久化。

安装并注册 Pinia

npm install pinia

在 main.js 中注册:

// src/main.js
import { createApp } from 'vue'
import { createPinia } from 'pinia'
import App from './App.vue'
createApp(App).use(createPinia()).mount('#app')

创建任务仓库

新建 src/stores/task.js,用 defineStore 定义仓库:

// src/stores/task.js
import { defineStore } from 'pinia'
export const useTaskStore = defineStore('task', {
  state: () => ({              // 状态:存放数据
    tasks: [],
    filter: 'all'              // 筛选:all | active | done
  }),
  getters: {                   // 派生数据
    doneCount: (state) => state.tasks.filter(t => t.done).length,
    showTasks: (state) => {
      if (state.filter === 'done') return state.tasks.filter(t => t.done)
      if (state.filter === 'active') return state.tasks.filter(t => !t.done)
      return state.tasks
    }
  },
  actions: {                   // 动作:修改状态
    addTask(title) {
      this.tasks.push({ id: Date.now(), title, done: false })
    },
    removeTask(id) {
      this.tasks = this.tasks.filter(t => t.id !== id)
    },
    toggleTask(id) {
      const t = this.tasks.find(t => t.id === id)
      if (t) t.done = !t.done
    },
    setFilter(f) {
      this.filter = f
    }
  }
})

组件中使用仓库

任何组件调用 useTaskStore() 都能拿到同一份数据:

<script setup>
import { useTaskStore } from './stores/task'
const store = useTaskStore()   // 全局唯一的数据源
store.addTask('学习 Pinia')     // 调用 action 添加任务
console.log(store.tasks)       // 输出:[{ id:…, title:'学习 Pinia', done:false }]
</script>

模板中直接渲染 store.showTasks,替代原先 props 层层传递的写法。

用 watch 实现自动持久化

在 store 文件末尾导出初始化函数:加载时先从本地恢复,之后每次变化自动保存。

// src/stores/task.js 末尾
import { watch } from 'vue'

export function initTaskPersistence() {
  const store = useTaskStore()
  // 页面加载时先从本地恢复
  const saved = localStorage.getItem('taskhub-tasks')
  if (saved) store.tasks = JSON.parse(saved)
  // 之后每次变化都自动保存(deep 监听嵌套改动)
  watch(
    () => store.tasks,
    (val) => localStorage.setItem('taskhub-tasks', JSON.stringify(val)),
    { deep: true }
  )
}

在 main.js 中调用一次即可:

import { initTaskPersistence } from './stores/task'
initTaskPersistence()

效果:添加、删除、勾选后刷新浏览器,任务列表原样恢复,数据真正"留下来"了。

小结

Pinia 让全局数据共享变得简单,watch + localStorage 补上了持久化。数据层稳定后,下一节用 Vue Router 把"列表页"和"统计页"串起来。

笔记加载中…