vue-router(路由的两种工作模式、router-link的replace属性、编程式路由导航、缓存路由组件keep-alive、路由守卫【全局路由守卫、独享路由守卫、组件内路由守卫】)(十二)

vue系列文章目录

第一章:Vue基础知识笔记(模板语法、数据绑定、事件处理、计算属性)(一)
第二章:Vue基础知识(计算属性、监视属性、computed和watch之间的区别、绑定样式)(二)
第三章:Vue基础知识(条件渲染、列表渲染、收集表单数据、过滤器)(三)
第四章:Vue基础知识(内置指令、自定义指令、Vue生命周期)(四)
第五章:Vue基础知识之组件机制(非单文件组件、单文件组件)(五)
第六章:Vue创建脚手架(六)
第七章:Vue使用脚手架(ref、props、mixin、插件、scoped)(七)
第八章:Vue组件通信(组件的自定义事件、全局事件总线、消息订阅与发布、插槽、props)(八)
第九章:Vue使用脚手架(nextTick、Vue封装的过度与动画、vue脚手架配置代理)(九)
第十章:Vuex(十)
第十一章:vue-router(基本使用、路由重定向、多级路由、路由命名、路由的query和params参数、路由的props配置)(十一)
第十三章:Vue组件通信二&数据同步(v-model、属性修饰符.sync、 a t t r s 与 attrs与 attrslisteners、refs. c h i l d r e n 与 r e f s . children与refs. childrenrefs.parent、provide与inject )(十三)



一、路由的两种工作模式

  • 对于一个url来说,什么是hash值?—— #及其后面的内容就是hash值。
  • hash值不会包含在 HTTP 请求中,即:hash值不会带给服务器。

1. hash模式

Url格式: http://yoursite.com/#/user/id

  • 默认采用的模式。地址中永远带着#号 。
  • 若以后将地址通过第三方手机app分享,若app校验严格,则地址会被标记为不合法。
  • 使用 URL 的 hash 来模拟一个完整的 URL,于是当 URL 改变时,页面不会重新加载。
  • 兼容性较好。
    const router = new VueRouter({ routes: [...] })
    

2.history模式

Url格式: http://yoursite.com/user/id

  • 地址干净,美观
  • 兼容性和hash模式相比略差。
  • 应用部署上线时需要后端人员支持,解决刷新页面服务端404的问题。
  • 这种模式充分利用 history.pushState API 来完成 URL 跳转而无须重新加载页面。可以使用mode: 'history’来切换模式,默认是Hash模式
const router = new VueRouter({ mode: 'history', routes: [...] })

二、<router-link>的replace属性

  • 作用: 控制路由跳转时操作浏览器历史记录的模式
  • 浏览器的历史记录有两种写入方式: 分别为pushreplacepush是追加历史记录,replace是替换当前记录。路由跳转时候默认为push
  • 如何开启replace模式:<router-link replace .......>News</router-link>
    注意:
    当浏览器返回上一页内容时,其实返回的是上上页的内容,因为上一页内容,已经被当前页面替换掉了

二、编程式路由导航

  1. 作用: 不借助<router-link> 实现路由跳转,让路由跳转更加灵活

  2. 具体编码:

    //$router的两个API
    this.$router.push({
    	name:'xiangqing',
    		params:{
    			id:xxx,
    			title:xxx
    		}
    })
    	   //通过命名路由结合params进行路由传值
    this.$router.replace({
    	name:'xiangqing',
    		params:{
    			id:xxx,
    			title:xxx
    		}
    })
    
     this.$router.push({ 
       path: '/about', 
       //通过query进行路由传值 如果参数对象中是path, params 不生效,只能通过query来传递
       query: { age: 18 }
     }) 
    this.$router.forward() //前进
    this.$router.back() //后退
    this.$router.go() //可前进也可后退
    

this.$router.replace(参数)
与push非常类似,不同的是不在history中加入新的记录,而是替换当前的history记录

this.$router.go(num)
这个方法的参数是一个整数,意思是在 history 记录中向前或者后退多少步,类似window.history.go(n)

注意:

  1. $route$router是有区别的

    • $router为VueRouter实例,想要导航到不同URL,则使用$router.push方法
    • $route为当前router跳转的对象,里面可以获取name、path、query、params等 Router实例方法。
  2. 对比push传递params与传递query的区别

    • 传递params的时候,参数不会拼接到浏览地址栏,重新刷新参数将会丢失

    • 传递query的时候,参数会拼接到浏览器地址栏

编程式导航问题

当我们使用编程式导航来控制路由跳转时,会出现一个问题,当多次点击某个按钮时,会报错冗余导航。
原因:vue-router3.1.0之后, 引入了push()的promise的语法, 如果没有通过参数指定回调函数就返回一个promise来指定成功/失败的回调, 且内部会判断如果要跳转的路径和参数都没有变化, 会抛出一个失败的promise

解决方法有两种 :

方法一

​ 在项目内使用Vue-Router 3.0以下版本

方法二

​ 解决Vue-Router在3.0版本以上重复点击报错的问题,在适当的的位置对push和replace方法进行重写设置
重写一

// push
const originalPush = VueRouter.prototype.push
  VueRouter.prototype.push = function push(location) {
  return originalPush.call(this, location).catch(err => err)
}
// replace
const originalReplace = VueRouter.prototype.replace
  VueRouter.prototype.replace = function replace(location) {
  return originalReplace.call(this, location).catch(err => err)
}

重写而

//需要重写VueRouter.prototype原型对象身上的push|replace方法
//先把VueRouter.prototype身上的push|replace方法进行保存一份
let originPush = VueRouter.prototype.push;
let originReplace = VueRouter.prototype.replace;
//重写VueRouter.prototype身上的push方法了
VueRouter.prototype.push = function(location, resolve, reject) {
  //第一个形参:路由跳转的配置对象(query|params)
  //第二个参数:undefined|箭头函数(成功的回调)
  //第三个参数:undefined|箭头函数(失败的回调)
  if (resolve && reject) {
    //push方法传递第二个参数|第三个参数(箭头函数)
    //originPush:利用call修改上下文,变为(路由组件.$router)这个对象,第二参数:配置对象、第三、第四个参数:成功和失败回调函数
    originPush.call(this, location, resolve, reject);
  } else {
    //push方法没有产地第二个参数|第三个参数
    originPush.call(
      this,
      location,
      () => {},
      () => {}
    );
  }
};
//重写VueRouter.prototype身上的replace方法了
VueRouter.prototype.replace = function(location, resolve, reject) {
  if (resolve && reject) {
    originReplace.call(this, location, resolve, reject);
  } else {
    originReplace.call(
      this,
      location,
      () => {},
      () => {}
    );
  }
};

三、缓存路由组件

keep-alive 是一个抽象组件:它自身不会渲染成一个 DOM 元素,也不会出现在父组件链中。

  1. 作用: 让不展示的路由组件保持挂载,不被销毁。防止重复渲染DOM,减少加载时间及性能消耗,提高用户体验性。
  2. 原理: 在 created 函数调用时将需要缓存的 VNode 节点保存在 this.cache 中/在 render(页面渲染) 时,如果 VNode 的 name 符合缓存条件(可以用 include 以及 exclude 控制),则会从 this.cache 中取出之前缓存的 VNode 实例进行渲染。(VNode:虚拟DOM,其实就是一个JS对象
  3. 参数:
    在这里插入图片描述
  4. 案例(代码片段):

注意: include/exclude 值是组件中的 name 命名,而不是路由中的组件 name 命名;

<!-- 缓存多个路由组件 -->
<keep-alive :include="['News','Message']"> 
</keep-alive>

<!-- 将缓存 name 为News的路由组件 -->
<keep-alive include="News"> //include中写的是组件的name
     <router-view></router-view>
</keep-alive>

<!-- 使用正则表达式,需使用 v-bind -->
<keep-alive :include='/a|b/'>
  <router-view/>
</keep-alive>
	
<!-- 将缓存 name 为 a 或者 b 的组件,结合动态组件使用-->
<keep-alive include='a,b'>
  <router-view/>
</keep-alive>

<!-- 将不缓存 name 为 test 的组件-->
<keep-alive exclude='test'>
  <router-view/>
</keep-alive>

四、两个新的生命周期钩子

  • 作用: 路由组件所独有的两个钩子,用于捕获路由组件的激活状态。

  • 具体名字:

    • activated路由组件被激活时触发,该钩子函数在服务器端渲染期间不被调用。
    • deactivated路由组件失活时触发,该钩子函数在服务器端渲染期间不被调用。

    注意: 包含在 keep-alive 中,但符合 exclude ,不会调用activated和 deactivated。
    初次进入时: created > mounted > activated;退出后触发 deactivated
    再次进入时: 直接触发 activated

案例(代码片段):

  1. router/index.js

    routes:[
    	{
    		path:'/about',
    		component:About,
    	},
    	{
    		path:'/home',
    		component:Home,
    		children:[ //通过children配置子级路由
     	   		 // 子路由中也可以设置默认路径 路由重定向
     	        { path: '/', redirect: 'news' },
    			{
    				//配置子路由的时候 不需要加 / 但是在router-link中配置使用时 要写完整路径 比如/home/news
    				path:'news', //此处一定不要写:/news
    				component:News
    			},
    			{
    				path:'message',//此处一定不要写:/message
    				component:Message
    			}
    		]
    	}
    ]
    
  2. 路由页面

    Home.vue

    <template>
      <div>
        <h2>Home组件内容</h2>
        <div>
          <ul class="nav nav-tabs">
            <li>
              <router-link
                class="list-group-item"
                active-class="active"
                to="/home/news"
                >News</router-link
              >
            </li>
            <li>
              <router-link
                class="list-group-item"
                active-class="active"
                to="/home/message"
                >Message</router-link
              >
            </li>
          </ul>
    
          <keep-alive include="News">
            <router-view></router-view>
          </keep-alive>
        </div>
      </div>
    </template>
    

    News.vue

    <template>
      <ul>
        <li :style="{ opacity }">欢迎学习Vue</li>
        <li>news001 <input type="text" /></li>
        <li>news002 <input type="text" /></li>
        <li>news003 <input type="text" /></li>
      </ul>
    </template>
    
    <script>
    export default {
      name: "News",
      data() {
        return {
          opacity: 1,
        };
      },
      beforeDestroy() {
        console.log("News组件即将被销毁了");
        clearInterval(this.timer);
      },
      created() {
        console.log("created");
      },
      mounted() {
        console.log("mounted");
      },
      activated() {
        console.log("News组件被激活了,activated");
        this.timer = setInterval(() => {
          console.log("@");
          this.opacity -= 0.01;
          if (this.opacity <= 0) this.opacity = 1;
        }, 1000);
      },
      deactivated() {
        console.log("News组件失活了,deactivated");
        clearInterval(this.timer);
      },
    };
    </script>
    

运行结果:
在这里插入图片描述
在这里插入图片描述

五、路由守卫

作用: 对路由进行权限控制

分类: 全局守卫、独享守卫、组件内守卫

1. 全局守卫

全局路由守卫分为全局前置路由守卫beforeEach全局后置路由守卫afterEach

//全局前置守卫:初始化时执行、每次路由切换前执行
router.beforeEach((to,from,next)=>{
	console.log('beforeEach',to,from)
	if(to.meta.isAuth){ //判断当前路由是否需要进行权限控制
		if(localStorage.getItem('school') === 'hhh'){ //权限控制的具体规则
			next() //放行
		}else{
			alert('暂无权限查看')
			// next({name:'guanyu'})
		}
	}else{
		next() //放行
	}
})

//全局后置守卫:初始化时执行、每次路由切换后执行
router.afterEach((to,from)=>{
	console.log('afterEach',to,from)
	if(to.meta.title){ 
		document.title = to.meta.title //修改网页的title
	}else{
		document.title = '123'
	}
})

案例(代码片段,为了节省空间,完整案例请结合上篇文章,和本篇上下文自行领会):
router/index.js

import VueRouter from 'vue-router';

import Home from '../pages/Home';
import About from '../pages/About';
import Message from '../pages/Message'
import News from '../pages/News'

const router = new VueRouter({
    routes: [{
            name: '关于',
            path: '/about',
            component: About,
            meta: { title: '关于' }
        },
        {
            name: "zhuye",
            path: '/home',
            component: Home,
            meta: { title: '主页' },
            children: [
                { path: '/', redirect: 'news' },
                {
                    name: 'message',
                    path: 'message/:id/:title',
                    component: Message,
                    meta: { isAuth: true, title: '消息' }
                }, {
                    name: 'news',
                    path: 'News',
                    component: News,
                    meta: { isAuth: true, title: '新闻' }

                }
            ]
        },
        // {
        //     path: '/',
        //     redirect: '/home'
        // }
    ]
});

//全局前置路由守卫————初始化的时候被调用、每次路由切换之前被调用
router.beforeEach((to, from, next) => {
        console.log('前置路由守卫,to~~', to);
        console.log('前置路由守卫,from~~', from);

        if (to.meta.isAuth) {
            if (localStorage.getItem('school') === 'hhh') {
                next()
            } else {
                alert('学校名不对,无权限查看!')
            }
        } else {
            next();
        }
    })
    //全局后置路由守卫————初始化的时候被调用、每次路由切换之后被调用
router.afterEach((to, from) => {
    console.log('后置路由守卫,to~~~', to)
    console.log('后置路由守卫,from~~~', from)
    
	// 根据我们在每一个路由映射对象中设置的meta元数据 来进行一个动态title的显示
    document.title = to.meta.title || '123'
})
export default router;

运行结果:
结果一:(localStorage中存储了school且值正确)
在这里插入图片描述
结果一:(localStorage中没有存储school或值不正确)
在这里插入图片描述

2.独享守卫

可以在路由配置上直接定义 beforeEnter 守卫。

beforeEnter(to,from,next){
	console.log('beforeEnter',to,from)
	if(to.meta.isAuth){ //判断当前路由是否需要进行权限控制
		if(localStorage.getItem('school') === 'atguigu'){
			next()
		}else{
			alert('暂无权限查看')
			// next({name:'guanyu'})
		}
	}else{
		next()
	}
}

案例(代码片段):

import VueRouter from 'vue-router';

import Home from '../pages/Home';
import About from '../pages/About';
import Message from '../pages/Message'
import News from '../pages/News'

const router = new VueRouter({
    routes: [{
            name: '关于',
            path: '/about',
            component: About,
            meta: { title: '关于' }
        },
        {
            name: "zhuye",
            path: '/home',
            component: Home,
            meta: { title: '主页' },
            children: [
                { path: '/', redirect: 'news' },
                {
                    name: 'message',
                    path: 'message/:id/:title',
                    component: Message,
                    meta: { isAuth: true, title: '消息' },
                    beforeEnter: (to, from, next) => {
                        console.log('独享路由守卫', to, from);
                        if (to.meta.isAuth) { //判断是否需要鉴权
                            if (localStorage.getItem('school') === 'hhh') {
                                next()
                            } else {
                                alert('学校名不对,无权限查看!')
                            }
                        } else {
                            next()
                        }
                    }
                }, {
                    name: 'news',
                    path: 'News',
                    component: News,
                    meta: { isAuth: true, title: '新闻' }

                }
            ]
        },

    ]
});

export default router;

运行结果:
在这里插入图片描述

3. 组件内守卫

组件内守卫分为进入时路由守卫beforeRouteEnter,离开时路由守卫beforeRouteLeave,路由更新时路由守卫beforeRouteUpdate

//进入守卫:通过路由规则,进入该组件时被调用 ,不能获取组件实例 `this`
beforeRouteEnter (to, from, next) {
},
//在当前路由改变,但是该组件被复用时调用,可访问this,主要搭配动态路由一起使用监听动态路由的变化
 beforeRouteUpdate (to, from, next) {
//离开守卫:通过路由规则,离开该组件时被调用
beforeRouteLeave (to, from, next) {
}

案例(代码片段):
About.vue

<template>
  <h2>About页面</h2>
</template>

<script>
export default {
  name: "About",
  beforeCreate() {
    console.log("beforeCreate");
  },
  created() {
    console.log("created");
  },
  beforeMount() {
    console.log(" beforeMount");
  },
  mounted() {
    console.log("mounted");
  },

  beforeRouteEnter(to, from, next) {
    console.log("About--beforeRouteEnter", to, from);
    if (to.meta.isAuth) {
      //判断是否需要鉴权
      if (localStorage.getItem("school") === "atguigu") {
        next();
      } else {
        alert("学校名不对,无权限查看!");
      }
    } else {
      next();
    }
  },
  //通过路由规则,离开该组件时被调用
  beforeRouteLeave(to, from, next) {
    console.log("About--beforeRouteLeave", to, from);
    next();
  },
  beforeDestroy() {
    console.log("beforeDestroy");
  },
  destroyed() {
    console.log("destroyed");
  },
};
</script>

运行结果:
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值