一、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 的结合使用,能够构建出状态管理清晰、数据流转可控的大型单页应用。在实际项目中,需根据业务复杂度合理拆分模块,遵循 "单一职责" 原则,确保代码的可维护性与可扩展性。

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐