vue-11vue router

Vue Router

Vue Router 是 Vue.js 官方的路由管理器。

安装

vue add router
npm install vue-route --save-dev

基础

起步

路由规划、配置,router/index.js

商品列表(home) - 商品管理(about)

路由出口、导航,App.vue

<nav>
    <router-link to="/">首页</router-link> 
    <router-link to="/about">管理</router-link> 
</nav> 
<router-view></router-view>

商品管理,About.vue

<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.show = true 
                this.$refs.msgSuccess.toggle(); 
            } else { 
                // 显示错误信息 
                // this.showWarn = true 
                this.$refs.msgWarning.toggle(); 
            } 
                        } 
        } 
    };
</script>

动态路由匹配

我们经常需要把某种模式匹配到的所有路由,全都映射到同个组件。例如,我们有一个 User 组件,对于所有 ID 各不相同的用户,都要使用这个组件来渲染。那么,我们可以在 vue-router 的路由路径中使用“动态路径参数”(dynamic segment) 来达到这个效果:

{ path: '/user/:id', component: User }

范例:查看课程详情,views/Detail.vue

<div> 
    <h2>detail page</h2> 
    <p>{{$route.params.name}} ...</p>
</div>

router/index.js

{
    path: '/course/:name', 
        component: () => import('../views/Detail.vue') 
}

列表中的导航,About.vue

<router-link :to="`/course/${c.name}`"> 
    {{ c.name }} - {{ c.price | currency('¥') }}
</router-link>

通配符

适合做404页面路由

{ 
    // 会匹配所有路径
    path: '*', component: () => import('../views/404.vue') 
}

/user/foo/profile /user/foo/posts +------------------+ +-----------------+ | User | | User | | +--------------+ | | +-------------+ | | | Profile | | +------------> | | Posts | | | | | | | | | | | +--------------+ | | +-------------+ | +------------------+ +-----------------+

范例:嵌套方式显示课程详情

<router-link :to="`/about/${c.name}`">
    {{ c.name }} - {{ c.price | currency('¥') }} 
</router-link> 
<router-view></router-view>

路由配置

{ 
    path: '/about',
    name: 'about', 
    component: () => import(/* webpackChunkName: "about" */ '../views/About.vue'), 
    children: [ 
        { 
            path: ':name', component: () => import('../views/Detail.vue') 
        }, 
    ] 
}

响应路由参数变化,Detail.vue

export default { 
    watch: { 
        $route: { 
            handler: () => { 
                console.log("$route change"); 
            },
            immediate: true 
        } 
    } 
};

编程导航

借助 router 的实例方法,可编写代码来实现编程式导航

router.push(location, onComplete?, onAbort?)

// 字符串 
router.push('home') 
// 对象 
router.push({ path: 'home' }) 
// 命名的路由 
router.push({ name: 'user', params: { userId: '123' }}) 
// 带查询参数,变成 /register?plan=private 
router.push({ path: 'register', query: { plan: 'private' }})

范例:修改为课程详情跳转为编程导航

<div @click="selectedCourse = c;$router.push(`/about/${c.name}`)"> 
    {{ c.name }} - {{ c.price | currency('¥') }}
</div>

命名路由

通过一个名称来标识一个路由显得更方便一些,特别是在链接一个路由,或者是执行一些跳转的时候。

你可以在创建 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 }})

进阶

路由守卫

vue-router 提供的导航守卫主要用来通过跳转或取消的方式守卫导航。有多种机会植入路由导航过程

中:全局的, 单个路由独享的, 或者组件级的。

全局守卫

router.beforeEach((to, from, next) => { 
    // ... 
    // to: Route: 即将要进入的目标 路由对象 
    // from: Route: 当前导航正要离开的路由 
    // next: Function: 一定要调用该方法来 resolve 这个钩子。
})

范例:守卫About.vue

router.beforeEach((to, from, 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="logout" 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, from, next) { 
        if (to.meta.auth) { 
            if (window.isLogin) { 
                next() 
            } else { 
                next('/login?redirect=' + to.fullPath) 
            } 
        } else { 
            next()
        } 
    } 
},

组件内守卫

可以在路由组件内直接定义以下路由导航守卫:

beforeRouteEnter

beforeRouteUpdate

beforeRouteLeave

// About.vue 
beforeRouteEnter(to, from, next) { 
    if (window.isLogin) { 
        next(); 
    } else { 
        next("/login?redirect=" + to.fullPath); 
    } 
}

数据获取

路由激活时,获取数据的时机有两个:

路由导航前


// 组件未渲染,通过给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': 'fetchData' 
    }

动态路由

通过router.addRoutes(routes)方式动态添加路由

// 全局守卫修改为:要求用户必须登录,否则只能去登录页 
router.beforeEach((to, from, next) => { 
    if (window.isLogin) { 
        if (to.path === '/login') { 
            next('/') 
        } else { 
            next() 
        } 
    } 
    else { 
        if (to.path === '/login') { 
            next() 
        } else { 
            next('/login?redirect=' + to.fullPath)
        } 
    } 
})



// Login.vue用户登录成功后动态添加/about 
login() { 
    window.isLogin = true; 
    this.$router.addRoutes([ 
        { 
            path: "/about", 
            //... 
        } 
    ]); 
    
    const redirect = this.$route.query.redirect || "/"; 
    this.$router.push(redirect); 
}

路由组件缓存

利用keepalive做组件缓存,保留组件状态,提高执行效率

范例:缓存about组件

<keep-alive include="about"> 
    <router-view></router-view> 
</keep-alive>

使用include或exclude时要给组件设置name

两个特别的生命周期:activated、deactivated

路由懒加载

路由组件的懒加载能把不同路由对应的组件分割成不同的代码块,然后当路由被访问的时候才加载对应

组件,这样就更加高效了。

() => import("../views/About.vue")

把组件按组分块

() => import(/* webpackChunkName: "group-about" */ "../views/About.vue")
posted @ 2021-11-07 10:14  zhangzchun  阅读(63)  评论(0)    收藏  举报