1、路由原理

1、需要路由的原因:
传统开发方式url改变后,立刻发生请求,响应整个页面,有可能资源过多,传统开发会让页面出现白屏的现象,或者加载慢!

2、路由原理:
SPA(Single Page Application)单页面应用
锚点值改变,不立刻发送请求,而是在合适的时机发Ajax请求,局部改变页面数据(改变页面的局部组件
3、优点:页面不立刻跳转用户体验好
4、模拟路由的实现

1.定义一个a标签,
2.用window.hashchange获取a标签的href属性值
3.根据属性内容值,将不同数据插入到一个入口中

2、vue-router的使用

1、引包

环境准备:引资源包,
方式一:通过npm 下载到本地,
npm install vue-router --save
方式二:bootcdn引入
1、先引vue.js
2、再引入vue-router

2、让vue使用vueRouter对象

Vue.use(vueRouter);
注意:
如果Vue是局部作用域的对象,那么必须上一步,否则可以省略

3、创建路由对象

这里创建的路由对象
var router = new VueRouter({
//4、配路由对象
routes:[
//路由规则

{
    path:"/login",
    components:Login
},
 {
    path:"/register",
    name:"register",
    components:Register
}

]
})
重定向

{
    path:'/',
    redirect:'/home'
}

router抛出两个全局组件:

<router-link>

默认渲染a标签,to对应路由规则中的path
写法1:
<router-link to="/login">
写法2:
在路由规则中,也可以给路由信息加上name属性,通过命名路由方式跳转,to要用绑定属性的方式:to
<router-link :to="{name:'register'}">

<router-view>

匹配的路由的出口。(通过路由的方式显示就不要在去挂载相应的组件了)

4、配置路由对象

在组件的template中通过使用router的两个全局组件来配置

     var App = {
            template: `<div>
                <router-link :to="{name:'login'}">登录</router-link>
                <router-link to='/register'>注册</router-link>

                <router-view></router-view>
                </div>
                `
        }

5、关联路由对象到Vue实例化对象中

      new Vue({
            el: "#app",
            template: `
           <App/>
            `,
            router,
            components: {
                App
            }
        })

若没挂载到Vue实例化对象中,会报错
报错内容:

vue.js:1897 TypeError: Cannot read property 'matched' of undefined
    at render (vue-router.min.js:6)
    at createFunctionalComponent (vue.js:3065)
    at createComponent (vue.js:3238)
    at _createElement (vue.js:3428)
    at createElement (vue.js:3360)
    at Proxy.vm._c (vue.js:3497)
    at Proxy.eval (eval at createFunction (vue.js:11649), <anonymous>:3:310)
    at VueComponent.Vue._render (vue.js:3551)
    at VueComponent.updateComponent (vue.js:4067)
    at Watcher.get (vue.js:4478)
    

路由抛出两个全局对象:

路由对象:$router
路由信息:$route

3、路由参数

1、动态路由

路由范式:
1、Xxx.html/user/1
2、Xxx.html/user?userid=1
动态路由参数:
routes:[
{
path:user/:id,
name:"userP"
component:UserP
},{
path:user/,
name:"userQ"
component:userQ
},
]
注意params里的值要和path的冒号后的值对应

:to={'name':"userP",params:{id:1}}
:to={'name':"userQ",query:{userid:2}}

当组件内容不同,整体样式一样的时候可以使用动态路由,结合导航守卫,根据动态传进来的参数修改页面数据:
例子详见嵌套路由中的动态模块

2、嵌套路由

在子组件中使用<route-link>和<route-view>
路由匹配规则中通过children匹配
动态路由通过冒号匹配

routes:[{
path:"/home",
component:Home,
children:[{
    path:"song",
    component:Song,
},{
 path:"/song/:comDec",
 component:comDec,
}
]
}
]

image
具体详见官网
模仿掘金做一个动态路由和嵌套路由
第一步先引vue包,和VueRouter包
第二步加到body中

 <div id='app'>
    </div> 
    <script>
          var Comdec = {
              data(){
                  return{
                      msg:"后端"
                  }
              },
            template: `
            <div>{{msg}}</div>
            `,
            watch:{
                // 导航守卫
                $route(to,from){
                    this.msg = to.params.id
                }
            }
        }

           var Home = {
            template: `
            <div>
            <router-link :to = "{name:'im',params:{id:'backend'}}" >后端</router-link>
            <router-link :to = "{name:'im',params:{id:'frontend'}}" >前端</router-link>
            <router-link :to = "{name:'im',params:{id:'android'}}" >安卓</router-link>
            <router-view></router-view>  
                </div>
            `
        }
        var Pins = {
            template: `
            <div>我是沸点</div>
            `
        }
        var router = new VueRouter({
           
            routes: [
                {
                    path:'/',
                    redirect:Home
                },
                {
                    path: "/home",
                    name: "home",
                    component: Home,
                    children:[
                        {
                            path:'im/:id',
                            name:'im',
                            component:Comdec
                        }
                    ]
                },
                {
                    path: "/pins",
                    name: "pins",
                    component: Pins
                },
                {
                    path: '/',
                    redirect: '/userq'

                }

            ]
        });
        
        var App = {
            template: `<div>
                <router-link :to="{name:'home'}">首页</router-link>
                <router-link :to = "{name:'pins'}">沸点</router-link>

                <router-view></router-view>
                </div>
                `,
                
         
        }
        new Vue({
            el:"#app",
            data(){
                return{
                    msg:"这是测试"
                }
            },
            template:`
           <App/>
            `,
            router,
            components:{
                App
            }

        })

动态路由的目的:

组件相同,内容不同,则可以通过动态路由的方式修改组件数据,不用频繁创建和销毁组件,性能高

动态路由和嵌套路由的应用

动态路由应用:通常用在公共组件中
嵌套路由应用:组件结构不一样

4、导航

1、导航守卫

监听动态路由变化

    watch:{
    '$route' (to,from){
    console.log(to)
    }
    }

2、全局路由守卫

meta和全局路由的渲染前配置
给未来路由做权限控制,全局路由守卫来做参照条件
1、路由配置信息中给需要权限控制的对象加meta

meta:{
auth:true
}

2、在进入路由前加判断

router.beforeEach((to,from,next)=>{
if(to.meta.auth){
//处理
}else{
//直接放行
next()
}
})

关于next的概念:
不传参数代表直接跳转到当前路由,next()
传参数跳转到对应的路由next({path:'/login'})
例子:

  <div id='app'>
    </div>
    <script>
        var Comdec = {
            data() {
                return {
                    msg: "后端"
                }
            },
            template: `
            <div>{{msg}}</div>
            `,
            watch: {
                '$route'(to, from) {
                    this.msg = to.params.id
                }
            }
        }

        var Blog = {
            template: `
            <div>
            我是博客
                </div>
            `
        }
        var Pins = {
            template: `
            <div>点我发布</div>
            `
        }  
        var Login = {
            data() {
                return {
                    name: "zhangsan",
                    pwd: 124
                }

            },
            template: `
            <div>
                <input type = text v-model = "name"/>
                <input type = text v-model = "pwd"/>
                <input type = button value = "登录"  @click = 'login' />
            </div>
            `
            ,
            methods: {
                login() {
                    window.localStorage.setItem("item", { name: this.name, pwd: this.pwd });
                    let log = window.localStorage.getItem("item")
                    console.log(log);
                    this.$router.push('/pins');
                }
            }
        }
        Vue.use(VueRouter)
        var router = new VueRouter({

            routes: [
               
                {
                    path: "/blog",
                    name: "blog",
                    component: Blog,

                },
                {
                    path: "/pins",
                    name: "pins",
                    meta: {
                        auth: true
                    },
                    component: Pins
                },
                {
                    path: "/login",
                    name: "login",
                    component: Login
                }, {
                    path:'/',
                    redirect:Blog
                },


            ]
        });
        router.beforeEach((to, from, next) => {
            if (to.meta.auth) {
                if(window.localStorage.getItem("item")){
                    console.log(this)
                    
                    next()
                   
                   
                }else{
                    next({ path: '/login' })
                }
                
                // this.$router.push('/login');
               
                // router.push('/login');
            } else {
                next()
            }
        })
        var App = {
            template: `<div>
                <router-link :to="{name:'blog'}">博客</router-link>
                <router-link :to = "{name:'pins'}">发布</router-link>
                <router-link :to = "{name:'login'}" >登录</router-link>
                <a href="javascript:void(0)" @click = "out">退出</a>
                <router-view></router-view>
                </div>
                `,
            methods: {
                out() {
                    window.localStorage.removeItem("item");
                    if(this.$router.currentRoute.name!=="login"){
                        this.$router.push('/login');
                    }
                    
                }
            }
        }
        new Vue({
            el: "#app",
            template: `
           <App/>
            `,
            router,
            components: {
                App
            }
        })

    </script>

3、路由的两种导航

1、声明式导航

<router-link>

2、编程式导航
参数可以是name属性,也可以path属性

this.$router.push({name:'blog'});
this.$router.push('/login');

4、 路由跳转的bug

image

当login跳转到login的时候路由相同会报错,在做路由跳转的时候可以先判断下当前路由是否等于要跳转的路由

  if(this.$router.currentRoute.name!=="login"){
                        this.$router.push('/login');
                    }

错误内容

vue-router.min.js:6 Uncaught (in promise) NavigationDuplicated: Avoided redundant navigation to current location: "/login".
    at xt (http://127.0.0.1:5500/code/node_modules/vue-router/dist/vue-router.min.js:6:15948)
    at e.St.confirmTransition (http://127.0.0.1:5500/code/node_modules/vue-router/dist/vue-router.min.js:6:18842)
    at e.St.transitionTo (http://127.0.0.1:5500/code/node_modules/vue-router/dist/vue-router.min.js:6:18210)
    at e.push (http://127.0.0.1:5500/code/node_modules/vue-router/dist/vue-router.min.js:6:22535)
    at http://127.0.0.1:5500/code/node_modules/vue-router/dist/vue-router.min.js:6:25920
    at new Promise (<anonymous>)
    at Ft.push (http://127.0.0.1:5500/code/node_modules/vue-router/dist/vue-router.min.js:6:25884)
    at VueComponent.out (http://127.0.0.1:5500/code/DAY3-%E6%8F%92%E4%BB%B6vueRouter/5%E3%80%81%E5%85%A8%E5%B1%80%E8%B7%AF%E7%94%B1%E5%AE%88%E5%8D%AB.html:144:38)
    at invokeWithErrorHandling (http://127.0.0.1:5500/code/node_modules/vue/dist/vue.js:1863:28)
    at HTMLAnchorElement.invoker (http://127.0.0.1:5500/code/node_modules/vue/dist/vue.js:2188:16)

5、组件中的导航守卫

const Foo = {
  template: `...`,
  beforeRouteEnter (to, from, next) {
    // 在渲染该组件的对应路由被 confirm 前调用
    // 不!能!获取组件实例 `this`
    // 因为当守卫执行前,组件实例还没被创建
  },
  beforeRouteUpdate (to, from, next) {
    // 在当前路由改变,但是该组件被复用时调用
    // 举例来说,对于一个带有动态参数的路径 /foo/:id,在 /foo/1 和 /foo/2 之间跳转的时候,
    // 由于会渲染同样的 Foo 组件,因此组件实例会被复用。而这个钩子就会在这个情况下被调用。
    // 可以访问组件实例 `this`
  },
  beforeRouteLeave (to, from, next) {
    // 导航离开该组件的对应路由时调用
    // 可以访问组件实例 `this`
  }
}

注意1:beforeRouteEnter
不!能!获取组件实例 this,可以通过next函数的回调函数调用:

beforeRouteEnter (to, from, next) {
  next(vm => {
    // 通过 `vm` 访问组件实例
  })
}

注意2:动态路由。组件相同数据不同,这个函数只会进来一次,切换路由的时候,到beforeRouteUpdate中
注意3:如果写了 beforeRouteUpdatebeforeRouteLeave函数,需要调用一下next()方法,否则路由进不去
注意4:路由离开前。要离开这个组件才会进入这个函数
学习小demo。实现组件内动态路由的跳转,和离开,实现清空定时器和保存数据后离开的小功能

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script src="../node_modules/vue/dist/vue.min.js"></script>
    <script src="../node_modules/vue-router/dist/vue-router.min.js"></script>
    <script src="../DAY4-axios/node_modules/axios/dist/axios.js"></script>
</head>
<body>
    <div id="app"></div>
    <script>
    // https://www.easy-mock.com/mock/5d65ef060d2acc6a1fb9a126/example/restful/:id
    Vue.prototype.$axios = axios;
  
        var User = {
            template:`
            <div>
                <h2>{{userName}}</h2>
                <input type="text" v-model = "name" placeholder="请输入名字"></input>
                <button @click = "saveValue">保存</button>
                <h3>{{saveName}}</h3>
                </div>`,
            data(){
                return{
                    userName:"用户",
                    timer:null,
                    num:null,
                    name:"",
                    saveName:''
                }
            },
            methods:{
                changName(val){
                    this.userName = val.data.user;
                },
                saveValue(){
                    this.saveName = this.name
                }

            },
            beforeRouteEnter(to,from,next){
                //注意1:动态路由。组件相同数据不同,这个函数只会进来一次,切换路由的时候,到beforeRouteUpdate中
                // 注意2:这个函数内路由还灭挂载通过this去取不到组件对象,需要通过next函数取
                axios.defaults.baseURL = "https://www.easy-mock.com/mock/5d65ef060d2acc6a1fb9a126/example"
                console.log(to.params.id);
                axios.get(`/vuelearn/${ to.params.id }`).then(res=>{
                    console.log(res);
                    next((vm=>{
                        vm.changName(res);
                    }))
                }).catch(err=>{
                    console.log(err)
                })
         
            },
            beforeRouteUpdate(to,from,next){
                // 注意3:一定要调用next函数,否则组件不会进来
               
                axios.get(`/vuelearn/${ to.params.id }`).then(res=>{
                    this.changName(res)
                }).catch(err=>{
                    console.log(err)
                });
                next();
                if(to.params.id==2){
                    // 添加定时器
                    var time = new Date();
                    this.num =time.getSeconds()
                    this.timer = setInterval(()=>{
                        this.num++;
                        console.log(this.num);
                    },1000);
                }

            },
            beforeRouteLeave(to,from,next){
                // 路由离开前。要离开这个组件才会进入这个函数
               
               
                console.log("离开了");
                 // 常用场景1:清空定时器
                clearInterval(this.timer);
                // 判断是否保存,保存了才能离开
                if(this.saveName===this.name){
                    next()
                }else{
                    confirm("请保存后在离开")
                }
                

            }
        } 
        var Test = {
            template:`
            <div>我是测试</div>`,
          
        }
        var router = new VueRouter({
            routes:[
                {
                    path:"/test",
                    component:Test,
                    name:"test"
                },
                {
                    path:"/user/:id",
                    component:User,
                    name:"user"
                }
            ]
        })
        // <router-link :to = "{name:'user',params:{id:2}}">用户2</router-link> 
        var App = {
            template:`
            <div>
                <router-link :to = "{name:'user',params:{id:1}}">用户1</router-link>
                <router-link :to = "{name:'user',params:{id:2}}">用户2</router-link>
                <router-link to="/test">测试</router-link>
                <router-view></router-view>
                </div>
            `,
            components:{
                User,
                Test

            }

        }
        new Vue({
            el:'#app',
            template:`
            <App/>
            `,
            router,
            components:{
                App
            }
        })
    </script>
</body>
</html>

6、keep alive在路由中的使用

缓存机制

<keep-alive>
<router-view><router-view>
</keep-alive>

一声蔷薇udVkP
25 声望3 粉丝

未来可期