Code前端首页关于Code前端联系我们

Vue3 里怎么实现页面跳转?不同场景下有哪些方法?

terry 2小时前 阅读数 5 #Vue
文章标签 Vue3;页面跳转

在 Vue3 项目里,页面跳转是实现路由交互的核心操作之一,不管是做导航栏切换、按钮点击跳转,还是带参数跳转到详情页,不同场景得用不同方法才顺手,今天就把 Vue3 里常见的页面跳转方式、传参技巧,还有容易踩的坑,一次性唠明白~

声明式跳转:用 router-link 轻松实现

Vue 里专门搞了个 <router-link> 组件来做声明式导航,不用写 JS 代码,靠标签就能完成跳转。

先看最基础的用法,直接指定跳转路径:

<router-link to="/home">回到首页</router-link>

这就跟 HTML 里的 <a> 标签功能类似,但 <router-link> 更“智能”——它会自动处理单页应用的路由切换,不会像 <a> 那样整页刷新。

要是需要传参数,有两种常见写法:

  • 传 query 参数(参数会显示在 URL 里,/detail?id=1):
    <router-link :to="{ path: '/detail', query: { id: 1, title: 'xxx' }}">
      跳转到详情页
    </router-link>
  • 传 params 参数(参数藏在路由配置里,URL 里不显示,得配合 name 使用):
    先在路由配置里给页面配个 name
    const routes = [
      {
        path: '/detail/:id', 
        name: 'Detail', 
        component: Detail
      }
    ]

    然后在 <router-link> 里用 nameparams

    <router-link :to="{ name: 'Detail', params: { id: 1 }}">
      跳转到详情页
    </router-link>

啥时候适合用 <router-link> ?像导航栏、侧边菜单这种“点一下就跳转”的静态交互,用声明式写法特别清爽,不用写事件处理函数~

编程式跳转:用 router.push / replace / go 灵活控制

要是跳转逻辑需要判断(比如点击按钮后先验证表单,再决定跳不跳),这时候就得用编程式导航,靠 JS 代码控制跳转。

Vue3 里要先通过 useRouter 拿到路由实例,再调用对应的方法,步骤如下:

  1. 引入并使用 useRouter

    import { useRouter } from 'vue-router'
    export default {
      setup() {
        const router = useRouter()
        // 接下来用 router 里的方法跳转
      }
    }
  2. 常用的跳转方法:

    • router.push():最常用的跳转,会往历史记录里新增一条,就像浏览器的“前进”按钮逻辑。
      用法有三种:
      • 直接写路径字符串:router.push('/home')
      • 用对象配 pathrouter.push({ path: '/detail', query: { id: 1 } })
      • 用对象配 name + paramsrouter.push({ name: 'Detail', params: { id: 1 } })(记得路由配置里要有 name
    • router.replace():替换当前历史记录,不会新增,比如从 A 页跳 B 页,用 replace 的话,返回时会直接跳过 A 页到更前面的页面,适合“登录后替换登录页”这种场景。
      用法和 push 差不多:router.replace('/home') 或者传对象。
    • router.go(n):控制历史记录的前进后退,n 是数字。router.go(1) 是前进一页,router.go(-1) 是后退一页,跟浏览器的 history.go(n) 一个逻辑。

举个实际例子,点击按钮验证用户是否登录,再跳转:

<button @click="handleJump">跳转到个人中心</button>
import { useRouter } from 'vue-router'
import { useUserStore } from '@/stores/user' // 假设用 Pinia 存用户信息
export default {
  setup() {
    const router = useRouter()
    const userStore = useUserStore()
    const handleJump = () => {
      if (userStore.isLogin) {
        router.push('/profile') // 已登录跳个人中心
      } else {
        router.push('/login') // 未登录跳登录页
      }
    }
    return { handleJump }
  }
}

带参数的页面跳转,query 和 params 咋选?

做项目时,跳转到详情页、订单页经常需要传参数,Vue3 里主要用 queryparams 两种方式,得搞清楚它们的区别:

对比项 query 参数 params 参数
URL 显示 会显示在 URL 里(如 ?id=1 不显示在 URL 里(靠路由配置隐藏)
刷新后是否保留 刷新页面参数还在 刷新页面参数会丢失
路由配置要求 不需要配动态路由 需要配动态路由(如 /detail/:id

query 传参 & 接收

传参(以编程式为例):

router.push({ 
  path: '/detail', 
  query: { id: 1, type: 'goods' } 
})

URL 会变成 http://xxx/detail?id=1&type=goods

接收参数:用 useRoute 拿到当前路由信息,取 query 里的值:

import { useRoute } from 'vue-router'
export default {
  setup() {
    const route = useRoute()
    const id = route.query.id // 注意:query 里的参数是字符串,需要自己转数字
    const type = route.query.type
    console.log(id, type) // 1, goods
  }
}

params 传参 & 接收

传参(必须配合 name,不能用 path!因为 path 会忽略 params):

router.push({ 
  name: 'Detail', 
  params: { id: 1, title: '商品详情' } 
})

路由配置得提前写好动态参数:

const routes = [
  {
    path: '/detail/:id', // 这里的 `:id` 对应 `params.id`
    name: 'Detail',
    component: Detail
  }
]

接收参数:同样用 useRouteparams

import { useRoute } from 'vue-router'
export default {
  setup() {
    const route = useRoute()
    const id = route.params.id 
    const title = route.params.title 
    console.log(id, title) // 1, 商品详情
  }
}

注意:如果路由配置里没写对应的动态参数(比如上面路由只配了 :id,但传了 title),title 刷新后会丢失,URL 里也不会显示。params 适合传“刷新后不重要,或者需要隐藏的参数”,query 适合传“刷新后要保留,或者需要分享的参数”~

新窗口打开页面,咋搞?

单页应用里,默认的 router.push 是在当前页面内切换路由,如果要打开新标签页/新窗口,得换思路:

方法 1:用 router.resolve 生成 URL,再 window.open

先通过 router.resolve 把路由信息转成完整的 URL,再用 window.open 打开。

例子:点击按钮在新窗口打开详情页

<button @click="openNewWindow">新窗口打开详情</button>
import { useRouter } from 'vue-router'
export default {
  setup() {
    const router = useRouter()
    const openNewWindow = () => {
      // 生成目标路由的完整信息
      const target = router.resolve({ 
        name: 'Detail', 
        params: { id: 1 } 
      })
      // 新窗口打开(_blank 表示新标签页)
      window.open(target.href, '_blank')
    }
    return { openNewWindow }
  }
}

方法 2:动态创建 a 标签,设置 target="_blank"

原理和上面一样,只是用 a 标签的方式(更符合 HTML 语义化):

<a 
  :href="targetHref" 
  target="_blank" 
  rel="noopener noreferrer"
>新窗口打开详情</a>
import { computed } from 'vue'
import { useRouter } from 'vue-router'
export default {
  setup() {
    const router = useRouter()
    const targetHref = computed(() => {
      const target = router.resolve({ name: 'Detail', params: { id: 1 } })
      return target.href
    })
    return { targetHref }
  }
}

为啥不能直接用 router.push 开新窗口?因为 router.push 是操作当前页面的路由历史,单页应用里所有内容都在一个 HTML 里切换,新窗口需要加载全新的页面实例,所以必须用 window.open 或者 a 标签~
(小知识:rel="noopener noreferrer" 是为了安全——noopener 防止新窗口篡改当前页面,noreferrer 防止泄露当前页面的 referrer 信息。)

路由守卫里咋控制跳转?

有时候需要“拦截跳转”,比如用户没登录就访问个人中心,得强制跳登录页;或者页面离开前验证表单是否保存,这时候得用路由守卫

Vue3 里路由守卫分全局守卫组件内守卫,这里重点讲常用的 全局前置守卫 beforeEach组件内守卫 onBeforeRouteUpdate / onBeforeRouteLeave

全局前置守卫 beforeEach

作用:每次路由跳转前都会触发,能全局控制权限(比如登录拦截)。

用法:在路由配置文件(router/index.js)里写:

import { createRouter, createWebHistory } from 'vue-router'
import { useUserStore } from '@/stores/user' // 假设用 Pinia 存用户
const routes = [/* 你的路由配置 */]
const router = createRouter({
  history: createWebHistory(),
  routes
})
// 全局前置守卫
router.beforeEach((to, from, next) => {
  const userStore = useUserStore()
  // 判断目标页面是否需要登录(靠路由的 meta 元信息标记)
  if (to.meta.requiresAuth && !userStore.isLogin) {
    // 需要登录但没登录,跳登录页
    next('/login')
  } else {
    // 放行
    next()
  }
})
export default router

这里的 to.meta.requiresAuth 是给路由加的自定义元信息——除了控制权限,还能存页面标题、菜单图标等,比如给路由配标题:

const routes = [
  {
    path: '/profile',
    name: 'Profile',
    component: Profile,
    meta: { 
      requiresAuth: true, 
      title: '个人中心' 
    }
  }
]

组件内守卫 onBeforeRouteUpdate / onBeforeRouteLeave

作用:在组件内部控制路由,比如页面跳转前提示“表单未保存,是否离开?”

用法:在组件的 setup 里引入并使用:

import { onBeforeRouteLeave, onBeforeRouteUpdate } from 'vue-router'
import { useFormStore } from '@/stores/form' // 假设用 Pinia 存表单数据
export default {
  setup() {
    const formStore = useFormStore()
    // 页面离开前触发(比如从当前页跳走时)
    onBeforeRouteLeave((to, from) => {
      if (formStore.isDirty) { // 表单有未保存内容
        const confirm = window.confirm('表单还没保存,确定要离开吗?')
        if (confirm) {
          // 确定就放行(Vue3 组合式 API 里的组件内守卫,无需手动调用 next,逻辑判断即可)
        } else {
          // 取消就留在当前页(抛错阻止跳转)
          throw new Error('取消离开')
        }
      }
    })
    // 路由参数变化时触发(比如从 /detail/1 跳 /detail/2,组件复用的情况)
    onBeforeRouteUpdate((to, from) => {
      // 可以在这里更新数据,比如根据新的 params.id 重新请求接口
      console.log('路由参数变了,新的 id 是', to.params.id)
    })
    return {}
  }
}

注意:全局守卫(如 beforeEach)里必须调用 next() 放行/跳转;但组件内守卫onBeforeRouteXXX)在 Vue3 组合式 API 中,更推荐用逻辑判断控制(比如上面用 throw Error 阻止跳转),不用手动调 next 啦~

跳转时常见问题 & 解决办法

实际开发中,跳转经常遇到“页面不刷新”“参数丢了”“重复跳转报错”这些坑,一个个解决:

问题 1:路由参数变了,但页面没刷新

场景:比如从 /detail/1 跳转到 /detail/2,因为路由配置里 path: '/detail/:id',组件会被复用(性能优化),所以组件的生命周期不会重新执行,导致页面数据没更新。

解决方法:用 watch 监听路由变化,或者用组件内守卫 onBeforeRouteUpdate

  • watch 监听:

    import { useRoute, watch } from 'vue-router'
    export default {
      setup() {
        const route = useRoute()
        watch(
          () => route.params.id, // 监听 params.id 的变化
          (newId) => {
            // 新的 id 变化了,重新请求数据
            fetchData(newId)
          }
        )
      }
    }
  • onBeforeRouteUpdate:前面讲组件内守卫时举过例子,在这个守卫里处理数据更新~

问题 2:params 传参,刷新页面后参数丢了

原因:params 参数默认存在内存里,不在 URL 中,所以刷新后会丢失。

解决方法

  • 改成 query 传参(适合参数能暴露在 URL 的情况)。
  • 或者把 params 参数对应的动态路由配置写全,并且用 name 跳转(虽然刷新还丢,但至少跳转时能拿到)。
  • 进阶玩法:把参数存在 Pinia / Vuex 里,刷新时从存储中取(但得配合持久化,Pinia 插件存 localStorage)。

问题 3:重复点击路由,控制台报错 “Uncaught (in promise) NavigationDuplicated”

原因:Vue Router 3.x 以上版本对重复导航做了限制,重复调用 router.push 同一个路由会报错。

解决方法

  • 简单粗暴:全局捕获异常(适合项目里很多地方都有重复跳转的情况):

    // 在 router/index.js 里写
    const originalPush = router.push
    router.push = function push(location) {
      return originalPush.call(this, location).catch(err => err)
    }
  • 优雅点:跳转前判断是否已经是目标路由:

    import { useRouter, useRoute } from 'vue-router'
    export default {
      setup() {
        const router = useRouter()
        const route = useRoute()
        const handleJump = () => {
          if (route.path !== '/target') { // 判断当前是否已经是目标路由
            router.push('/target')
          }
        }
        return { handleJump }
      }
    }

Vue3 里的页面跳转得根据场景选方法:静态导航用 router-link 省心,逻辑复杂的用 router.push/replace 灵活,传参分 query/params 各有适用场景,新窗口打开得靠 router.resolve + window.open,路由守卫能搞权限和拦截,把这些方法吃透,不管是做后台管理系统的菜单切换,还是电商项目的商品详情跳转,都能游刃有余~要是你还有其他跳转相关的疑问,评论区随时喊我~

版权声明

本文仅代表作者观点,不代表Code前端网立场。
本文系作者Code前端网发表,如需转载,请注明页面地址。

发表评论:

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。

热门