5.VueRouter路由与Vuex状态管理

书诚小驿2024/09/25前端知识库Vue3

前言

随着前端技术的快速发展和前后端分离架构的普及,单页面应用(SPA)已成为现代 Web 开发的主流。在 SPA 中,前端路由和状态管理扮演着至关重要的角色。Vue3 作为当前流行的前端框架之一,提供了强大的路由(Vue Router)和状态管理(Vuex、Pinia 等)解决方案。本章节将深入探讨 Vue3 中的路由与状态管理,涵盖路由的基本概念、实现原理、搭建与配置、动态与编程式路由、命名路由与视图、路由守卫等关键内容,以及 Vuex 和 Pinia 等状态管理库的基本使用、异步处理、计算属性和辅助函数的应用,还包括 Vuex-persist 数据持久化、模块分割和多状态管理等高级话题。此外,还将探讨在组合式 API 中如何使用 Router 和 Vuex,并通过一个综合案例来加深理解。

一、路由的基本搭建与嵌套路由模

1、vue 路由的搭建

路由在 vue 中属于第三方插件,需要下载安装后进行使用。

npm i -D vue-router

安装好后,需要对路由进行配置,并且与 Vue 进行结合,让路由插件生效。在/src/router/index.js创建配置文件。

import Home from "@/views/Home.vue";
import Test from "@/views/Test.vue";
import { createRouter, createWebHistory } from "vue-router";

const routes = [
  {
    path: "/",
    component: Home,
  },
  {
    path: "/test",
    component: Test,
  },
];

// 创建路由实例
const router = createRouter({
  history: createWebHistory(),
  routes,
});

export default router;

src/main.js使用 router。

import { createApp } from "vue";
import App from "./App.vue";
import router from "./router";

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

src/views下创建 Home 页面,定义路由组件。

<template>
  <div class="wrapper">hello world</div>
</template>

<script>
export default {
  name: "HomeView",
};
</script>
<style scoped>
.wrapper {
  width: 100px;
  height: 100px;
  background: orange;
}
</style>

src/views下创建 Test 页面,定义路由组件。

<template>
  <div class="wrapper">world hello</div>
</template>

<script>
export default {
  name: "TestView",
};
</script>
<style scoped>
.wrapper {
  width: 100px;
  height: 100px;
  background: aqua;
}
</style>

/src/App.vue

<template>
  <div>
    <router-view></router-view>
  </div>
</template>

<script>
export default {
  name: "App",
};
</script>
<style></style>

本地开发环境运行项目,通过 url 切换查看路由页面

npm run serve

image.png

切换 url

image.png

2、路由跳转

修改App.vue

<template>
  <div>
    <router-link to="/">首页</router-link> |
    <router-link to="/test">测试</router-link>
    <router-view></router-view>
  </div>
</template>
<script>
export default {
  name: "App",
};
</script>
<style></style>

image.png

3、嵌套路由模式(二级路由)

src/views下创建 TestChild 页面,定义路由组件。

<template>
  <div class="wrapper">这里是test的嵌套路由</div>
</template>
<script>
export default {
  name: "TestChild",
};
</script>
<style scoped>
.wrapper {
  width: 100px;
  height: 100px;
  background: pink;
}
</style>

src/router下配置 test 嵌套路由。

import Home from "@/views/Home.vue";
import Test from "@/views/Test.vue";
import TestChild from "@/views/TestChild.vue";
import { createRouter, createWebHistory } from "vue-router";
const routes = [
  {
    path: "/",
    component: Home,
  },
  {
    path: "/test",
    component: Test,
    children: [
      {
        path: "testchild",
        component: TestChild,
      },
    ],
  },
];
const router = createRouter({
  history: createWebHistory(),
  routes,
});
export default router;

test.vue中使用嵌套路由

<template>
  <div class="wrapper">world hello</div>
  <router-link to="/test/testchild">嵌套路由</router-link>
  <div>
    <router-view></router-view>
  </div>
</template>
<script>
export default {
  name: "TestView",
};
</script>
<style scoped>
.wrapper {
  width: 100px;
  height: 100px;
  background: aqua;
}
</style>

image.png

二、动态路由模式与编程式路由模式

1、动态路由模式

所谓的动态路由其实就是不同的 URL 可以对应同一个页面,这种动态路由一般 URL 还是有规律可循的,所以非常适合做类似于详情页的功能。 通常采用path: 'xxx/:id'的方式来实现动态路由。

  • 动态路由TestChilddemo

/src/router配置TestChild动态路由

import Home from "@/views/Home.vue";
import Test from "@/views/Test.vue";
import TestChild from "@/views/TestChild.vue";
import { createRouter, createWebHistory } from "vue-router";
const routes = [
  {
    path: "/",
    component: Home,
  },
  {
    path: "/test",
    component: Test,
    children: [
      {
        path: "testchild/:id",
        component: TestChild,
      },
    ],
  },
];
const router = createRouter({
  history: createWebHistory(),
  routes,
});
export default router;

修改Test.vue

<template>
  <div class="wrapper">world hello</div>
  <router-link to="/test/testchild/1">嵌套路由</router-link>|
  <router-link to="/test/testchild/2">嵌套路由</router-link>
  <div>
    <router-view></router-view>
  </div>
</template>

image.png

image.png

  • 获取动态 id 值

TestChild.vuetemplate中获取动态路由 id

{{$route.params.id}}

2、编程式路由(插槽方式)

前面介绍过如何在页面中对路由进行切换,可以采用声明式写法<router-link>来完成,但是往往这种方式不够灵活,首先不能更灵活的控制结构和样式,其次不能自动进行路由的跳转,必须点击操作才行。那么编程式路由就会在 JavaScript 中通过逻辑的方式进行路由跳转。

1)、作用域插槽

属性作用
href这是 <router-link> 组件解析后的目标 URL,可以用它来在 JavaScript 中进行编程式导航
route包含了当前路由信息的对象,可以通过它访问路由的路径、参数、查询等。
isActive表示<router-link>是否指向当前活跃的路由,to 属性所指定的路由与当前路由匹配(包括子路由)
isExactActive表示<router-link>是否精确匹配当前活跃的路由(路径),与 to 属性所指路由完全一致(不包括子路由)
navigate通常与路由跳转相关,this.$router.push()this.$router.replace() 这样的方法也能实现路由的跳转。

2)、 @click.preventthis.$router.push(href)this.$router.replace(),

@click监听 DOM 元素的点击事件,.prevent修饰符会调用事件对象的 preventDefault() 方法 preventDefault() 方法阻止元素的默认事件行为,从而确保确保不会有其他潜在的默认行为被意外触发 this.$router.push()会向浏览器的历史记录中添加一个新的记录,因此当用户点击后退按钮时, 他们会返回到使用 push() 方法跳转之前的页面。 this.$router.replace()用于跳转到指定的 URL,但它不会向浏览器的历史记录(history stack)中添加新的记录。

3)、demo 案例,

修改App.vue,使用href, isExactActive ,isActive,href,通过this.$router.push(href)实现路由跳转

<template>
  <div>
    <router-link to="/">首页</router-link> |
    <router-link to="/test" v-slot="{ href, isActive }">
      <button :class="{ active: isActive }" @click.prevent="navigateTo(href)">
        测试
      </button>
    </router-link>
    <router-view></router-view>
  </div>
</template>
<script>
export default {
  name: "App",
  methods: {
    navigateTo(href) {
      this.$router.push(href);
    },
  },
};
</script>
<style scoped>
.active {
  color: #666;
  background: orange;
  border: none;
  /* 活跃的样式 */
  font-weight: bold;
}
</style>

image.png

image.png

修改Test.vue,使用navigatethis.$router.push(href)

<template>
  <div class="wrapper">
    <router-link to="/test/testchild/1" v-slot="{ href, isExactActive }">
      <button
        :class="{ 'exact-active1': isExactActive }"
        @click.prevent="navigateTo(href)"
      >
        嵌套路由1
      </button>
    </router-link>
    <router-link
      to="/test/testchild/2"
      custom
      v-slot="{ navigate, isExactActive }"
    >
      <button @click="navigate" :class="{ 'exact-active2': isExactActive }">
        嵌套路由2
      </button>
    </router-link>
  </div>
  <router-view></router-view>
</template>
<script>
export default {
  name: "TestView",
  methods: {
    navigateTo(href) {
      this.$router.push(href);
    },
  },
};
</script>
<style scoped>
.wrapper {
  width: 100px;
  height: 100px;
  background: green;
}
.exact-active1 {
  color: orange;
}
.exact-active2 {
  color: purple;
}
</style>

image.png

image.png

三、命名路由与命名视图与路由元信息

1、普通命名路由

在路由跳转中,除了 path 之外,你还可以为任何路由提供 name 的形式进行路由跳转。

  • 没有硬编码的 URL
  • params 的自动编码/解码
  • 防止你在 url 中出现打字错误
  • 绕过路径排序(如显示一个)

修改src/router,使用name:'test'

import Home from "@/views/Home.vue";
import Test from "@/views/Test.vue";
import { createRouter, createWebHistory } from "vue-router";
const routes = [
  {
    path: "/",
    component: Home,
  },
  {
    path: "/test",
    component: Test,
    name: "test",
  },
];
const router = createRouter({
  history: createWebHistory(),
  routes,
});
export default router;

修改App.vue,:to="{name:'test'}"

<template>
  <div>
    <router-link to="/">首页</router-link> |
    <router-link :to="{ name: 'test' }">测试 </router-link>
    <router-view></router-view>
  </div>
</template>
<script>
export default {
  name: "App",
};
</script>
<style scoped></style>

2、命名路由(携带动态路由 id)

修改src/router里的 test 路由 path

{
        path: '/test/:id',
        component: Test,
        name:'test'
}

修改App.vue,通过 params 指定路由 idparams:{id:123}

<template>
  <div>
    <router-link to="/">首页</router-link> |
    <router-link :to="{ name: 'test', params: { id: 123 } }">测试 </router-link>
    <router-view></router-view>
  </div>
</template>
<script>
export default {
  name: "App",
};
</script>
<style scoped></style>

3、同级展示多个视图

修改src/router,使用 components 将HomeTest放在一起

import Home from "@/views/Home.vue";
import Test from "@/views/Test.vue";
import { createRouter, createWebHistory } from "vue-router";
const routes = [
  {
    path: "/",
    components: {
      default: Home,
      test: Test,
    },
  },
];
const router = createRouter({
  history: createWebHistory(),
  routes,
});
export default router;

修改App.vue,使用router-view渲染视图

<template>
  <div>
    <router-view></router-view>
    <router-view name="test"></router-view>
  </div>
</template>
<script>
export default {
  name: "App",
};
</script>
<style scoped></style>

修改Test.vue

<template>
  <div class="wrapper">Test</div>
</template>
<script>
export default {
  name: "TestView",
};
</script>
<style scoped>
.wrapper {
  width: 100px;
  height: 100px;
  background: pink;
}
</style>

image.png

4、meta 属性接受附加信息

修改src/router,给 test 路由加上meta属性

import Home from "@/views/Home.vue";
import Test from "@/views/Test.vue";
import { createRouter, createWebHistory } from "vue-router";
const routes = [
  {
    path: "/",
    component: Home,
    name: "home",
  },
  {
    path: "/test",
    component: Test,
    name: "test",
    meta: { auth: true },
  },
];
const router = createRouter({
  history: createWebHistory(),
  routes,
});
export default router;

test.vue中获取打印meta

mounted(){
    console.log(this.$route.meta.auth,'auth')
}

四、 路由传递参数的多种方式

  • query 方式(显示) -> $route.query
  • params 方式(显、隐式) -> $route.params

但是 vue-router 从 4.1.4 版本开始 不再支持 params 隐式传参,该传参方式之前一直是不推荐的,因为一旦刷新页面参数就获取不到了。 query:直接在 URL 中作为查询字符串(?id=value)显示 params 是通过路径中的动态段来传递的,而不是查询字符串

  • query、params传参

修改src/router

import Home from "@/views/Home.vue";
import Test from "@/views/Test.vue";
import TestChild1 from "@/views/TestChild1.vue";
import TestChild2 from "@/views/TestChild2.vue";
import { createRouter, createWebHistory } from "vue-router";
const routes = [
  {
    path: "/",
    component: Home,
    name: "home",
  },
  {
    path: "/test",
    component: Test,
    name: "test",
    children: [
      {
        path: "testchild1",
        component: TestChild1,
        name: "test1",
      },
      {
        path: "testchild2/:id",
        component: TestChild2,
        name: "test2",
      },
    ],
  },
];
const router = createRouter({
  history: createWebHistory(),
  routes,
});
export default router;

修改Test.vue

<template>
  <div class="wrapper">
    <router-link :to="{ name: 'test1', query: { id: 111 } }">Test1</router-link>
    |
    <router-link :to="{ name: 'test2', params: { id: 222 } }"
      >Test2</router-link
    >
    <router-view></router-view>
  </div>
</template>
<script>
export default {
  name: "TestView",
};
</script>
<style scoped>
.wrapper {
  width: 100px;
  height: 100px;
  background: pink;
}
</style>

新建src/views/TestChild1.vue

<template>
  <div class="wrapper">TestChild1</div>
</template>
<script>
export default {
  name: "TestChild1",
  mounted() {
    console.log(this.$route.query, "query");
  },
};
</script>
<style scoped>
.wrapper {
  width: 100px;
  height: 100px;
  background: green;
}
</style>

新建src/views/TestChild2.vue

<template>
  <div class="wrapper">TestChild2</div>
</template>
<script>
export default {
  name: "TestChild2",
  mounted() {
    console.log(this.$route.params, "params");
  },
};
</script>
<style scoped>
.wrapper {
  width: 100px;
  height: 100px;
  background: blue;
}
</style>

image.png

image.png

五、route 对象与 router 对象

1、route 对象是获取路由信息 -> $route.params

route 对象对应属性功能作用
fullPath完整路径包含了查询参数和 hash 的完整解析后的 URL
hashURL hash 值当前的 URL hash 值 (带 #)
href解析后的目标 URL解析完成后的 URL,等同于 window.location.href
matched匹配的路由记录数组包含了当前匹配的路由的所有嵌套路径片段的路由记录(数组)
meta路由元信息包含在路由定义中可以自定义的任意字段,常用于路由元数据的存储
name当前路由的名字当前路由的名称(如果有的话)
params动态段值一个 key/value 对象,包含了动态片段和全匹配片段的键值对
path路径字符串,对应当前路由的路径,总是解析为绝对路径,如 "/test"
query查询参数一个 key/value 对象,包含了 URL 查询参数(没有 ?)

2、router 对象是调用路由方法 -> $router.push()

router 对象对应属性功能作用
addRoute动态添加路由允许你在运行时向路由映射添加新的路由规则
afterEach全局后置守卫在所有路由跳转完成以后调用,不接受任何参数
back后退模拟点击浏览器后退按钮,相当于 window.history.back()
beforeEach全局前置守卫在路由跳转前调用,常用于登录验证、页面加载前的数据处理等
beforeResolve全局解析守卫在路由被解析之后调用,但在组件被渲染之前
currentRoute当前路由对象一个可以观察的对象,表示当前激活的路由的状态信息
forward前进模拟点击浏览器前进按钮,相当于 window.history.forward()
getRoutes获取路由配置获取路由映射中定义的路由记录数组
go前进或后退控制历史记录中前进或后退的步数,相当于 window.history.go(n)
hasRoute检查路由是否存在
push编程式导航导航到不同的 URL,此方法会向 history 栈添加一个新的记录,所以,当用户点击浏览器后退按钮时,则回到之前的 URL
removeRoute动态删除路由允许你在运行时从路由映射中删除特定的路由规则

3、路由守卫详解及应用场景

vue-router 提供的导航守卫主要用来通过跳转或取消的方式守卫导航,守卫主要的作用就是在进入到指定路由前做一个拦截,看一下我们是否具备权限,如果有权限就直接进入,如果没权限就跳转到其他页面。

路由守卫分类一般可以分为三种路由守卫使用的方式:

  • 全局环境的守卫
  • 路由独享的守卫
  • 组件内的守卫

全局环境的守卫,其中to表示需要进入到哪个路由,from表示从哪个路由离开的,那么next表示跳转到指定的页面。

router.beforeEach((to, from, next) => {
  if (to.meta.auth) {
    next("/");
  } else {
    next();
  }
});

路由独享的守卫,只给某一个指定的路由添加守卫

const routes = [
  {
    name: "bar",
    component: Bar,
    beforeEnter(to, from, next) {
      if (to.meta.auth) {
        next("/");
      } else {
        next();
      }
    },
  },
];

组件内的守卫,可以通过在.vue文件中进行路由守卫的设置,代码如下:

<script>
export default {
  name: "FooView",
  beforeRouteEnter(to, from, next) {
    if (to.meta.auth) {
      next("/");
    } else {
      next();
    }
  },
};
</script>

六、Vuex 共享状态

image.png

1、Vuex 五大核心模块

模块名称描述功能与特性
state状态管理用来存放应用的状态(数据,类似于 Vue 组件中的 data 属性,是响应式的,当状态改变时,视图会自动更新
getters计算属性可以获取 state 中的状态并进行计算后返回,类似于 Vue 组件中的 computed 属性, 可以用于对状态进行复杂处理,返回处理后的结果
mutations同步状态更新唯一可以修改 state 的方法,必须是同步函数,用于处理同步事务, 提交时,可以使用 commit 方法
actions异步操作可以包含任意异步操作,通过提交 mutations 来改变 state,可以包含任意异步操作,如 API 请求, 提交时,可以使用 dispatch 方法
modules模块化开发将单一状态树分割成多个模块,每个模块拥有自己的 state、mutations、actions、getters, 使得代码更加清晰,便于维护, 可以使用 modules 属性将多个模块合并成一个单一的 Vuex store
  • 安装vuex
npm i -D vuex

2、使用state模块,状态管理

用来存放应用的状态(数据,类似于 Vue 组件中的 data 属性,是响应式的,当状态改变时,视图会自动更新

常见的 state 属性状态管理对象

属性名类型初始值描述
countnumber0计数器的当前值,用于示例
userInfoObject{ name: '', email: '' }用户信息对象,包含姓名和电子邮件
isLoggedInboolean0表示用户是否已登录的标志
cartItemsArray[]购物车中的商品列表
selectedCategorystringall当前选中的商品分类(例如:'all', 'electronics', 'books'等)
  • 新建src/store/index.js
import { createStore } from "vuex";
const store = createStore({
  state: {
    count: 0,
  },
});
export default store;
  • src/main.js中引入 store
import { createApp } from "vue";
import App from "./App.vue";
import router from "./router";
import store from "./store";
createApp(App).use(router).use(store).mount("#app");
  • .vue文件中使用store,如Home.vue
<template>
  <div>{{ $store.state.count }}</div>
</template>
<script>
export default {
  name: "HomeView",
};
</script>
<style scoped></style>

3、使用getters模块,计算属性

可以获取 state 中的状态并进行计算后返回,类似于 Vue 组件中的 computed 属性, 可以用于对状态进行复杂处理,返回处理后的结果

  • 修改src/store/index.js
import { createStore } from "vuex";
const store = createStore({
  state: {
    users: [
      { id: 1, name: "A", isActive: true },
      { id: 2, name: "B", isActive: false },
      { id: 3, name: "C", isActive: true },
    ],
  },
  getters: {
    activeUsersCount: (state) => {
      return state.users.filter((user) => user.isActive).length;
    },
  },
});
export default store;
  • 修改Home.vue
<template>
  <div class="wrapper">
    <p>活跃用户数量: {{ activeUsersCount }}</p>
    <!-- <p>活跃用户数量: {{ $store.getters.activeUsersCount }}</p> -->
  </div>
</template>
<script>
export default {
  name: "HomeView",
  computed: {
    activeUsersCount() {
      return this.$store.getters.activeUsersCount;
    },
  },
};
</script>
<style scoped>
.wrapper {
  width: 100px;
  height: 100px;
  background: orange;
}
</style>
  • 修改Test.vue,注释 count 使用
//{{ $store.state.countModule.count }}

4、使用mutations模块,同步状态更新

唯一可以修改 state 的方法,必须是同步函数,用于处理同步事务, 提交时,可以使用 commit 方法

  • 修改src/store/index.js
import { createStore } from "vuex";
const store = createStore({
  state: {
    count: 0,
  },
  mutations: {
    plus(state) {
      state.count++;
    },
  },
});
export default store;
  • 修改Home.vue
<template>
  <div class="wrapper">
    {{ $store.state.count }}
    <button @click="handleClick">点击</button>
  </div>
</template>
<script>
export default {
  name: "HomeView",
  methods: {
    handleClick() {
      // this.$store.state.count++
      this.$store.commit("plus");
    },
  },
};
</script>
<style scoped>
.wrapper {
  width: 100px;
  height: 100px;
  background: orange;
}
</style>
  • 修改Test.vue
<template>
  <div class="wrapper">
    {{ $store.state.count }}
  </div>
</template>
<script>
export default {
  name: "TestView",
};
</script>
<style scoped>
.wrapper {
  width: 100px;
  height: 100px;
  background: pink;
}
</style>

5、使用actions模块,异步操作

异步操作|可以包含任意异步操作,通过提交 mutations 来改变 state,可以包含任意异步操作,如 API 请求, 提交时,可以使用 dispatch 方法

  • 修改src/store/index.js
import { createStore } from "vuex";
const store = createStore({
  state: {
    count: 0,
  },
  mutations: {
    plus(state, payload) {
      state.count++;
      console.log(payload, "mutations-payload");
    },
  },
  actions: {
    update(context, payload) {
      setTimeout(() => {
        console.log(payload, "actions-payload");
        context.commit("plus", payload);
      });
    },
  },
});
export default store;
  • 修改Home.vue
<template>
  <div class="wrapper">
    {{ $store.state.count }}
    <button @click="handleClick">点击</button>
  </div>
</template>
<script>
export default {
  name: "HomeView",
  methods: {
    handleClick() {
      const num = 999;
      this.$store.dispatch("update", num);
    },
  },
};
</script>
<style scoped>
.wrapper {
  width: 100px;
  height: 100px;
  background: orange;
}
</style>

6、使用modules模块,模块化开发

将单一状态树分割成多个模块,每个模块拥有自己的 state、mutations、actions、getters, 使得代码更加清晰,便于维护, 可以使用 modules 属性将多个模块合并成一个单一的 Vuex store

  • 新建src/store/modules/index.js
const state = {
  count: 0,
};
const getters = {};
const actions = {};
const mutations = {
  plus(state) {
    state.count++;
  },
};
export default {
  namespaced: true,
  state,
  getters,
  actions,
  mutations,
};
  • 修改src/store/index.js,使用modules:{countModule }加载单个 countModule 模块
import { createStore } from "vuex";
import countModule from "./modules/index";
const store = new createStore({
  state: {},
  getters: {},
  mutations: {},
  actions: {},
  modules: {
    countModule,
  },
});
export default store;
  • 修改Home.vue,还是通过this.$store.commit('plus')或者this.$store.commit('countMoudlues/plus')调用 mutations 模块里的 plus 方法
<template>
  <div class="wrapper">
    {{ $store.state.countModule.count }}
    <button @click="handleClick">点击</button>
  </div>
</template>
<script>
export default {
  name: "HomeView",
  methods: {
    handleClick() {
      this.$store.commit("countMoudlues/plus");
    },
  },
};
</script>
<style scoped>
.wrapper {
  width: 100px;
  height: 100px;
  background: orange;
}
</style>
  • 修改Test.vue,通过{{ $store.state.countModule.count }}获取 count
<template>
  <div class="wrapper">
    {{ $store.state.countModule.count }}
  </div>
</template>
<script>
export default {
  name: "TestView",
};
</script>
<style scoped>
.wrapper {
  width: 100px;
  height: 100px;
  background: pink;
}
</style>

7、vuex 的辅助函数

Vuex 提供了一些辅助函数(helper functions)来帮助我们更方便地在 Vue 组件中使用 Vuex 的状态(state)、getters、mutations 和 actions。这些辅助函数通常在结合 Vue 的 mapState、mapGetters、mapMutations 和 mapActions 一起使用时非常有用。

  • 修改home.vue,@click="plus()"中 plus 可以传递参数
<template>
  <div class="wrapper">
    {{ count }}
    <button @click="plus()">点击</button>
  </div>
</template>
<script>
import { mapState, mapMutations } from "vuex";
export default {
  name: "HomeView",
  methods: {
    ...mapMutations("countModule", ["plus"]),
  },
  computed: {
    // 使用对象展开运算符将 `mapState` 的结果混入 computed 对象中
    // ...mapState(['count'])
    //如果是使用的countModule模块,则使用
    ...mapState("countModule", ["count"]),
  },
};
</script>
<style scoped>
.wrapper {
  width: 100px;
  height: 100px;
  background: orange;
}
</style>

8、Vuex-presist 对数据进行持久化处理

Vuex-presist 文档

将 Vuex 的状态持久化到本地存储(如 localStorage 或 sessionStorage),以便在页面刷新或重新加载时能够恢复状态。这对于需要长期存储的数据(如用户信息、令牌等)特别有用。

  • 安装插件,使用上一节的mutations的模块案例扩展使用Vuex-presist
npm i -D vuex-persist
  • 修改src/store/index.js
import { createStore } from "vuex";
import VuexPersistence from "vuex-persist";
const vuexLocal = new VuexPersistence({
  //默认存储window下的localStorage
  storage: window.localStorage,
  // 单一存储对象,一般会存储,state中的user和token属性
  // reducer: (state)=>({count:state.count})
});
const store = new createStore({
  state: {
    count: 0,
  },
  mutations: {
    plus(state) {
      state.count++;
    },
  },
  actions: {},
  plugins: [vuexLocal.plugin],
});
export default store;
  • 修改Home.vue
<template>
  <div class="wrapper">
    {{ $store.state.count }}
    <button @click="handleClick">点击</button>
  </div>
</template>
<script>
export default {
  name: "HomeView",
  methods: {
    handleClick() {
      // this.$store.state.count++
      this.$store.commit("plus");
    },
  },
};
</script>
<style scoped>
.wrapper {
  width: 100px;
  height: 100px;
  background: orange;
}
</style>

七、使用组合式 api 获取 router。router,vuex 对象

在 Vue 3 中,随着 Composition API 的引入,我们使用新的逻辑组合和重用来编写我们的 Vue 组件。

import { useRoute,useRouter,useStore } from 'vue-router';
export default {
  setup() {
    const route = useRoute();
    const router = useRouter();
    const store = useStore();
    console.log(route,'route)
    console.log(router,'router)
    console.log(store,'store)
  }
};
最后更新时间' 2025/1/3 14:16:58