9.1安装
vue add router
9.2基础
9.2.1 起步
<nav>
<router-link to="/">首页</router-link>
<router-link to="/about">管理</router-link>
</nav>
<router-view></router-view>
<template>
<div>
<message ref="msgSuccess" class="success">
<!-- 命名为title插槽内容 -->
<template v-slot:title="slotProps">
<strong>{{slotProps.title}}</strong>
</template>
<!-- 默认插槽内容 -->
<template v-slot:default>新增课程成功!</template>
</message>
<message ref="msgWarning" class="warning">
<!-- 命名为title插槽内容 -->
<template v-slot:title>
<strong>警告</strong>
</template>
<!-- 默认插槽内容 -->
<template v-slot:default>请输入课程名称!</template>
</message>
<cart-add v-model="course" @add-course="addCourse"></cart-add>
<course-list :courses="courses"></course-list>
</div>
</template>
<script>
import CartAdd from "@/components/CartAdd.vue";
import CourseList from "@/components/CourseList.vue";
import Message from "@/components/Message.vue";
import { getCourses } from "@/api/course";
export default {
name: "app",
data() {
return {
course: "",
courses: []
};
},
components: {
CartAdd,
CourseList,
Message
},
async created() {
// 组件实例已创建,由于未挂载,dom不存在
const courses = await getCourses();
this.courses = courses;
},
methods: {
addCourse() {
if (this.course) {
// 添加course到数组
this.courses.push({ name: this.course, price: 8999 });
this.course = "";
this.$refs.msgSuccess.toggle();
}else{
this.$refs.msgWarning.toggle();
}
}
}
}
</script>
9.2.2 动态路由匹配
我们经常需要把某种模式匹配到的所有路由,全都映射到同个组件。例如,我们有一个 User 组件,对于所有 ID 各不相同的用户,都要使用这个组件来渲染。那么,我们可以在 vue-router 的路由路径中使用“动态路径参数”(dynamic segment) 来达到这个效果:
{path:'/user/:id',component:User}
<div>
<h2>detail page</h2>
<p>{{$route.params.name}}</p>
</div>
{
path:'/course/:name'
component:()=>import('../views/Detail.vue')
}
<router-link :to="`/course/${c.name}`">
{{c.nmae}}-{{c,price|currency('¥')}}
</router-link>
通配符
适合做404页面路由
{
path:'*',
compoent:()=>import('../views/404.vue')
}
9.2.3 嵌套路由
<router-link :to=`/about/${c.name}`">
{{c.name}}-{{c.price|currency('¥')}}
</router-link>
{
path:'/about',
name:about,
component:()=>import('../views/About.vue')
children:[{
path:':name'
component:()=>import('../views/Detail.vue')
}]
}
export default{
watch:{
$route:{
handler:()=>{
console.log("$route change")
},
immediate:true
}
}
}
9.2.4 编程导航
router.push(location,onComplete?,onAbort?)
//字符串
router.push('home')
//对象
router.push({path:'home'})
//命名的路由
router.push({name:'user',params:{userId:'123'}})
//带参数查询
router.push({path:'register'},query:{plan:'private'})
<div @click="selectedCourse = c;this.$router.push(`/about/${c.name}`)">
{{c.name}}-{{c.price|currency('¥')}}
</div>
9.2.5 命名路由
通过一个名称来标识一个路由显得更方便一些,特别在链接一个路由,或者是执行一些跳转的时候,你可以在创建Router实例的时候,在routes配置中给某个路由设置名称
const router = new VueRouter({
routes:[
{
path:'/user/:userId',
name:'user'
component:User
}
]
})
要链接到一个命名路由,可以给router-link的to属性传一个对象
<router-link
:to = "{
name :'user',
params:{userId:123}
}"
>
User
</router-link>
调用router.push()时
router.push({
name:'user',
params:{userId:123}
})
9.3进阶
9.3.1路由守卫
vue-router提供的导航守卫主要是通过跳转或取消的方式守卫导航。有多种机会植入路由导航过程中:全局的,单个路由独享的,或者组件级的。
全局守卫
router.beforeEach((to,from,next)=>{})
router.beforeEach((to,form,next)=>{
if(to.meta.auth){
if(window.isLogin){
next()
}else{
next('/login?redirect='+to.fullPath')
}
}else{
next()
}
})
{
path:'/about',
meta:{
auth:true
}
},{
path:'/login'
component:()=>import('../views/Login.vue')
}
<template>
<div>
<button @click="login" v-if="isLogin">登录</button>
<button @click="loginout" v-else>登出</button>
</div>
</template>
<script>
export default{
methods:{
login(){
window.isLogin = true
this.$router.push(this.$route.query.redirect)
},
logout(){
window.isLogin = false
}
},
computed:{
isLogin(){
return window.isLogin
}
}
}
</script>
路由独享的守卫
可以路由配置上直接定义beforeEnter守卫:
{
path:'/about',
name:'about',
beforeEnter(to,form,next){
if(to.meta.auth){
if(window.isLogin){
next()
}else{
next('/login?redirect='+to.fullPath)
}
}else{
next()
}
}
}
组件内守卫
可以在路由组件内直接定义以下路由导航守卫
beforeRouteEnter
beforeRouteUpdate
beforeRouteLeave
beforeRouteEnter(to,from,next){
if(window.isLogin){
next()
}else{
next("/login?redirect="+to.fullPath)
}
9.3.2 数据获取
路由激活时,获取数据的时机有两个:
路由导航前
//组件未渲染,通过给next传递访问组件实例
beforeRouteEnter(to,from,next){
getPost(to,params.id,post=>{
next(vm=>vm.setData(post))
})
}
//组件已渲染,可以访问this直接赋值
beforeRouteUpdate(to,from,next){
this.post = null
getPost(to.params.id,post=>{
this.setData(post)
next()
})
}
路由导航后
created(){
this.fetchData()
},
watch:{
'$route':'fecthData'
}
9.3.3 动态路由
通过router.addRoutes(routes)方式动态添加路由
router.beforeEach((to,form,next)=>{
if(window.isLogin){
if(to.path==='/login'){
next('/')
}else{
next()
}
}else{
if(to.path==='/login'){
next()
}else{
next('/login?redirect='+to.fullPath)
}
}
})
login(){
window.isLogin = true;
this.$router.addRoutes([
{
path:"/about"
}
])
const redirect = this.$route.query.redirect()||''
this.$router.push(redirect)
}
9.3.4 路由组件缓存
利用keepalive做组件缓存,保留组件状态,提高执行效率
<keep-alive include="about">
<router-view></router-view>
</keep-alive>
使用include或exclude时要给组件设置name
两个特别的生命周期:activated,deactivated
9.3.5 路由组件懒加载
路由组件的懒加载可以把不同路由对应的组件分割成不同的代码块,然后路由被访问的时候,才加载对应的组件
()=>import("../views/About")