Pinia:Vue3 状态管理的进阶之道

在 Vue3 项目的生态体系中,Pinia 作为新一代状态管理库崭露头角,为开发者提供了更为高效、简洁且功能丰富的状态管理方案,助力打造复杂而健壮的前端应用。

一、Pinia 特点剖析

1. 简洁直接的 API

Pinia 设计理念聚焦于简洁性与直观性,提供了组合式风格的 API。摒弃了 Vuex 中繁琐的 mutations 概念,开发者能够以更直接的方式操作状态,无论是定义状态、计算属性(getters)还是异步操作(actions),代码逻辑一目了然,极大降低了学习成本与代码复杂度,使得状态管理代码与组件代码风格更趋一致,易于维护与理解。

2. 热模块更新与服务端渲染友好

在现代前端开发流程中,模块热更新(HMR)是提升开发效率的关键特性。Pinia 无缝支持 HMR,当修改状态相关代码时,能够实时反馈在应用界面上,无需手动刷新页面,保持开发过程流畅高效。同时,针对服务端渲染(SSR)场景,Pinia 精心优化,确保在服务器与客户端两端均能稳定、高效地运行,为全栈 Vue3 应用开发提供坚实基础。

3. 卓越的 TS 支持

随着 TypeScript 在 Vue3 项目中的广泛普及,Pinia 深度融入 TS 生态。类型推导精准无误,无论是定义 store 的状态结构、getters 返回值类型,还是 actions 参数及返回类型,均能获得完善的类型提示与检查,有效规避类型相关错误,增强代码健壮性,为大型团队协作开发保驾护航。

二、安装指南

开启 Pinia 之旅只需一条简单的 npm 命令:

npm i pinia

三、基础使用步骤

1. 创建实例

在项目的 src 目录下新建 store 文件夹,并于其中创建 index.ts 文件,作为 Pinia 实例的创建与导出中心:

import { createPinia } from 'pinia';
 
const store = createPinia();
 
export default store;

2. 实例挂载

跳转至 main.ts 文件,引入并挂载 Pinia 实例至 Vue 应用:

import { createApp } from 'vue';
import pinia from '@/store';
import './style.css';
import App from './App.vue';
 
import router from '@/router/index';
 
createApp(App).use(router).use(pinia).mount('#app');

通过 use(pinia) 操作,将 Pinia 融入 Vue 应用生命周期,实现全局状态管理功能。

3. 创建 Store

  • 类型定义先行:在 types 文件夹下创建 store.ts 文件,用于明确状态结构类型,增强类型安全性。例如:

    export interface UserState {
    token: string;
    userInfo: { name?: string; phone?: string };
    }
  • Store 模块构建:于 store 目录下新建 modules 文件夹,创建 user.ts 文件定义具体业务 store,此处以用户模块为例:

    import { defineStore } from 'pinia';
    import { UserState } from 'types/store';
     
    // 第一个参数是 id,要求全局唯一
    export const useUserStore = defineStore('user', {
    state: () => {
      return {
        token: 'EFA68205747CB561BB7C0F85D5689856',
        userInfo: { name: 'jijc', phone: '15800000000' }
      };
    },
    getters: {
      namePic: (state) => state.userInfo.name.substring(0, 1)
    },
    actions: {
      setToken(token: string) {
        this.token = token;
      },
      setUserInfo(userInfo: UserState['userInfo']) {
        this.userInfo = {...this.userInfo,...userInfo };
      }
    }
    });

    此代码片段构建了用户模块 store,涵盖初始状态、基于状态派生的计算属性(如提取用户姓名首字母的 namePic)以及修改状态的 actions 方法。

4. 在组件中使用 Store

  • view 文件夹下的 home.vue 组件中展示如何获取 store 数据:

    <script setup lang="ts">
    import { computed } from 'vue';
    import { storeToRefs } from 'pinia';
    import { useUserStore } from '@store/user';
     
    defineOptions({
    name: 'V-home'
    });
     
    const userStore = useUserStore();
    // 获取 state 使用 computed 或者使用 storeToRefs,直接使用不具备响应式(拿到的永远是初次的值)
    const username = computed(() => userStore.userInfo.name);
    // 获取 getter 使用 storeToRefs,或者直接使用,在模板里 userStore.namePic
    const { namePic, token } = storeToRefs(userStore);
    </script>
     
    <template>
    <div>Hello: {{ namePic }}, your name is {{ username }}, your token is {{ token }}</div>
    </template>
     
    <style scoped></style>

    通过 useUserStore 获取 store 实例,巧妙运用 computedstoreToRefs 保障数据响应式更新,实时渲染至模板。

  • 同样在 view 文件夹下的 login.vue 组件演示设置 store 数据:

    <script setup lang="ts">
    import { ref } from 'vue';
    import { storeToRefs } from 'pinia';
    import { useUserStore } from '@store/user';
     
    defineOptions({
    name: 'V-login'
    });
     
    const userStore = useUserStore();
    const { userInfo, token } = storeToRefs(userStore);
    const userName = ref(userInfo.value.name);
    const userToken = ref(token);
     
    const updateUserName = () => {
    userStore.setUserInfo({
      name: userName.value
    });
    };
    const updateUserToken = () => {
    userStore.setToken(userToken.value);
    };
    </script>
     
    <template>
    <div>login page</div>
    name:
    <input type="text" v-model="userName" @input="updateUserName" />
    <br />
    token:
    <input type="text" v-model="userToken" @input="updateUserToken" />
    </template>
     
    <style scoped></style>

    借助响应式引用与 action 调用,实现用户输入数据实时同步至 store 状态。

若在组件中 @store/user 路径无法正确识别,需在 tsconfig.json 文件的 paths 配置项里添加映射:

"paths": {
  "@store/*": ["src/store/modules/*"],
},

四、持久化策略

鉴于页面刷新会致使 store 状态重置,为维持用户体验连贯性,通常借助 cookie 或本地存储(storage)实现 store 持久化。在此推荐 js-cookie 插件助力 cookie 操作。

1. 安装依赖

执行以下命令安装必要依赖:

npm i js-cookie -S
npm i @types/js-cookie -S

前者提供核心 cookie 操作功能,后者为 TS 项目补充类型定义,确保类型安全。

2. 封装工具函数

src 目录下新建 utils 文件夹,并创建 auth.ts 文件用于封装 cookie 操作逻辑:

import Cookies from 'js-cookie';
 
export const TokenKey = 'weiz-token';
 
type ExpiresData = Date | number;
export interface TokenInfo {
  token: string;
  expires: ExpiresData;
}
 
export function getToken() {
  return Cookies.get(TokenKey);
}
 
export function setToken(data: TokenInfo) {
  const { token, expires } = data;
  return expires? Cookies.set(TokenKey, token, { expires: expires }) : Cookies.set(TokenKey, token);
}
 
export function removeToken() {
  return Cookies.remove(TokenKey);
}

这些函数简洁地抽象了获取、设置与移除 token 的 cookie 操作,便于在 store 及其他模块复用。

3. 在 Store 中应用持久化

回到 store 目录下的 user.ts 文件,整合持久化逻辑:

import { getToken, setToken } from '@/utils/auth';
// 省略部分代码
state: () => {
  return {
    token: getToken() || 'EFA68205747',
    userInfo: { name: 'jijc', phone: '15800000000' }
  };
}
// 省略部分代码
setToken(token: string) {
  this.token = token;
  setToken({
    token,
    expires: 30
  });
}

state 初始化时尝试从 cookie 获取 token,确保刷新页面后状态接续;在 setToken action 中,不仅更新 store 内 token 值,还同步至 cookie 并设置有效期,保障状态持久稳固。

通过上述全方位的 Pinia 探索,从基础搭建到高级持久化,开发者能够充分驾驭这一强大工具,为 Vue3 项目构建高效、可靠且用户体验至上的状态管理体系。

最后修改:2024 年 12 月 11 日
如果觉得我的文章对你有用,请随意赞赏