Vue--3、Vue 路由传参与 Vuex 状态管理
·
一、Vue Router 路由传参详解
1. 动态路由传参(Params)
通过路由配置中的动态路径参数传递,参数在路径中可见但不在查询字符串中:
- 配置路由:
// router/index.js const routes = [ { path: '/user/:userId', // 动态参数用冒号定义 name: 'userDetail', component: UserDetail } ] - 导航方式:
<!-- 路由链接 --> <router-link :to="`/user/${userId}`">用户详情</router-link> <!-- 编程式导航 --> this.$router.push({ name: 'userDetail', // 推荐使用命名路由 params: { userId: 123 } }) - 接收参数:
// UserDetail.vue created() { const userId = this.$route.params.userId console.log('接收动态参数:', userId) // 发起API请求获取用户数据 this.getUserData(userId) }
生成的 URL 示例:/user/123(刷新页面参数不丢失)
2. 查询传参与动态参数对比
| 特性 | 查询传参(Query) | 动态参数(Params) |
|---|---|---|
| URL 显示 | 包含在问号后(如?id=1) |
包含在路径中(如/user/1) |
| 刷新保留 | 是(参数在 URL 中) | 是(参数在路径中) |
| 路由配置 | 无需特殊配置 | 需要定义动态路径(如:id) |
| 传递对象 | 需 JSON.stringify () 转为字符串 | 同样需序列化(URL 仅支持字符串) |
| 导航方式 | path: '/user', query: {id:1} |
name: 'user', params: {id:1} |
| 必传性 | 非必填(可空) | 路由匹配时必填(否则 404) |
3. 路由传参高级技巧
- 传递复杂数据:
// 序列化对象为Base64 const user = { id: 1, name: 'John', roles: ['admin'] } const encodedUser = btoa(unescape(encodeURIComponent(JSON.stringify(user)))) // 传递 this.$router.push({ path: '/profile', query: { user: encodedUser } }) // 接收 created() { if (this.$route.query.user) { const decodedUser = JSON.parse(decodeURIComponent(escape(atob(this.$route.query.user)))) console.log('解析复杂数据:', decodedUser) } } - 路由守卫中获取参数:
// 全局前置守卫 router.beforeEach((to, from, next) => { if (to.path.startsWith('/user/')) { const userId = to.params.userId if (!userId) return next({ path: '/404' }) // 验证用户权限 next() } else { next() } })
二、Vuex 状态管理核心
1. Vuex 基础架构
Vuex 采用集中式存储管理应用所有组件的状态,核心概念包括:
// store/index.js
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
export default new Vuex.Store({
// 存储应用状态
state: {
count: 0,
userInfo: null,
cartItems: []
},
// 唯一修改state的方式(同步操作)
mutations: {
increment(state) {
state.count++
},
setUserInfo(state, user) {
state.userInfo = user
},
addToCart(state, product) {
state.cartItems.push(product)
}
},
// 处理异步操作,触发mutations
actions: {
async fetchUserInfo({ commit }, userId) {
try {
const response = await fetch(`/api/users/${userId}`)
const user = await response.json()
commit('setUserInfo', user)
} catch (error) {
console.error('获取用户信息失败', error)
}
},
addToCartAsync({ commit }, product) {
// 模拟异步操作
setTimeout(() => {
commit('addToCart', product)
}, 300)
}
},
// 计算属性(缓存状态派生值)
getters: {
doubleCount(state) {
return state.count * 2
},
cartTotalPrice(state, getters) {
return state.cartItems.reduce((total, item) => {
return total + item.price * item.quantity
}, 0)
}
},
// 模块拆分(大型项目必备)
modules: {
auth: {
state: { token: null },
mutations: { setToken(state, token) { state.token = token } },
// 模块内的getters/actions
},
products: {
state: { list: [] },
// ...
}
},
// 严格模式(开发环境建议开启)
strict: process.env.NODE_ENV !== 'production'
})
2. 组件中使用 Vuex
- 访问 State:
<template> <div> <p>计数:{{ count }}</p> <p>双倍计数:{{ doubleCount }}</p> </div> </template> <script> export default { computed: { // 方式一:直接通过$store访问 count() { return this.$store.state.count }, // 方式二:使用mapState辅助函数 ...mapState(['count', 'userInfo']), doubleCount() { return this.$store.getters.doubleCount } }, methods: { // 触发mutation increment() { this.$store.commit('increment') }, // 触发action fetchUser() { this.$store.dispatch('fetchUserInfo', 1) } } } </script> - mapState/mapGetters 辅助函数:
import { mapState, mapGetters, mapMutations, mapActions } from 'vuex' export default { computed: { // 映射多个state到组件计算属性 ...mapState({ count: 'count', user: 'userInfo' }), // 映射getters ...mapGetters(['doubleCount', 'cartTotalPrice']) }, methods: { // 映射mutation方法 ...mapMutations(['increment', 'setUserInfo']), // 映射action方法 ...mapActions(['fetchUserInfo', 'addToCartAsync']) } }
3. Vuex 高级特性
- 模块命名空间:避免模块间命名冲突
// 模块定义时开启命名空间 const moduleA = { namespaced: true, state: { ... }, mutations: { ... }, actions: { ... }, getters: { // 命名空间内的getter specialValue(state) { ... } } } // 组件中访问带命名空间的模块 computed: { ...mapState('moduleA', ['count']), specialValue() { return this.$store.getters['moduleA/specialValue'] } } - 插件与持久化:
// 安装vuex-persistedstate插件实现状态本地存储 import createPersistedState from 'vuex-persistedstate' export default new Vuex.Store({ plugins: [ createPersistedState({ paths: ['userInfo', 'cartItems'], // 持久化指定状态 storage: window.sessionStorage // 存储介质(localStorage/sessionStorage) }) ], // ...其他配置 })
三、路由与 Vuex 结合实践
1. 路由变化时更新状态
// 在App.vue中监听路由变化
export default {
created() {
this.$router.afterEach((to, from) => {
// 根据路由路径更新页面标题
document.title = to.meta.title || '默认标题'
// 路由切换时记录页面访问历史
this.$store.dispatch('addVisitHistory', {
path: to.path,
name: to.name,
time: new Date()
})
})
}
}
2. 基于 Vuex 状态的路由守卫
// 全局前置守卫:基于登录状态控制访问
router.beforeEach((to, from, next) => {
const isAuthenticated = !!store.state.auth.token
// 需要登录的页面
if (to.meta.requiresAuth && !isAuthenticated) {
next({ path: '/login', query: { redirect: to.fullPath } })
} else {
next()
}
})
// 路由配置示例
const routes = [
{
path: '/dashboard',
name: 'dashboard',
component: Dashboard,
meta: { requiresAuth: true, title: '控制台' }
},
{
path: '/login',
name: 'login',
component: Login,
meta: { title: '登录' }
}
]
3. 路由参数驱动状态更新
// 商品详情页:根据路由参数加载商品数据
export default {
created() {
// 初始加载
this.loadProductData()
// 监听路由参数变化(如/shop/1 -> /shop/2)
this.$route.params.$on('change', () => {
this.loadProductData()
})
},
methods: {
loadProductData() {
const productId = this.$route.params.id
this.$store.dispatch('products/fetchProduct', productId)
}
}
}
通过路由传参与 Vuex 的结合使用,能够构建出状态管理清晰、数据流转可控的大型单页应用。在实际项目中,需根据业务复杂度合理拆分模块,遵循 "单一职责" 原则,确保代码的可维护性与可扩展性。
更多推荐
所有评论(0)