1、安装
cnpm install vue-router -S
2、在创建路由的js中引入
import Vue from 'vue'
import VueRouter from 'vue-router'
3、创建路由
routes=[{
name:'路由名称'
component:组件,
path:'/路由路径',
meta:{...}, 路由元信息,$route.meta获取
ts中定义:typings.d.ts or router.ts
import 'vue-router'
declare module 'vue-router' {
interface RouteMeta {
meta中的字段...
}
}
路由重定向
redirect: '/',
redirect: { name: 'homepage',params... } } 通过路由名重定向
redirect: to => {
to相当于$route
return { path: '/search', query: { q: to.params.searchText } }
},
写redirect的时候,可以省略component配置,唯一的例外是嵌套路由:如果一个路由有children和redirect属性,也应该有component属性
路由别名
alias: '/home'
alias: ['/:id']
alias: ['/home',...]
children:[{
alias: ['/people', 'list'] 嵌套路由中加上/则为绝对路径,/people则可访问,否则/父路由/list才能访问
}]
将路由参数传递给组件
props:true route.params将被设置为组件的props,组件可直接this.x使用
props: { ... } 手动定义传给组件props的内容,适用于静态组件
props: route => ({ query: route.query.q }) 将静态值与基于路由的值相结合
命名视图中必须为每个视图定义
components: { default: User, sidebar: Sidebar },
props: { default: true, sidebar: false }
},...]
新版本:
const router = VueRouter.createRouter({
history: VueRouter.createWebHashHistory(), hash模式
base:提供的可选 base,https://example.com/folder/之类的文件夹中时非常有用当应用程序被托管在诸如
history: createWebHistory(), history模式
history: createMemoryHistory() SSR
linkActiveClass:'x', 用于激活的RouterLink的类,默认会使用router-link-active
linkExactActiveClass :'x', 用于精准激活的RouterLink的默认类,默认会使用 router-link-exact-active
parseQuery: qs.parse, 用于解析查询的自定义实现
stringifyQuery: qs.stringify, 对查询对象进行字符串化的自定义实现,不应该在前面加上 ?
routes,
})
const app = Vue.createApp({})
app.use(router)
app.mount('#app')
3.5、嵌套路由
const routes = [
{ path: '/users/:id',
component: User,
children:[{
name:'...'
path:'不加/、不加父级路径的名称', 若加上/则为绝对路径,/x访问子路由,而不是/父路由路径/x访问
component:子组件,
children:[{...}]
}]
},
]
4、展示路由对应的组件
<router-view />
嵌套路由:在父路由的组件中设置给子路由的组件显示位置处设置
4.1、命名视图
同级展示多个视图,而不是嵌套展示
<router-view name="LeftSidebar"></router-view>
<router-view></router-view> 没有设置name默认为default
<router-view name="RightSidebar"></router-view>
routes: [
{
path: '/',
components: {
default: Home,
LeftSidebar:LeftSidebar,
RightSidebar:RightSidebar
},
},
],
嵌套命名视图同理,在children中配置多个components
4.5、编程式导航、声明式导航
声明式导航:
<router-link :to="..."> 相当于调用router.push(...)
to的使用方法和编程式跳转中的使用方式相同:
如:
<router-link :to="{ name: 'user', params: {id: 123 }}">User</router-link>
其中router-link的额外属性:
(1)custom :自定义是否应该将其内容包裹在<a>元素中
<router-link to="/home" custom v-slot="{ navigate, href, route }">
<a :href="href" @click="navigate">{{ route.fullPath }}</a>
</router-link>
渲染成:<a href="/home">/home</a>
<router-link to="/home" v-slot="{ route }">
<span>{{ route.fullPath }}</span>
</router-link>
渲染成:<a href="/home"><span>/home</span></a>
把激活的class应用到一个外部元素而不是<a>标签本身
<router-link
to="/foo"
custom
v-slot="{ href, route, navigate, isActive, isExactActive }"
>
<li
:class="[isActive && 'router-link-active', isExactActive && 'router-link-exact-active']"
>
<a :href="href" @click="navigate">{{ route.fullPath }}</a>
</li>
</router-link>
v-slot向外暴露的内容:
href:解析后的 URL
route:解析后的规范化的地址。
navigate:触发导航的函数
isActive:如果需要应用active class,为true
isExactActive:如果需要应用exact active class,为true
(2)replace:将默认的this.$router.push()替换成replace
用法:标签添加 replace
(3)active-class:设置active-class属性是当router-link中的链接被激活是,添加css类名。
也就是当前页面所有与当前地址所匹配的的链接都会被添加class属性,包括父路由的名称
如果没有设置active-class属性,vue会有一个默认的class来表示当前链接被激活:.router-link-active。
用法:active-class='类名'
(4)exact-active-class:配置当链接被精确匹配的时候应该激活的 class。
将当前所在路由名称改变,不改变父路由路径
不设置的时候,默认的class:.router-link-exact-active或全局createRouter({
linkExactActiveClass:'类名' 在主入口的style中设置该类样式
})
用法:exact-active-class='类名'
编程式导航:(返回的是Promise,可以使用await等待导航结束)
this.$router.push('/users/eduardo')
this.$router.push({ path: '/users/eduardo' }) path不能和params一起使用
this.$router.push({ name: 'user', params: { username: 'eduardo' } }) /user/:username
this.$router.push({ path: '/register', query: { plan: 'private' } }) /register?plan=private
this.$route.query获取参数
this.$router.push({ path: '/about', hash: '#team' }) /about#team
替换当前路由
this.$router.push({ path: '/home', replace: true })
this.$router.replace({ path: '/home' })
前进后退
this.$router.forward() 等效于this.$router.go(1)
this.$router.back() 等效于this.$router.go(-1)
this.$router.go(n)
事件回调
this.$router.push(...,onComplete,onAbort)
onComplete: 在导航成功完成(在所有的异步钩子被解析之后)调用
onAbort: 终止(导航到相同的路由、或在当前导航完成之前导航到另一个不同的路由)的时候进行相应的调用
等待导航结束
await this.$router.push('/users/eduardo');
检测导航故障
this.$router.push如果导航未成功,会返回一个Promise,成功返回false或undefined
const navigationResult = await router.push('/my-profile')
if(navigationResult){
失败导航
}else{
成功导航
}
检验失败类型
const failure = await router.push('/articles/2')
if(isNavigationFailure(failure, NavigationFailureType.aborted)){
NavigationFailureType.aborted:在导航守卫中返回false 中断了本次导航。
NavigationFailureType.cancelled: 在当前导航还没有完成之前又有了一个新的导航。比如,在等待导航守卫的过程中又调用了router.push
NavigationFailureType.duplicated:导航被阻止,因为我们已经在目标位置了
NavigationFailureType.redirected:导航被重定向了
如果忽略第二个参数:isNavigationFailure(failure),那么就只会检查这个failure是不是一个Navigation Failure
}
或
router.push('/admin').then(failure => {
if (isNavigationFailure(failure, NavigationFailureType.redirected)) {
...
}
})
5、动态参数路由匹配
(1)创建
const routes = [
{ path: '/users/:id', component: User },
{ path: '/路径/:键名/路径/:键名',...}
可选参数
{ path: '/users/:userId?' }, 0个或1个,匹配/users和/users/posva
通过正则区分不同类型
{ path: '/:orderId(\\d+)' } 仅匹配参数是数字的路径,如/123,在数组的顺序并不重要
{ path: '/user/:orderId(\\d+)?' } 匹配/users和/users/42
{ path: '/:productName' } 匹配任意类型
匹配多个参数
{ path: '/:chapters+' }, 0个或多个,如/one/two/three
{ path: '/:chapters*' }, 1个或多个,如/one/two/three
]
(2)传递路径参数
<router-link to='/users/值'><router-link>
this.$router.push('/users/123')
this.$router.push({ name:'users',params: {id: 123 } })
(3)获取路径参数
this.$route.params.id
6、监听路由变化
当使用路由参数时,例如从/user/foo导航到/user/bar,原来的组件实例会被复用。意味着组件的生命周期钩子不会再被调用。
watch: {
$route(to, from) {
...
}
}
this.$watch(
() => this.$route.params,
(toParams, previousParams) => {
...
}
)
或路由守卫:
beforeRouteUpdate(to, from, next) {
...
next();
}
7、404路由
const routes = [
{ path: '/users/:id', component: User },
创建可捕获参数的路由
pathMatch是参数的名称,例如,跳转到/not/found会得到,{ params: { pathMatch: ['not', 'found'] } }
pathMatch(.*)*:末尾的*表示重复的意思,若没有,在解析或跳转时,参数中的 `/` 字符将被编码,即not/found=>/not%2Ffound'
{ path: '/:pathMatch(.*)*', name: 'NotFound', component: NotFound }, 将匹配所有内容并将其放在 `$route.params.pathMatch` 下
{ path: '/user-:afterUser(.*)', component: UserGeneric }, 将匹配以`/user-`开头的所有内容,并将其放在 `$route.params.afterUser` 下
]
8、路由过渡动效
<router-view v-slot="{ Component }"> 插槽原理,v-slot向外暴露组件中的内容
<transition name="fade">
<component :is="Component" />
</transition>
</router-view>
根据meta展示不同动效
const routes = [
{
path: '/custom-transition',
component: PanelLeft,
meta: { transition: 'slide-left' },
},
{
path: '/other-transition',
component: PanelRight,
meta: { transition: 'slide-right' },
},
]
<router-view v-slot="{ Component, route }">
<transition :name="route.meta.transition || 'fade'">
<component :is="Component" />
</transition>
</router-view>
9、监听滚动行为
当切换到新路由时,想要页面滚到顶部,或者是保持原先的滚动位置等功能
const router = createRouter({
history: createWebHashHistory(),
routes: [...],
scrollBehavior (to, from, savedPosition) {
savedPosition 由浏览器的后退/前进按钮触发时才能使用
return savedPosition; 在按下后退/前进按钮时,就会像浏览器的原生表现那样
return false或{} 不会发生滚动
return { top: 0,behavior: 'smooth', } 返回滚动位置,behavior可设置滚动动画('instant'|'auto')
return {
el:选择器或dom元素,
top,left 基于选中元素的偏移位置
}
return {
el: to.hash, 如果hash用作了锚点,可实现滚动到锚点的功能
}
延迟滚动
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve({ left: 0, top: 0 })
}, 500)
})
}
})
10、路由懒加载
不要在路由中使用异步组件,异步组件仍然可以在路由组件中使用,但路由组件本身就是动态导入的。
const UserDetails = () => import('./views/UserDetails')
把某个路由下的所有组件都打包在同个异步块(chunk)中
const UserDetails = () =>
import(/* webpackChunkName: "group-user" */ './UserDetails.vue')
const router = createRouter({
routes: [{ path: '/users/:id', component: UserDetails }],
})
11、动态添加|删除路由
const router = createRouter({
history: createWebHistory(),
routes: [{ path: '/:articleName', component: Article }],
})
(1)新增路由
router.addRoute({ path: '/about', component: About })
如果新增加的路由与已有路由位置相匹配,就需要用router.push()或router.replace()来手动导航
await router.replace(router.currentRoute.value.fullPath)
也可以使用this.$route或route = useRoute()
如果添加的路由name和原有路由相同,会删除原有路由
router.addRoute({ path: '/about', name: 'about', component: About })
这将会删除之前已经添加的路由
router.addRoute({ path: '/other', name: 'about', component: Other })
添加嵌套路由
将路由的name作为第一个参数传递
router.addRoute('admin', { path: 'settings', component: AdminSettings })
(2)在导航守卫中添加路由
router.beforeEach(to => {
if (...) {
router.addRoute({...})
触发重定向,如果新添加的路由记录将与to位置相匹配,实际上导致与我们试图访问的位置不同
return to.fullPath
}
})
(3)删除路由
方式一:
const removeRoute = router.addRoute(routeRecord)
removeRoute() 删除路由如果存在的话,当路由没有名称时,这很有用
方式二:
router.removeRoute('about') 通过路由name进行删除
(4)查看路由
router.hasRoute():检查路由是否存在。
router.getRoutes():获取一个包含所有路由记录的数组。
代码示例:
main.js:
// The Vue build version to load with the `import` command
// (runtime-only or standalone) has been set in webpack.base.conf with an alias.
import Vue from 'vue'
import App from './App'
import Axios from 'axios'
import VueRouter from 'vue-router'
import HelloWorld from './components/HelloWorld'
Vue.use(VueRouter)
Vue.config.productionTip = false
const router=new VueRouter({
routes:[
{
path:'/hello',
name:"HelloWorld",
component:HelloWorld
}
]
})
/* eslint-disable no-new */
new Vue({
el: '#app',
router,
components: { App },
template: '<App/>'
})
App.vue:
<template>
<div id="app">
<img src="./assets/logo.png">
<router-view />
</div>
</template>
<script>
export default {
name: 'App',
data()
{
return{
}
}
</script>
<style>
#app {
font-family: 'Avenir', Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
margin-top: 60px;
}
</style>
嵌套路由:
import Vue from 'vue'
import VueRouter from 'vue-router'
import HelloWorld from '../components/HelloWorld'
import C from '../components/C'
import D from '../components/D'
import A from '../components/A'
import B from '../components/B'
Vue.use(VueRouter)
export default new VueRouter({
routes:[
{
path:'/hello',
name:"HelloWorld",
component:HelloWorld
},
{
path:'/c',
name:'C',
component:C,
//子路由
children:[
{
path:'cc',
component:A,
//子子路由
children:[
{
path:'ccc',
component:A
}
]
}
]
},
{
path:'/d/:name',
name:'D',
component:D,
//子路由
children:[{
path:"dd",
component:B
}]
}
]
})
===============================父路由组件
<template>
<div id="app">
<img src="./assets/logo.png">
<ul>
<router-link to='/d/jeff'>d</router-link>
<router-link to='/c'>c</router-link>
</ul>
//主路由的显示
<router-view />
</div>
</template>
<script>
import Vuedemo from './components/Vuedemo'
import Vue1 from './components/Vuee'
import A from './components/A'
import B from './components/B'
import D from './components/D'
export default {
name: 'App',
data()
{
return{
}
},
methods:{
},
components: {
Vuedemo,
Vue1,
A,
B,
D
}
}
</script>
<style>
#app {
font-family: 'Avenir', Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
margin-top: 60px;
}
</style>
=======================子路由组件
<template>
<div>
ccc
<ul>
<router-link to="/c/cc">c的子路由</router-link>
</ul>
<router-view />
</div>
</template>
<script>
export default{
name:'c',
data()
{
return{
}
}
}
</script>
<style lang='css'>
</style>