简介
1、嵌套的路由/视图表;
2、模块化的、基于组件的路由配置;
3、路由参数、查询。通配符;
4、基于vue.js过滤系统的视图过滤效果;
5、细粒度的导航控制;
6、带有自动激活的css class的链接;
7、HTML5历史模式或hash模式,在ie9中自动降级;
8、自定义的滚动条行为
安装(v3.x)
安装依赖:
npm install vue-router@3.0.1 --save
- –save:
dependencies
键下,发布后还需要依赖的模块,譬如像Echarts库或者Vue框架类似的,我们在开发完后后肯定还要依赖它们,否则就运行不了。 - –save-dev:
devDependencies
键下,开发时的依赖比如安装 js的压缩包gulp-uglify 因为我们在发布后用不到它,而只是在我们开发才用到它。
以vue2.x为例,有如下文件,打开index.js文件
- 在文件中引入vue,
- 引入vue-router,
- 引入所需要的导航的页面路径,
- 使用路由vue.use(Router),
- 创建Router实例
- 在main.js中引入router.js,注册:import router from ‘./router’;
new Vue({
el: '#app',
router,// es6方式,key和value相同时,只写一个就行
components: {App},
template: '<App/>'
});
安装使用(v4.x)
vue-router如果使用v4.x版本,vue需要使用v3.x版本,如果vue使用v2.x版本,启动项目会报警告
使用方式:略~
基础
动态路由匹配
- 动态路径参数,以冒号开头。像/user/foo和/user/bar都将映射到相同的路由。
一个“路径参数”使用冒号:标记。当匹配到一个路由时,参数值会被设置到this.$route.params
,可以在每个组件内使用。
注意:当使用路由参数时, 例如从/user/foo导航到/user/bar,原来的组件实例会被复用,意味着组件的生命周期钩子不会再被调用,如果想对路由的变化做出响应的话,可以用watch监听路由或者导航守卫beforeRouteUpdate。{path: '/user/:id', component: User} - 跳转user路由时:this.$router.push({name: 'user', params: {id: '33333333'}}) this.$router.push({path: '/user/33333'})等; - 跳转其他页面路由的时候,不能使用this.$router.push('test'),可以使用this.$router.push('/test')或者对象形式;
- 路由通配符(*)匹配。 当使用一个通配符时,$route.params内会自动添加一个名为pathMatch参数。
会匹配所有路径,通常用于客户端404错误。当使用通配符路由时,含有通配符的路由应该放在最后。
{path: '*'}
会匹配以/user-开头的任意路径
{path: '/user-*'}
嵌套路由
route中使用children添加嵌套路由
注意:以/
开头的嵌套路径会被当做根路径。这让你充分的使用嵌套组件而无须设置嵌套的路径
const router = new VueRouter({
routes: [{
path: '/user/:id',
component: User,
children: [{
// 当 /user/:id/profile 匹配成功,UserProfile 会被渲染在 User 的 <router-view> 中
path: 'profile',
component: UserProfile
},{
// 当 /user/:id/posts 匹配成功,UserPosts 会被渲染在 User 的 <router-view> 中
path: 'posts',
component: UserPosts
}
]
}]
})
编程式的导航
声明式导航:
<router-link :to="{name: 'user', params?: {id: 'test'}}">
<router-link :to="{path: 'user', query?: {id: '1111'}}">
编程式导航:
- 参数location
* 字符串this.$router.push('home');
* 对象this.$router.push({path: 'home', query?: {id: '1111'}})参数会拼在url后面
* 命名的路由this.$router.push({name: 'user', params?: {id: '2222'}})参数不会拼在url后面
- 参数onComplete回调将会在导航成功完成(在所有的异步钩子被解析之后)的时候进行调用。
- 参数onAbort回调将会在导航终止(导航到相同的路由、或在当前导航完成之前导航到另一个不同的路由)的时候进行调用。
this.$router.push(location, onComplete?,onAbort?)
this.$router.replace(location, onComplete?,onAbort?)
- 在浏览器记录中前进一步,参数为整数
this.$router.go(1) => history.forward()
- 在浏览器记录中后退一步,参数为整数
this.$router.go(-1) => history.back()
命名路由
const router = new VueRouter({
routes: [{
path: '/user/:userId',
// 命名路由
name: 'user',
component: User
}]
})
命名视图
一个视图使用一个组件渲染,因此对于同个路由,多个视图就需要多个组件,components的配置需要带上s,如果router-view没有设置名字,那么默认为default。
<router-view class="view one"></router-view>
<router-view class="view two" name="a"></router-view>
<router-view class="view three" name="b"></router-view>
const router = new VueRouter({
routes: [{
path: '/',
components: {
default: Foo,
a: Bar,
b: Baz
}
}]
})
命名视图也可以嵌套
{
path: '/settings',
// 你也可以在顶级路由就配置命名视图
component: UserSettings,
children: [{
path: 'emails',
component: UserEmailsSubscriptions
}, {
path: 'profile',
components: {
default: UserProfile,
helper: UserProfilePreview
}
}]
}
重定向和别名
重定向:当用户访问/a时,URL将会被替换成/b,然后匹配路由为/b
const router = new VueRouter({
routes: [
{ path: '/a', redirect: '/b' }
]
})
const router = new VueRouter({
routes: [
// 也可以是一个命名的路由
{ path: '/a', redirect: { name: 'foo'}}
]
})
const router = new VueRouter({
routes: [
{ path: '/a', redirect: to => {
// 方法接收 目标路由(也就是'/a') 作为参数
// return 重定向的 字符串路径/路径对象
}}
]
})
别名:/a的别名是/b,当用户访问/b时,URL会保持为/b,但是路由匹配则为/a,渲染/a的页面,就像用户访问/a一样
const router = new VueRouter({
routes: [
{ path: '/a', component: A, alias: '/b' },
// 所有的 alias 和 path 值必须共享相同的参数。
{ path: '/user/:id', component: A, alias: '/u/:id' }
]
})
路由组件传参
使用props将组件和路由解耦,取代与$route的耦合
const router = new VueRouter({
routes: [
{
path: '/user/:id',
component: User,
// 布尔模式,path: '/user/:id'
props: true
// 对象模式,path: '/user'
props: {id: '777777'}
// 函数模式,path: '/user'
props: route => ({query: route.query.q})
},
// 对于包含命名视图的路由,你必须分别为每个命名视图添加props选项:
{
path: '/user/:id',
components: { default: User, sidebar: Sidebar },
props: { default: true, sidebar: false }
}
]
})
const User = {
props: ['id'],
template: '<div>User {{ id }}</div>'
}
HTML5 history模式
因为单页应用只用一个html文件;history需要配合后端配置;不然当重新刷新就会报错404。我们需要在服务器配置,如果URL匹配不到任何静态资源,就跳转到默认的index.html。
当使用history模式时,URL就像正常的url,没有#。
进阶
导航守卫
-
全局前置守卫router.beforeEach((to, from, next) => {})
- 参数to:即将要进入的目标路由对象
- 参数from:当前导航正要离开的路由
- 参数next:调用此方法resolve这个钩子。执行效果依赖next方法的调用参数
- next()进行管道中的下一个钩子,如果全部钩子执行完了,则导航的状态就是confirmed。
- next(false)中断当前的导航。如果浏览器的URL改变了(可能是用户手动或者浏览器后退按钮),那么URL地址会重置到from路由对应的地址。
- next(‘/’)或者next({path: ‘/a’})跳转到一个不同的地址。当前的导航被中断,然后进行一个新的导航。你可以向next传递任意位置对象,且允许设置例如
replace: true
、name: 'home'
之类的选项以及任何用在router-link
的to
prop或者router.push()
中的选项。 - next(error)如果传入next的参数是一个error实例,则导航会被终止且改错误会被传递给
router.onError()
注册过的回调。
-
全局解析守卫router.beforeResolve((to, from, next) => {})
与router.beforeEach类似,是获取数据或者执行任何其他操作(如果用户无法进入页面时你希望避免执行的操作)的理想位置。 -
全局后置钩子router.afterEach((to, from) => {})
-
路由独享的守卫
只在进入路由时触发,不会在params,query或者hash改变时触发,他们只有在从一个不同的路由导航时,才会被触发。const routes = [ { path: '/users/:id', component: UserDetails, beforeEnter: (to, from) => { // reject the navigation return false } }, { path: '/about', component: aboutDetails, beforeEnter: [removeQueryParams] } ] function removeQueryParams(to) { if (Object.keys(to.query).length) return { path: to.path, query: {}, hash: to.hash } }
-
组件内的守卫
- beforeRouteEnter:在渲染该组件的对应路由被验证前调用;不能获取组件实例this,因为当守卫执行时,组件实例还没有被创建。是支持给next传递回调的唯一守卫。
beforeRouteEnter (to, from, next) { next(vm => { // 通过 `vm` 访问组件实例 vm.id = '1111' }) }
- beforeRouteUpdate:在当前路由改变,但是该组件被复用时调用。可以访问this。
- beforeRouteLeave:在导航离开渲染该组件的对应路由时调用。可以访问this。
-
完整的导航解析流程
- 导航被触发;
- 在失活的组件里调用beforeRouteLeave守卫;
- 调用全局beforeEach守卫;
- 在重用的组件里调用beforeRouteUpdate守卫;
- 在路由配置里调用beforeEnter
- 解析异步路由组件;
- 在被激活的组件里调用beforeRouteEnter;
- 调用全局的beforeResolve守卫;
- 导航被确认;
- 调用全局的afterEach钩子;
- 触发DOM更新;
- 调用beforeRouteEnter守卫中传给next的回调函数,创建好的组件实例会作为回调函数的参数传入。
路由元信息
定义路由的时候可以配置 meta
字段,将任意信息附加到路由上。
一个路由匹配到的所有路由记录会暴露为
r
o
u
t
e
对
象
的
‘
route对象的 `
route对象的‘route.matched数组。我们需要遍历这个数组来检查路由记录中的meta字段。 vue Router还提供了一个
$route.meta` 方法,他是一个非递归合并所有meta字段的方法。
const routes = [{
path: '/posts',
component: PostsLayout,
children: [{
path: 'new',
component: PostsNew,
// 只有经过身份验证的用户才能创建帖子
meta: { requiresAuth: true }
}]
}]
过渡效果
- v-enter:定义进入过渡的开始状态。在元素被插入之前生效,在元素被插入之后的下一帧移除;
- v-enter-active:定义进入过渡生效时的状态。在整个进入过渡的阶段中应用。在元素被插入之前生效,在过渡/动画完成之后移除。这个类可以被用来定义进入过渡的过程时间,延迟和曲线函数;
- v-enter-to:定义进入过渡的结束状态。在元素被插入之后下一帧生效(与此同时v-enter被移除),在过渡/动画完成之后移除;
- v-leave:定义离开过渡的开始状态。在离开过渡被触发时立刻生效,下一帧被移除;
- v-leave-active:定义离开过渡生效时的状态。在整个离开过渡的阶段中应用,在离开过渡被触发时立刻生效,在过渡/动画完成之后移除。这个类可以被用来定义离开过渡的过程时间,延迟和曲线函数;
- v-leave-to:定义离开过渡的结束状态。在离开过渡被触发之后下一帧生效 (与此同时 v-leave 被删除),在过渡/动画完成之后移除。
const routes = [{
path: '/custom-transition',
component: PanelLeft,
meta: { transition: 'fade' },
},{
path: '/other-transition',
component: PanelRight,
meta: { transition: 'fade' },
}]
// 每个路由的组件有不同的过渡,你可以将元信息和动态的name结合在一起,放在<transition>上
<transition :name = "$route.meta.transition">
<router-view/>
</transition>
.fade-enter-active, .fade-leave-active {
transition: opacity 2s
}
.fade-enter, .fade-leave-to {
opacity: 0
}
数据获取
有时候,进入某个路由后,需要从服务器获取数据。例如,在渲染用户信息时,你需要从服务器获取用户的数据。通过两种方式实现:
- 导航完成之后获取:先完成导航,然后再接下来的生命周期钩子中获取数据,在数据获取期间显示加载中之类的提示;
- 导航完成之前获取:导航完成前,在路由进入的守卫中获取数据,在获取成功后执行导航。
滚动行为(v3.x)
const router = new VueRouter({
routes: [...],
scrollBehavior (to, from, savedPosition) {
// 返回 savedPosition,在按下 后退/前进 按钮时,就会像浏览器的原生表现那样,不触发滚动:
if (savedPosition) {
return savedPosition
} else {
// return 期望滚动到哪个的位置
return { x: 0, y: 0 }
}
}
})
滚动行为(v4.x)
// 创建router文件的时候,需要引入import {createRouter,createWebHashHistory} from 'vue-router';
const router = createRouter({
history: createWebHashHistory(),
routes: [...],
scrollBehavior (to, from, savedPosition) {
// return 期望滚动到哪个的位置
// return { top: 100 }
// 始终在元素 #main 上方滚动 10px
return {
// 也可以这么写
// el: document.getElementById('main'),
el: '#main',
top: -10,
}
}
})
路由懒加载
当打包构建应用时,js包会变得非常大,影响页面加载。如果我们能把不同路由对应的组件分割成不同的代码块,然后当路由被访问的时候才加载对应组件,这样就更加高效了。
懒加载简单来说就是延迟加载或按需加载,即在需要的时候的时候进行加载。
- 像vue这种单页面应用,如果没有应用懒加载,运用webpack打包后的文件将会异常的大;
- 造成进入首页时,需要加载的内容过多,时间过长,会出现长时间的白屏,即使做了loading也不理想;
- 运用懒加载则可以将页面进行划分,需要的时候加载页面,可以有效的分担首页所承担的加载压力,减少首页加载用时;
- 进入页面不用也不需要一次性加载过多资源造成加载时间过程。
const Foo = () => import('./Foo.vue')
const router = new VueRouter({
routes: [{ path: '/foo', component: Foo }]
})
打包之后的文件区别:比不使用路由懒加载的情况,多了各个页面的js
把组件按组分块
有时候我们想把某个路由下的所有组件都打包在同个异步块 (chunk) 中。只需要使用 命名 chunk (opens new window),一个特殊的注释语法来提供 chunk name (需要 Webpack > 2.4)。
const Foo = () => import(/* webpackChunkName: "group-foo" */ './Foo.vue')
const Bar = () => import(/* webpackChunkName: "group-foo" */ './Bar.vue')
const Baz = () => import(/* webpackChunkName: "group-foo" */ './Baz.vue')