Vue Router
使用Vue路由之前我们需要先安装router组件,由于VueCli4以上的版本可以很方便的使用vue ui添加vue router组件,所以这里就不再做详细的说明了。
使用Router
修改router/index.js
router的使用非常的简单,首先我们需要在index.js中添加路由规则。下面介绍了两种路由的注册方式
1、引入组件
2、创建router,修改模式为history
3、添加Vue.use(VueRouter)
4、创建routers,指定路由规则
5、在router中引入routers
6、将router导出
使用redirect可以进行重定向,定向到其他路由中。但是一定要注意,重定向到的路由一定要在此之前声明!!
import Vue from 'vue'
import VueRouter from 'vue-router'
import Home from '../views/Home.vue'
Vue.use(VueRouter)
const routes = [
{
path: '/',
name: 'Home',
component: Home
},
{
path: '',
redirect: '/'
},
{
path: '/about',
name: 'About',
// route level code-splitting
// this generates a separate chunk (about.[hash].js) for this route
// which is lazy-loaded when the route is visited.
component: () => import(/* webpackChunkName: "about" */ '../views/About.vue')
}
]
const router = new VueRouter({
mode: 'history',
base: process.env.BASE_URL,
routes
})
export default router
route中支持自定义属性!
在main.js中引入路由规则
当导入的是某个文件夹下的index.js时,可以直接使用文件夹代替
1、导入组件
2、导入router/index.js
3、在Vue中注册路由
import Vue from 'vue'
import App from './App.vue'
import router from './router'
import './plugins/element.js'
Vue.config.productionTip = false
new Vue({
router,
render: h => h(App)
}).$mount('#app')
在App.vue中使用路由
使用router-link标签使用路由
tag可以指定router-link使用什么组件代替
replace指定页面是否可以回退
router-view标签指定路由组件出现的位置
<template>
<div id="app">
<router-link to="/" tag="button" replace>首页</router-link>
<router-link to="/about" tag="button">关于</router-link>
<router-view></router-view>
</div>
</template>
<script>
export default {
name: 'app',
components: {
}
}
</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>
设置当前激活项的前景色
当选中某个页面时,我们经常要改变其样式,如果单独控制会非常麻烦,可以在App.vue中创建一个样式:
.active{
color: red;
}
在index.js中设置激活link的样式即可
const router = new VueRouter({
mode: 'history',
base: process.env.BASE_URL,
linkActiveClass: 'active',
routes
})
使用事件调用路由
不能使用原生进行路由,必须由router路由进行
自定义事件使用路由的方法非常简单,使用this.$router.push("/");
即可,但是一定要注意加上<router-view></router-view>
<template>
<div id="app">
<router-link to="/" replace>首页</router-link>
<router-link to="/about" replace>关于</router-link>
<router-view></router-view>
<button @click="homePage">首页</button>
<button @click="aboutPage">关于</button>
</div>
</template>
<script>
export default {
name: "app",
methods: {
homePage: function () {
this.$router.push("/");
},
aboutPage: function () {
this.$router.push("/about");
}
},
components: {},
};
</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>
动态路由
在某些情况下,一个页面的path路径可能事不确定的,比如我们进入用户界面时,希望如下的路径:
/user/aaaa或者/user/bbbb,除了前面的user之外,后面还跟上了用户的id。上述的需求就称之为动态路由。
先配置user的路由
在user后添加一段/:flag
{
path: '/user/:userId',
name: 'User',
component: User
}
在App.vue中是使用
此时在App.vue中,添加动态属性
data() {
return {
userId: "rayfoo"
};
},
此时,即可使用动态路由
<router-link :to="'/user/'+userId" replace>用户</router-link>
在组件中获取动态路由的属性
如果跳转到对应页面后,需要获取动态路由中的属性,可以使用$route获取
(route和)router不同,前者是获取当前活跃的路由,而后者是全局路由对象
<template>
<div id="app">
<h2>我是用户页面</h2>
<p>我是用户的相关信息</p>
<p v-text="userId"></p>
</div>
</template>
<script>
export default {
name: "User",
computed: {
userId(){
//获取当前活跃的路由
return this.$route.params.userId;
}
}
};
</script>
<style>
</style>
打包文件的解析
vue文件执行打包命令之后,所有js文件会编译为三个js文件
- app.hash.js 用户的业务代码
- chunk-vendors.hash.js Vue及第三方插件代码
- about.hash.js 项目的一些信息
路由的懒加载
当项目达到一定量级,app.hash.js中的代码会越来越大,当用户首次访问页面的时候,加载的事件会过长,所以我们可以将项目分为若干块,使用路由进行懒加载。
可以使用路由,将每个路由都打包为一个js文件,当使用某个路由时,再去加载对应的资源。
在上面的index.js中,aubot的引入代码如下。如果英文好的话,可以看出其注释中说明了这是一种懒加载的形式,所以在上面的代码中会有一个about.hash.js文件
{
path: '/about',
name: 'About',
// route level code-splitting
// this generates a separate chunk (about.[hash].js) for this route
// which is lazy-loaded when the route is visited.
component: () => import(/* webpackChunkName: "about" */ '../views/About.vue')
}
也可以使用下面的形式,进行懒加载(推荐)
使用懒加载后的数据会被打包为chunk-hash.hash.js
//lazy-loaded
const User = () => import('../views/User.vue')
{
path: '/user/:userId',
name: 'User',
component: User
}
使用懒加载可以大大的提高页面的渲染效率,尽量保证每个路由都以懒加载形式引入。
路由嵌套
路由嵌套是一个很常见的功能
比如在home页面中,我们希望通过/home/news和/home/message,访问一些内容
一个路径映射一个组件,访问这两个路径也会分别渲染这两个组件
路由嵌套的使用步骤
1、创建对应的子组件
2、在路由映射中配置对应的子路由
需要注意的是,子路由中,千万不要加/前缀!
{
path: '/home',
name: 'Home',
component: Home,
children: [
{
path: 'news',
component: () => import('../views/home/news.vue')
},
{
path: 'message',
component: () => import('../views/home/message.vue')
}
]
}
如果需要指定默认加载的子路由,可以新增一个默认的子路由+使用redirect来实现
{
path: '/home',
name: 'Home',
component: Home,
children: [
{
path: '',
redirect: 'news'
},
{
path: 'news',
component: () => import('../views/home/news.vue')
},
{
path: 'message',
component: () => import('../views/home/message.vue')
}
]
}
3、在Home组件内部使用
<template>
<div class="home">
<h1>我是首页</h1>
<p>我是内容</p>
<router-link to="/home/news">新闻</router-link>
<router-link to="/home/message">消息</router-link>
<router-view></router-view>
</div>
</template>
<script>
export default {
name: 'Home'
}
</script>
路由之间的消息传递
路由传递主要有两种类型,params和query
param类型
-
配置路由的格式:/router/:id
-
传递的方式:在path后面跟上对应的值
-
传递后形成的路径:/router/123,/router/abc
前面介绍的动态路由中使用的this.$route.params.val;就是param进行传递参数的方法,详细的内容请看h2:[动态路由]
query的类型
- 配置路由的格式:/router,也就是普通的配置
- 传递的方式:对象中使用query的key作为传递方式
- 传递后形成的路径:/router?id=123,/router?id=abc
下面我们介绍另一种更加优雅的写法:query
传递参数:
<router-link :to="{path:'/profile',query:{name:'rayfoo',age:18}}" replace>我的</router-link>
取出参数:
<template>
<div id="profile">
<h1>我的</h1>
<p v-text="$route.query"></p>
<p v-text="$route.query.name"></p>
</div>
</template>
<script>
export default {};
</script>
<style>
</style>
事件中使用query
案例代码:
<template>
<div id="app">
<router-link to="/" replace>首页</router-link>
<router-link to="/about" replace>关于</router-link>
<router-link :to="'/user/'+userId" replace>用户</router-link>
<router-link :to="{path:'/profile',query:{name:'rayfoo',age:18}}" replace>我的</router-link>
<router-view></router-view>
<button @click="homePage">首页</button>
<button @click="aboutPage">关于</button>
<button @click="userPage">用户</button>
<button @click="profilePage">我的</button>
</div>
</template>
<script>
export default {
name: "app",
data() {
return {
userId: "rayfoo"
};
},
methods: {
//此处多次点击会报错,这是因为跳转页面重复了 可以进行判断
homePage: function () {
this.$router.push("/");
},
aboutPage: function () {
this.$router.push("/about");
},
userPage: function(){
this.$router.push("/user/" + this.userId);
},
profilePage: function(){
this.$router.push({
path: '/profile',
query: {
name: 'giao',
age: 25
}
});
}
},
components: {}
};
</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>
解决自定义事件路由跳转多次点击报错的问题
在main.js的下方添加如下代码即可解决:
import Router from 'vue-router'
const originalPush = Router.prototype.push
Router.prototype.push = function push(location) {
return originalPush.call(this, location).catch(err => err)
}
导航守卫
所谓导航守卫就是当Vue路由发生前后,对其的监听,我们就可以对相应的操作进行监听和操作。个人感觉其类似于后端的AOP、拦截器等思想。
前置钩子
前置守卫是对路由跳转之前的监听,它可以实现权限校验等操作。
需求:加载不同组件的时候,标题显示与之对应的值
解决方案1:使用生命周期函数
可以借助组件生命周期函数中的created()来进行监听,使用document.title设置标题内容。但是不能因为这一个需求改变所有的组件。
解决方案2:使用全局前置守卫
利用route中的自定义属性name+前置守卫实现title的设置。
//前置守卫
router.beforeEach((to,from,next)=>{
//从from去to
document.title = to.matched[0].name
next()
})
使用matched是避免多级url中调用的问题。如果没有多级路径可以直接使用to.name
当一个导航触发时,全局前置守卫按照创建顺序调用。守卫是异步解析执行,此时导航在所有守卫 resolve 完之前一直处于 等待中。
每个守卫方法接收三个参数:
- to: Route: 即将要进入的目标 路由对象
- from: Route: 当前导航正要离开的路由
- next: Function: 一定要调用该方法来 resolve 这个钩子。执行效果依赖
next
方法的调用参数。- next(): 进行管道中的下一个钩子。如果全部钩子执行完了,则导航的状态就是 confirmed (确认的)。
- next(false): 中断当前的导航。如果浏览器的 URL 改变了 (可能是用户手动或者浏览器后退按钮),那么 URL 地址会重置到
from
路由对应的地址。 - next('/') 或者 next({ path: '/' }): 跳转到一个不同的地址。当前的导航被中断,然后进行一个新的导航。你可以向
next
传递任意位置对象,且允许设置诸如replace: true
、name: 'home'
之类的选项以及任何用在router-link
的to
prop 或router.push
中的选项。 - next(error): (2.4.0+) 如果传入
next
的参数是一个Error
实例,则导航会被终止且该错误会被传递给router.onError()
注册过的回调。
//后置钩子 必须使用to和from 否则会报错!
router.afterEach((to, from) => {
// ...
console.log(to)
console.log(from)
})
关于守卫(钩子)的介绍可以参考下面的内容
keep-alive和router-view
keep-alive是一个vue内置的一个组件,可以使被包含的组件保留状态,或避免重新渲染。
router-view也是一个组件,如果直接包在keep-alive里面,所有路径匹配到的视图组件都会被缓存。
前面我们已经使用过router-view来渲染页面
router-viwe的生命周期
在每个组件中,都各自添加一个生命周期函数created和destoryed,添加打印消息。
export default {
name: 'Home',
created(){
console.log("home created");
},
destroyed(){
console.log("home destoryed");
}
}
使用router-view进行路由显示时发现,当页面被加载时会执行created方法,切换到其他页面的时候。会执行destoryed方法。说明router-viwe渲染的组件是每次都会重新初始化和销毁的。
在App.vue中使用此标签,可以解决上述重复渲染的问题,但是这也意味着页面不会重新被渲染。需要根据使用的情况而定。
<keep-alive>
<router-view></router-view>
</keep-alive>
keep-alive中存在子路由如何处理?
路由发生后,如果跳转的页面中存在子路由。如果子路由没有进行keep-alive处理,子组件仍然会重复的进行创建、销毁等操作。并且子组件的状态不会被保存,我们可以通过在子路由中使用keep-alive以及守卫来解决这个问题。
解决子路由重复渲染
解决子组件重复渲染问题,我们可以像上面一样,在子路由的router-viwe上加入keep-alive标签
解决子路由状态保留问题
首先介绍两个方法
activated:当组件处于活跃状态时执行
beforeRouterLeave:导航离开该组件的对应路由时调用
使用上面两个方法加自定义属性path来记录当前的path
完整代码:
<template>
<div class="home">
<h1>我是首页</h1>
<p>我是内容</p>
<router-link to="/home/news">新闻</router-link>
<router-link to="/home/message">消息</router-link>
<keep-alive>
<router-view/>
</keep-alive>
</div>
</template>
<script>
export default {
name: 'Home',
data(){
return{
path: '/home/news'
}
},
created(){
console.log("home created");
},
destroyed(){
console.log("home destoryed");
},
activated(){
this.$router.push(this.path);
},
beforeRouteLeave (to, from, next) {
console.log(this.$route.path);
this.path = this.$route.path;
next()
}
}
</script>
补充:activated、deactivated,只有keep-alive添加时才会生效!
keep-alive属性介绍
前面的案例中,介绍了组件重复渲染的解决方案,但是所有组件都不会被重新的渲染
存在需求如下:
存在五个组件,其中有一个组件需要实时渲染,其余四个不需要。
keep-alive中的属性
keep-alive中存在两个非常重要的属性:
-
include-字符串或正则表达式,只有匹配的组件会被缓存
-
exclude-字符串或正则表达式,任何匹配的组件都不会被渲染
使用这两个属性,我们就可以解决上面的需求
在App.vue的keep-alive加入如下属性,即可解决此问题
其中Profile、Home均为组件的name属性,多个之间使用逗号间隔
注意:不要再此属性内加上“空格”等字符
<keep-alive exclude="Profile,Home">
<router-view></router-view>
</keep-alive>
export default {
name: "Profile",
created() {
console.log("Profile created");
},
destroyed() {
console.log("Profile destoryed");
}
};
</script>