介绍
Vue Router 是 Vue.js 的官方路由。它与 Vue.js 核心深度集成,让用 Vue.js 构建单页应用变得轻而易举。功能包括:
- 嵌套路由映射
- 动态路由选择
- 模块化、基于组件的路由配置
- 路由参数、查询、通配符
- 展示由 Vue.js 的过渡系统提供的过渡效果
- 细致的导航控制
- 自动激活 CSS 类的链接
- HTML5 history 模式或 hash 模式
- 可定制的滚动行为
- URL 的正确编码
官方文档地址
https://router.vuejs.org/zh/introduction.html
HTML
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| <script src="https://unpkg.com/vue@3"></script> <script src="https://unpkg.com/vue-router@4"></script>
<div id="app"> <h1>Hello App!</h1> <p> <router-link to="/">Go to Home</router-link> <router-link to="/about">Go to About</router-link> </p> <router-view></router-view> </div>
|
router-link
请注意,我们没有使用常规的 a 标签,而是使用一个自定义组件 router-link 来创建链接。这使得 Vue Router 可以在不重新加载页面的情况下更改 URL,处理 URL 的生成以及编码。我们将在后面看到如何从这些功能中获益。
router-view
router-view 将显示与 URL 对应的组件。你可以把它放在任何地方,以适应你的布局。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
|
const Home = { template: '<div>Home</div>' } const About = { template: '<div>About</div>' }
const routes = [ { path: '/', component: Home }, { path: '/about', component: About }, ]
const router = VueRouter.createRouter({ history: VueRouter.createWebHashHistory(), routes, })
const app = Vue.createApp({})
app.use(router)
app.mount('#app')
|
通过调用 app.use(router),我们会触发第一次导航且可以在任意组件中以 this.$router 的形式访问它,并且以 this.$route 的形式访问当前路由:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| export default { computed: { username() { return this.$route.params.username }, }, methods: { goToDashboard() { if (isAuthenticated) { this.$router.push('/dashboard') } else { this.$router.push('/login') } }, }, }
|
使用
1 2 3 4 5 6
| vue文件 <router-link to="/demo">demo</router-link>
要有<router-view /> 作为容器
子路由页面必须有容器<router-view />
|
路由跳转
html跳转
1 2 3 4 5
| 跳转到index下的子路由news <router-link to="/index/news">demo</router-link> === <router-link :to="{name:'news'}" >新闻列表</router-link> 注意 坑 这里是单花括号
|
方法跳转
方法this.$router.push(‘/‘)
路由传参
注意:和name配对的是params,和path配对的是query
params传参
命名路由传参params
1
| this.$router.push({ name: 'news', params: { userId: 123 }})
|
接受参数
1 2
| this.$route.params.userId
|
query传参
命名路由传参params
1
| this.$router.push({ path: '/news', query: { userId: 123 }});接受参数
|
接受参数
1
| this.$route.query.userId
|
router-link写法
1
| <router-link :to="{ name: 'news', params: { userId: 1111}}">click to news page</router-link>
|
1.命名路由搭配params,刷新页面参数会丢失
2.查询参数搭配query,刷新页面数据不会丢失
3.接受参数使用this.$router后面就是搭配路由的名称就能获取到参数的值
传参跳转this.$router.push
接受参数this.$route
新打开页面跳转
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| let routeUrl = this.$router.resolve({
path: "/share",
query: {id:96}
});
window.open(routeUrl.href, '_blank');
案例 // this.$router.push({ // path: "/postManagement/jobDetails", // query: { id: id }, // }); let routeData = this.$router.resolve({ path: "/postManagement/jobDetails", query: { id: id }, }); window.open(routeData.href, '_blank');
|
this.$router.replace() (用法同上,push)
路由前进后退
this.$router.go(n) ()
路由监听
1 2 3 4 5 6
| watch: { $route (route) { this.activePath = route.path this.activeClassAPath = route.path } },
|
路由守卫
全局前置守卫
你可以使用 router.beforeEach 注册一个全局前置守卫:
1 2 3 4 5 6 7
| const router = createRouter({ ... })
router.beforeEach((to, from) => { return false })
|
当一个导航触发时,全局前置守卫按照创建顺序调用。守卫是异步解析执行,此时导航在所有守卫 resolve 完之前一直处于等待中。
每个守卫方法接收两个参数:
可以返回的值如下:
false: 取消当前的导航。如果浏览器的 URL 改变了(可能是用户手动或者浏览器后退按钮),那么 URL 地址会重置到 from 路由对应的地址。
- 一个路由地址: 通过一个路由地址重定向到一个不同的地址,如同调用
router.push(),且可以传入诸如 replace: true 或 name: 'home' 之类的选项。它会中断当前的导航,同时用相同的 from 创建一个新导航。
1 2 3 4 5 6 7 8 9 10 11
| router.beforeEach(async (to, from) => { if ( !isAuthenticated && to.name !== 'Login' ) { return { name: 'Login' } } })
|
如果遇到了意料之外的情况,可能会抛出一个 Error。这会取消导航并且调用 router.onError() 注册过的回调。
如果什么都没有,undefined 或返回 true,则导航是有效的,并调用下一个导航守卫
以上所有都同 async 函数 和 Promise 工作方式一样:
1 2 3 4 5
| router.beforeEach(async (to, from) => { // canUserAccess() 返回 `true` 或 `false` const canAccess = await canUserAccess(to) if (!canAccess) return '/login' })
|
可选的第三个参数
在之前的 Vue Router 版本中,还可以使用 第三个参数 next 。这是一个常见的错误来源,我们经过 RFC 讨论将其移除。然而,它仍然是被支持的,这意味着你可以向任何导航守卫传递第三个参数。在这种情况下,确保 next 在任何给定的导航守卫中都被严格调用一次。它可以出现多于一次,但是只能在所有的逻辑路径都不重叠的情况下,否则钩子永远都不会被解析或报错。这里有一个在用户未能验证身份时重定向到/login的错误用例:
1 2 3 4 5 6
| // BAD router.beforeEach((to, from, next) => { if (to.name !== 'Login' && !isAuthenticated) next({ name: 'Login' }) // 如果用户未能验证身份,则 `next` 会被调用两次 next() })
|
下面是正确的版本:
1 2 3 4 5
| // GOOD router.beforeEach((to, from, next) => { if (to.name !== 'Login' && !isAuthenticated) next({ name: 'Login' }) else next() })
|
全局解析守卫
你可以用 router.beforeResolve 注册一个全局守卫。这和 router.beforeEach 类似,因为它在每次导航时都会触发,不同的是,解析守卫刚好会在导航被确认之前、所有组件内守卫和异步路由组件被解析之后调用。这里有一个例子,确保用户可以访问自定义 meta 属性 requiresCamera 的路由:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| router.beforeResolve(async to => { if (to.meta.requiresCamera) { try { await askForCameraPermission() } catch (error) { if (error instanceof NotAllowedError) { return false } else { throw error } } } })
|
router.beforeResolve 是获取数据或执行任何其他操作(如果用户无法进入页面时你希望避免执行的操作)的理想位置。
全局后置钩子
你也可以注册全局后置钩子,然而和守卫不同的是,这些钩子不会接受 next 函数也不会改变导航本身:
1 2 3
| router.afterEach((to, from) => { sendToAnalytics(to.fullPath) })
|
它们对于分析、更改页面标题、声明页面等辅助功能以及许多其他事情都很有用。
它们也反映了 navigation failures 作为第三个参数:
1 2 3
| router.afterEach((to, from, failure) => { if (!failure) sendToAnalytics(to.fullPath) })
|
了解更多关于 navigation failures 的信息在它的指南中。