VueRouter路由(Vue全家桶之一详解)

Vue路由初识

  • 两种模式: hash 模式或HTML5 历史模式
  • vue-router 默认 hash 模式 —— 使用 URL 的 hash 来模拟一个完整的 URL,于是当 URL 改变时,页面不会重新加载。
  • 如果不想要很丑的 hash,我们可以用路由的 history 模式,这种模式充分利用 history.pushState API 来完成 URL 跳转而无须重新加载页面。

Vuehash路由模拟

VueRouter路由(Vue全家桶之一详解)_第1张图片

Vue-histroy路由模拟

VueRouter路由(Vue全家桶之一详解)_第2张图片

vue-router 使用初识

路由:一个路径对应一个组件
1.创建组件
2.配置路由映射表
3.注册路由映射表
4. 把路由挂载到根实例上;

<body>
    <div id="app">
        <!-- to : 跳转的路由  tag:指定router-link渲染标签 -->
        <!-- 使用 router-link 组件来导航. -->
        <!-- 通过传入 `to` 属性指定链接.  tag:指令router-link渲染成啥标签-->
        <!-- <router-link> 默认会被渲染成一个 `` 标签 -->
        <router-link to="/allhome" tag="button">首页</router-link>
        <router-link to="/allperson" tag="button">个人中心</router-link>
        
        <!-- 用于显示路由对应组件的地方 -->
        <!-- 根据路由显示对应的组件 -->
        <!-- 路由出口 -->
        <!-- 路由匹配到的组件将渲染在这里 -->
        <router-view></router-view>
    </div>
    <script src="../node_modules/vue/dist/vue.js"></script>
    <script src="../node_modules/vue-router/dist/vue-router.js"></script>
    <script>
        // 路由:一个路径对应一个组件
        // 1.创建组件    
        // 2.配置路由映射表
        // 3.注册路由映射表
        // 4. 把路由挂载到根实例上; 
        let home = {
            data() {
                return {
                    con: '首页'
                }
            },
            template: "
{{con}}
"
} let person = { data() { return { con: '个人中心页' } }, template: "
{{con}}
"
} // 配置路由映射表: 是路由和组件的配对情况 let routes = [{ path: "/allhome", component: home }, { path: "/allperson", component: person }]; // 注册路由映射表 let router = new VueRouter({ routes: routes // routes 属性名不可以改 }) // 将路由挂载到根实例上 let vm = new Vue({ el: "#app", data: { }, components: { home, person }, router }) </script> </body>

VueRouter中的方法

引入时:vue-router一定要放vue.js的后面
当切换组件时,组件会销毁;

当每一个被路由渲染出来的组件上有一个$router属性,在这个属性的原型上有一些操作路由的方法
1.push : 直接跳转到当前路径对应的路由上 push(路径)
2.back : 回退到上一次的路由上
3.go(number):可以往前可以往后回退后前进几个
原生:
window.history
window.history.go(-1)

<body>
    <div id="app">
        <!-- to属性 -->
        <router-link to="/home" tag="button">首页</router-link>
        <router-link to="/list" tag="button">列表页</router-link>
        <!-- 展示对应的组件,当组件切换时,组件的DOM元素删除 -->
        <router-view></router-view>

    </div>
    <script src="../node_modules/vue/dist/vue.js"></script>
    <script src="../node_modules/vue-router/dist/vue-router.js"></script>
    <script>
        //window.history  
        //window.history.go(-1) 

        // vue-router一定要放vue.js的后面
        // 当切换组件时,组件会销毁;
        // 当每一个被路由渲染出来的组件上有一个$router属性,在这个属性的原型上有一些操作路由的方法
        // 1.push : 直接跳转到当前路径对应的路由上 push(路径)
        // 2.back : 回退到上一次的路由上
        // 3.go(number):可以往前可以往后回退后前进几个
        let home = {
            data() {
                return {}
            },
            created() {
                // 显示组件时,需要再次创建组件实例;调用钩子函数
                console.log("创建首页");
            },
            methods: {
                goList() {
                    console.log(this);
                    this.$router.push("/list")

                }
            },
            template: "
首页内容
"
, beforeDestroy() { console.log("销毁首页"); }, }; let list = { data() { return {} }, methods: { goBack() { console.log(this); // this.$router.back("/home") this.$router.go(-1) } }, template: "
列表页
"
}; // 路由映射表 let routes = [{ path: "/home", component: home }, { path: "/list", component: list }]; // 注册路由映射表 let router = new VueRouter({ routes }); // 挂载到根实例上 let vm = new Vue({ el: "#app", router }); </script> </body>

路由的嵌套

在组件路由配置是,对象中有children属性,属性值是一个数组,里面配置了子路由,子路由中不需要加父路由路径地址,同时也不需要加"/",当子路由进行匹配式,会自动加上父路由和/到子路由的前面;
子路由path后面也可以从一级路由到耳机路由
二级路由不能直接配置到routes,应该找到它对应的以及路由,配置到其children属性上;

下面例子:detail 和login都是list组件的子路由组件

<body>
    <div id="app">
        <router-link to="/home" tag="button" class="a">首页</router-link>
        <router-link to="/list" tag="button" class="b">列表页</router-link>
        <router-view></router-view>
    </div>
    <template id="list">
        <div>
            列表页
            <router-link to="/list/detail">详情页</router-link>
            <router-link to="/list/login">登录页</router-link>
            <router-view></router-view>
        </div>
    </template>  
    <script src="../node_modules/vue/dist/vue.js"></script>
    <script src="../node_modules/vue-router/dist/vue-router.js"></script>
    <script>
        let home={
            template:"
首页
"
} let list={ template:"#list" }; let detail = { template:"
详情页
"
} let login = { template:"
登录注册页
"
}; // detail 和login都是list组件的子路由组件 // 在组件路由配置是,对象中有children属性,属性值是一个数组,里面配置了子路由, 路由中不需要加父路由路径地址,同时也不需要加"/",当子路由进行匹配式,会自动加上父路由和/到子路由的前面; // 二级路由不能直接配置到routes,应该找到它对应的以及路由,配置到其children属性上; let routes =[ {path:"/home",component:home}, {path:"/list",component:list,children:[ {path:"detail",component:detail}, {path:"login",component:login} ]} ]; let router = new VueRouter({ routes }) let vm = new Vue({ el:"#app", data:{ }, router }) </script> </body>

命名路由

将to改成动态属性 :to={name:组件的name名称}
通过名字取匹配路由

<body>
    <div id="app">
        <!-- 将to改成动态属性 :to={name:组件的name名称} -->
        <router-link :to="{name:'first'}">首页</router-link>
        <router-view></router-view>
    </div>
    <script src="../node_modules/vue/dist/vue.js"></script>
    <script src="../node_modules/vue-router/dist/vue-router.js"></script>
    <script>
        // 通过名字取匹配路由
        let home={
            template:"
首页
"
} let routes =[ {path:"/home",component:home,name:"first"} ]; let router = new VueRouter({ routes }) let vm = new Vue({ el:"#app", data:{ }, router }) </script> </body>

动态路由

动态路由:路由传参;路径后面是一个 :变量 这就是动态路由,也可以叫路由动态传参;会把id以属性方式放到$route的params属性上,属性值就是路由实际的路径值

1.代码量少
2. 由于动态路由渲染的是同一个home组件,所以home组件不再销毁,当然也不再创建,复用了之前的组件,性能高;但是生命周期的前四个钩子函数也不再执行;

VueRouter路由(Vue全家桶之一详解)_第3张图片

<body>
    <div id="app">
        <router-link to="/home/1">第一本</router-link>
        <router-link to="/home/2">第二本</router-link>
        <router-link to="/home/3">第三本</router-link>
        <router-view></router-view>
    </div>
    <script src="../node_modules/vue/dist/vue.js"></script>
    <script src="../node_modules/vue-router/dist/vue-router.js"></script>
    <script>
        // $router:push go  back  forward
        // $route: 

        // 路由的动态传参:
        // 路由传参有哪些方式;

        let home = {
            // created(){
            //     // 
            //     console.log(this.$route);
            // },
            // watch:{
            //     '$route'(to,from){
            //        console.log(to);// to: 要到达的组件的$route
            //        console.log(from);// from :上一个$route
            //     }
            // },
            // 路由守卫
            // 在路由更新之前会默认调用该钩子函数
            beforeRouteUpdate(to, from, next) {
                console.log(to); // 即将进入的目标路由的对象信息
                console.log(from); // 即将离开路由的对象信息
                console.log(next); // 函数
                // 1. next 函数
                // next(): 会立即进入到目标路由
                // next(false):中断当前的导航;不再去访问下一个路由
                // if(to.params.id==3){
                //     next({path:"/home/1"});
                //     return;
                // }
                // 权限校验
                next();
            },
            template: "
这是我喜欢的第{{$route.params.id}}本书
"
} // 动态路由:路由传参;路径后面是一个:变量;这就是动态路由,也可以叫路由动态传参; 会把id以属性方式放到$route的params属性上,属性值就是路由实际的路径值 // 1.代码量少 // 2. 由于动态路由渲染的是同一个home组件,所以home组件不再销毁,当然也不再创建, 复用了之前的组件,性能高;但是生命周期的钩子函数也不再执行; let routes = [{ path: "/home/:id", component: home }]; let router = new VueRouter({ routes }) let vm = new Vue({ el: "#app", data: {}, router }) </script> </body>

路由传参

  • 路由传参有哪些方式;
  1. id 动态路由传参;路径后面是一个:变量;这就是动态路由,也可以叫路由动态传参;会把id以属性方式放到$route的params属性上,属性值就是路由实际的路径值
  2. query传参 path
  3. params传参 name 命名路由

VueRouter路由(Vue全家桶之一详解)_第4张图片

<body>
    <div id="app">
        <router-link :to="{name:'first'}">首页</router-link>
        <router-link :to="{name:'second'}">列表页</router-link>
        <router-view></router-view>
    </div>
    <script src="../node_modules/vue/dist/vue.js"></script>
    <script src="../node_modules/vue-router/dist/vue-router.js"></script>
    <script>
        // 通过名字取匹配路由 
        // 1. id 动态路由传参;路径后面是一个:变量;这就是动态路由,也可以叫路由动态传参;
      会把id以属性方式放到$route的params属性上,属性值就是路由实际的路径值 
        // 2.query传参 path
        // 3.params传参 name
        let home = {
            methods: {
                goList() {
                    // this.$router.push({
                    //     path: "/list",
                    //     query: {
                    //         id: 100
                    //     }
                    // }),
                    this.$router.push({
                        name: "second",
                        params: {
                            id: 500
                        }
                    })
                }
            },
            template: "
首页
"
} let list = { created() { // console.log(this.$route) // let id = this.$route.query.id; // console.log(id); console.log(this.$route); let id = this.$route.params.id; console.log(id); }, template: "
列表页
"
} let routes = [{ path: "/home", component: home, name: "first" }, { path: "/list", component: list, name: "second" } ]; let router = new VueRouter({ routes }) let vm = new Vue({ el: "#app", data: { }, router }) </script> </body>

命名视图

没有name属性,会显示属性为default的组件
let routes =[
{path:"/home",components:{
default:home,// home对应没有name属性的router-view
// 这个对象属性名和router-view的name属性值对应
b:foo,
c:bar
},name:“first”}
];

<body>
    <div id="app">
        <!-- 将to改成动态属性 :to={name:组件的name名称} -->
        <router-link :to="{name:'first'}">首页</router-link>
        <!-- 没有name属性,会显示属性为default的组件 -->
        
        <router-view></router-view>
        <router-view name="b"></router-view>
        <router-view name="c"></router-view>
    </div>
    <script src="../node_modules/vue/dist/vue.js"></script>
    <script src="../node_modules/vue-router/dist/vue-router.js"></script>
    <script>
        let home={
            template:"
首页
"
} let foo = { template:"
foo
"
} let bar={ template:"
bar
"
} let routes =[ {path:"/home",components:{ default:home,// home对应没有name属性的router-view // 这个对象属性名和router-view的name属性值对应 b:foo, c:bar },name:"first"} ]; let router = new VueRouter({ routes }) let vm = new Vue({ el:"#app", data:{ }, router }) </script> </body>

redirect: 路由地址重定向

redirect: 重定向

  1. routes:[{path:"/a",redirect:"/home"}]
  2. routes:[{path:"/a",redirect:{name:“路由名字”}}]
  • 最后匹配不到重定向到首页或者404页面
    VueRouter路由(Vue全家桶之一详解)_第5张图片
<body>
    <div id="app">
        <router-link to="/home" tag="button">首页</router-link>
        <router-link to="/list" tag="button">列表页</router-link>
        <router-view></router-view>
    </div>
    <script src="../node_modules/vue/dist/vue.js"></script>
    <script src="../node_modules/vue-router/dist/vue-router.js"></script>
    <script>
        
        // 路由{path:"",component:"",children:[{path:"",component:""},
{path:"",component:""}]}
        let home = {
            template: "
首页内容
"
}; let list = { template: "
列表页内容
"
}; let found = { template: "
页面404
"
}; // redirect: 重定向 // 1. routes:[{path:"/a",redirect:"/home"}] // 2. routes:[{path:"/a",redirect:{name:"路由名字"}}] let routes = [{ path: "/", // component: home // 一个地址一个组件,如果想用就重定向, }, { path: "/home", component: home }, { path: "/list", component: list }, { path: "/a", component: found }, { path: "*", redirect: "/home" }]; let router = new VueRouter({ routes }); let vm = new Vue({ el: "#app", router }) </script> </body>

路由的守卫(七个钩子函数)

导航守卫:当切换导航时,会默认调用一些钩子函数,那么这些钩子函数就是导航的守卫;可以在进入这个导航或者离开这个导航时,在钩子函数中做一些事情
守卫: 7个
全局守卫: beforeEach afterEach beforeResolve
路由独享守卫: beforeEnter
组件内部守卫: beforeRouteEnter beforeRouteUpdate beforeRouteLeave

<body>
    <div id="app">
        <router-link to="/home/1" tag="button">第一本</router-link>
        <router-link to="/home/2" tag="button">第二本</router-link>
        <router-link to="/home/3" tag="button">第三本</router-link>
        <router-link to="/list" tag="button">列表</router-link>
        <router-view></router-view>
    </div>

    <!--  IMPORT JS -->
    <script src="../node_modules/vue/dist/vue.js"></script>
    <script src="../node_modules/vue-router/dist/vue-router.js"></script>
    <script>
        //切换到另一个组件,组件时会销毁的;
        // 导航守卫:当切换导航时,会默认调用一些钩子函数,那么这些钩子函数就是导航的守卫;
      可以在进入这个导航或者离开这个导航时,在钩子函数中做一些事情
        // 生命周期 11个
        // 守卫: 7个
        // 全局守卫: beforeEach  afterEach  beforeResolve
        // 路由独享守卫: beforeEnter 
        // 组件内部守卫: beforeRouteEnter beforeRouteUpdate beforeRouteLeave

        let home = {
            beforeDestroy() {
                // console.log("销毁");
            },
            template: "
这是第{{$route.params.id}}本
"
, beforeRouteEnter(to, from, next) { // 这个钩子函数执行时进入组件实例之前,此时组件实例还没有创建; // console.log(this); //this->window console.log("组件内的守卫-home-beforeRouteEnter"); next(vm => { // 最后执行 // 当next执行传入回调函数,回调函数不能立即执行,等到组件实例创建好之后, 才会触发这个回调函数;其中vm就是组件实例 //console.log(vm); }) }, beforeRouteUpdate(to, from, next) { // 当复用这个组件并且更新了组件时,这个函数才会被调用 console.log("组件内的守卫-home-beforeRouteUpdate"); next(); }, beforeRouteLeave(to, from, next) { // 当离开list这个组件时,会调用这个钩子函数 console.log("组件内的守卫-home-beforeRouteLeave"); next(); } }; // 当第一次进入到list组件时,只触发了beforeRouteEnter let list = { template: "
列表内容
"
, // 组件内的守卫 beforeRouteEnter(to, from, next) { // 这个钩子函数执行时进入组件实例之前,此时组件实例还没有创建; // console.log(this); //this->window console.log("组件内的守卫-list-beforeRouteEnter"); next(vm => { // 当next执行传入回调函数,回调函数不能立即执行,会等到组件实例创建好之后, 才会触发这个回调函数,其中vm就是组件实例 console.log(vm); }) }, // beforeRouteUpdate(to,from,next){ // 当复用这个组件并且更新了组件时,这个函数才会被调用; // this--> 当前的组件实例 // console.log("组件内的守卫-list-beforeRouteUpdate"); // next() // }, beforeRouteLeave(to, from, next) { // 当离开list这个组件时,会调用这个钩子函数 console.log("组件内的守卫-list-beforeRouteLeave"); next(); } }; let routes = [{ path: "/home/:id", component: home, beforeEnter: (to, from, next) => { // 路由独享的守卫 console.log("home-路由独享的守卫"); next() } }, { path: "/list", component: list, beforeEnter: (to, from, next) => { // 路由独享的守卫 console.log("list-路由独享的守卫"); next() } }]; let router = new VueRouter({ routes }) // 全局的前置钩子函数;只要切换组件,就会执行 router.beforeEach((t0, form, next) => { console.log("beforeEach-全局前置的钩子函数"); // console.log(to);// 到哪去 // console.log(from);// 从哪来 // console.log(1); // 在这个钩子函数中获取到用户的信息,进行权限的校验,如果不符合要求, 那么next不需要运行;或者直接跳转到首页或 403页面 next(); // 只有执行了next,才会往下继续跳转路由; }) // 全局解析守卫 router.beforeResolve((t0, form, next) => { console.log("beforeResolve-全局解析守卫"); // console.log(to);// 到哪去 // console.log(from);// 从哪来 // console.log(2); next(); // 只有执行了next,才会往下继续跳转路由; }) // 全局后置的钩子函数 router.afterEach((t0, form) => { // 路由切换成功以后执行的钩子函数 console.log("afterEach-全局后置的钩子函数"); // console.log(to); // console.log(3); }) // 用户 /list 管理员 : /list /edit /home // 切换路由时:路由守卫执行顺序beforeEach==>beforeEnter==>beforeRouteUpdate==> beforeResolve==>afterEach //当进入组件时,先触发全局的前置钩子,然后触发进入组件的路由独享守卫, 然后触发组件内部的beforeRouteEnter,最后触发全局的beforeResolve和全局后置钩子函数 let vm = new Vue({ el: '#app', router }); // 从第三本到列表 // 组件内的守卫-home-beforeRouteLeave beforeEach-全局前置的钩子函数 list-路由独享的守卫 组件内的守卫-list-beforeRouteEnter beforeResolve-全局解析守卫 afterEach-全局后置的钩子函数 调用beforeRouteEnter守卫中传给next的回调函数 // 从列表到第三本 // 组件内的守卫-list-beforeRouteLeave beforeEach-全局前置的钩子函数 home-路由独享的守卫 组件内的守卫-home-beforeRouteEnter beforeResolve-全局解析守卫 afterEach-全局后置的钩子函数 调用beforeRouteEnter守卫中传给next的回调函数 // 从第三本到第二本 // beforeEach-全局前置的钩子函数 组件内的守卫-list-beforeUpdata beforeResolve-全局解析守卫 afterEach-全局后置的钩子函数 </script> </body>

VueRouter路由(Vue全家桶之一详解)_第6张图片

VueRouter核心源码

class VueRouter{
    constructor(options){
        const {routes}=options;
        // 监听当前页面的hash值的切换
        // 当第一次解析页面时,会有一个默认的hash值
        /// 循环遍历routes,把path和component重新放入一个新的对象中
        // {"/home/:id":home}
        this.routeMap = routes.reduce((prev,next)=>{
            prev[next.path]=next.component;
            return prev;
        },{});
        // 
        // this ==> VueRouter的实例,也是每一个组件上的_router
        Vue.util.defineReactive(this.route={},'path',"/");
        window.addEventListener("load",()=>{
            // 如果没有hash值,那么给其赋默认值/;如果本来就有hash,什么也不做;
            location.hash?null:location.hash="/";
        })
        window.addEventListener("hashchange",()=>{
            // 当页面hash值发生改变以后,会触发这个方法;1.a标签  2.手动
            // 获取当当前页面的hash值,获取到#后面的字符串;
            let path = location.hash.slice(1);
            this.route.path = path;
        })
    }
}
//在Vuex注入了$store,在路由注入_router
VueRouter.install=function(_Vue){

    _Vue.mixin({
        // 给每一个组件新增一个_router的属性,这个属性的属性值是VueRouter的实例
        beforeCreate(){
            // this==> 每一个组件实例
            if(this.$options&&this.$options.router){
                // 给每一个组件实例新增_router属性,属性值就是VueRouter的实例; 
              这是给Vm这个Vue实例新增
                this._router=this.$options.router;
            }else{
                // 给vm的组件的实例新增
                this._router=this.$parent && this.$parent._router;
            }
            // 给每一个实例添加$route属性,
            Object.defineProperty(this,"$route",{
                value:{
                    route:this._router.route// ? 这个route
                }
            });

            // 注册两个内置组件
            // router-link  router-view
            // 注册全局组件
            <router-link to="/home"></router-link>
            let child = {}
            Vue.component("router-link",{ 
                props:{
                    to:String
                },
                // template:"
",
render(createElement){// h是一个createdElement,这个方法可以直接接受一个组件; createElement 用来创建虚拟的DOM //return createElement("a",{},首页) // render : 渲染函数 // render: 将虚拟DOM可以转成真实的DOM;这个函数返回什么, 那么最终router-link就渲染成什么 // this==> 当前的组件实例 // return + 组件;可以把组件渲染成一个真实的DOM; // return h(child); // return // $slots return <a href={`#${this.to}`}>this.$slots.default</a> } }); // router-view : 根据页面的hash值显示对应的组件 Vue.component("router-view",{ render(createdElement){ // 这个传递一个动态的组件名字 return createElement(this._router.routeMap[this._router.route.path]) } }) } }) }; let router=new VueRouter({ routes:[] }) let vm = new Vue({ router, render(){ } }) export default VueRouter;

你可能感兴趣的:(Vue)