服务器之家:专注于VPS、云服务器配置技术及软件下载分享
分类导航

node.js|vue.js|jquery|angularjs|React|json|js教程|

服务器之家 - 编程语言 - JavaScript - vue.js - 一文搞定常考Vue-Router知识点

一文搞定常考Vue-Router知识点

2022-06-24 16:27前端一码平川找不到bug的coder vue.js

Vue-router有两种路由模式,分别是hash模式和history模式,在路由配置中默认的是hash模式。,history模式的url中没有#,看起来也比hash模式更美观,本质是通过传统的路由分发模式,即用户输入一个url时,服务器会接收请求并解析这个UR

1.路由的起源

路由其实就是url和文件的映射,在后端控制路由在接收到客户端发来的http请求时,会根据响应的url来找到相应的映射函数,执行得到返回值给客户端。对于简单的静态资源服务,所有url的映射函数是一个文件读取操作;对于动态资源,映射函数可能是个数据库读取操作,也可能是一些数据的处理等等。然后根据读取的数据路由,在服务端使用相应的模板对页面进行渲染后,在返回渲染完毕的页面。

服务端控制的路由来实现服务端渲染,服务端渲染的优缺点在于:

  • 优点:安全性好,便于SEO,能够提升前端页面的渲染速度
  • 缺点:会增加服务器的压力,代码冗余不便于维护,不利于用户体验

服务端控制路由本质是url和文件读取操作的映射,而前端路由是进行dom元素的显示和隐藏操作,在访问不同路径时显示不同的组件。当前前端路由主要有两种实现方式:hash模式和history模式。

前端路由的优缺点在于:

  • 缺点:不利于SEO,使用浏览器的前进、后退键时会重新发送请求,没有合理的利用缓存
  • 优点:良好的交互体验,用户不需要刷新页面,页面显示流畅,良好的前后端工作分离模式,减轻服务器压力

2.路由的两种模式的区别和原理?

Vue-router有两种路由模式,分别是hash模式和history模式,在路由配置中默认的是hash模式。

hash模式

早期的前端路由是基于location.hash来实现的,对此在react-router和vue-router都是默认将hash路由作为路由模式的。hash模式的url默认带有#,location.hash的值就是url的#后面的内容。

在vue-router中,对于http://blog.onechuan.cn/#/login的hash值就是#/login。

特点:

  • hash值会存现客户端的url中,但是不会出现在发送给服务端的http请求中,也就是hash不会被发送;
  • hash值得改变,不会重新加载页面,只是单纯地在浏览器的访问历史中增加个记录,其实就是在栈结构中添加和移除数据,来实现浏览器的回退和前进控制hash的切换;
  • hash模式对于浏览器的兼容性比较好,也是SPA单页面应用的标配。

使用: 切换hash路由的方式有两种

  • 使用a标签设置href属性,在用户点击后触发改变url,也就是触发hashchange事件search ;
  • 直接使用js来实现对location.hash进行赋值,从而实现改变url,触发hashchange事件location.hash="#search"。

对此,可以看到hash路由的实现就是基于hashchange事件进行监听。

原理:

hash模式的主要原理就是onhashchange()事件:

window.onhashchange = function(event){ console.log(event.oldURL, event.newURL); let hash = location.hash.slice(1); }

使用onhashchange()的优点:

  • 在页面的hash值发生变化时,无需向后端发起请求,window就可以监听事件的改变,并按规则加载相应的代码;
  • hash值变化对应的URL都会被浏览器记录下来,这样浏览器就能实现页面的前进和后退。没有请求后端服务器,但是页面的hash值和对应的URL关联映射。

history模式

history模式的url中没有#,看起来也比hash模式更美观,本质是通过传统的路由分发模式,即用户输入一个url时,服务器会接收请求并解析这个URL,然后做出相应的逻辑处理。

当使用history模式时,URL就像这样:https://blog.onechuan.cn/user/id。

特点:

  • history模式的url相对于hash模式而言,更加美观;
  • history模式需要服务端进行相应的配置支持,否则返回时会返回404;
  • pushState 和 repalceState 的标题(title):一般浏览器会忽略,最好传入 null;
  • 可以使用 popstate 事件来监听 url 的变化;

原理:

history模式主要依赖于:history.pushState()和 history.repalceState() 两个api来实现不进行刷新的情况下,操作浏览器的历史记录。

  • 修改历史状态:history.pushState()和 history.repalceState()提供了对历史记录进行修改的功能。只是当他们进行修改时,虽然修改了url,但浏览器不会立即向后端发送请求。如果要做到改变url但又不刷新页面的效果,就需要前端用上这两个API。
  • 切换历史状态:包括forward()、back()、go()三个方法,对应浏览器的前进、后退、跳转操作

pushState和repalceState不会触发popstate 事件,这时我们需要手动触发页面渲染。

缺点:在刷新页面的时候,如果没有相应的路由或资源,就会刷出404来。

两种路由模式的对比

对比点

hash模式

history模式

原理

onhashchange()

history.pushState()和 history.repalceState()

兼容性

>= ie 8,其它主流浏览器

>= ie 10,其它主流浏览器

实用性

不需要对服务端做改动

需要服务端配置支持

3.如何获取页面的hash变化?

通过监听$route的变化

// 监听,当路由发生变化的时候执行
watch: { $route: { handler: function(val, oldVal){ console.log(val); }, // 深度观察监听
    deep: true } }

通过js的window.location.hash读取#值

window.location.hash 的值可读可写,读取来判断状态是否改变,写入时可以在不重载网页的前提下,添加一条历史访问记录。

4.Vue-Router如何实现路由懒加载?

使用箭头函数+import动态加载

const List = () => import('@/components/list.vue') const router = new VueRouter({ routes: [ { path: '/list', component: List } ] });

使用箭头函数+require动态加载

const router = new Router({ routes: [ { path: '/list', component: resolve => require(['@/components/list'], resolve) } ] });

使用webpack的require.ensure技术

这种情况下,多个路由指定相同的chunkName,会合并打包成一个js文件。

// r就是resolve
const List = r => require.ensure([], () => r(require('@/components/list')), 'list'); // 路由也是正常的写法  这种是官方推荐的写的 按模块划分懒加载 
const router = new Router({ routes: [ { path: '/list', component: List, name: 'list' } ] }));

5.$route 和$router 的区别?

$route 是“路由信息对象”,包括 path、params hash、query、fullPath、matched、name 等路由信息参数;

$router 是“路由实例”对象包括了路由的跳转方法,钩子函数等。

6.如何定义动态路由?如何获取传过来的动态参数?

param方式

配置路由格式:/router/:id

  • 传递方式:在path后面跟上对应的值
  • 传递后形成的路径:/router/123

定义动态路由

//在APP.vue<router-link :to="`/user/${userId}`" replace>用户router-link> //在router.js { path: '/user/:userid', component: User, }

路由跳转

// 方法1: <router-link :to="{ name: 'users', params: { uname: oenchuan }}">按钮router-link> // 方法2:
this.$router.push({name:'users',params:{uname:onechuan}}) // 方法3:
this.$router.push(`/user/${onechuan}`);

参数获取通过 $route.params.userid 获取传递的值。

query方式

  • 配置路由格式:/router,也就是普通配置
  • 传递方式:对象中使用query的key作为传递方式
  • 传递后形成的路径:/route?id=123

路由定义

//方式1:直接在router-link 标签上以对象的形式 <router-link :to="{path:'/profile',query:{name:'why',age:28,height:188}}">档案router-link> // 方式2:写成按钮以点击事件形式 <button @click='profileClick'>我的button> profileClick(){ this.$router.push({ path: "/profile", query: { name: "kobi", age: "28", height: 198 } }); }

跳转方法

// 方法1: <router-link :to="{ name: 'users', query: { uname: james }}">按钮router-link> // 方法2:
this.$router.push({ name: 'users', query:{ uname:james }}) // 方法3: <router-link :to="{ path: '/user', query: { uname:james }}">按钮router-link> // 方法4:
this.$router.push({ path: '/user', query:{ uname:james }}) // 方法5:
this.$router.push('/user?uname=' + james)

获取参数通过$route.query 获取传递的值。

7.params和query的区别?

对比点

params

query

引入方式

用name来引入

用path来引入

接收参数

this.$route.params.name

this.$route.query.name

url地址显示

params则类似于post,url不显示参数

query更加类似于ajax中get传参,url显示参数

刷新页面

params刷新会丢失 params里面的数据

query刷新不会丢失query里面的数据

8.Vue-router 路由钩子在生命周期的体现?

完整的路由导航解析流程(不包括其他生命周期)

  • 触发进入其他路由。
  • 调用要离开路由的组件守卫beforeRouteLeave
  • 调用局前置守卫∶ beforeEach
  • 在重用的组件里调用 beforeRouteUpdate
  • 调用路由独享守卫 beforeEnter。
  • 解析异步路由组件。
  • 在将要进入的路由组件中调用 beforeRouteEnter
  • 调用全局解析守卫 beforeResolve
  • 导航被确认。
  • 调用全局后置钩子的 afterEach 钩子。
  • 触发DOM更新(mounted)。
  • 执行beforeRouteEnter 守卫中传给 next 的回调函数

触发钩子的完整顺序

路由导航、keep-alive、和组件生命周期钩子结合起来的,触发顺序,假设是从a组件离开,第一次进入b组件∶

  • beforeRouteLeave:路由组件的组件离开路由前钩子,可取消路由离开。
  • beforeEach:路由全局前置守卫,可用于登录验证、全局路由loading等。
  • beforeEnter:路由独享守卫
  • beforeRouteEnter:路由组件的组件进入路由前钩子。
  • beforeResolve:路由全局解析守卫
  • afterEach:路由全局后置钩子
  • beforeCreate:组件生命周期,不能访问tAis。
  • created;组件生命周期,可以访问tAis,不能访问dom。
  • beforeMount:组件生命周期
  • deactivated:离开缓存组件a,或者触发a的beforeDestroy和destroyed组件销毁钩子。
  • mounted:访问/操作dom。
  • activated:进入缓存组件,进入a的嵌套子组件(如果有的话)。
  • 执行beforeRouteEnter回调函数next。

导航行为被触发到导航完成的整个过程

  • 导航行为被触发,此时导航未被确认。
  • 在失活的组件里调用离开守卫 beforeRouteLeave。
  • 调用全局的 beforeEach守卫。
  • 在重用的组件里调用 beforeRouteUpdate 守卫(2.2+)。
  • 在路由配置里调用 beforeEnteY。
  • 解析异步路由组件(如果有)。
  • 在被激活的组件里调用 beforeRouteEnter。
  • 调用全局的 beforeResolve 守卫(2.5+),标示解析阶段完成。
  • 导航被确认。
  • 调用全局的 afterEach 钩子。
  • 非重用组件,开始组件实例的生命周期:beforeCreate&created、beforeMount&mounted
  • 触发 DOM 更新。
  • 用创建好的实例调用 beforeRouteEnter守卫中传给 next 的回调函数。
  • 导航完成

9.Vue-router 导航守卫有哪些?

  • 全局前置/钩子:beforeEach、beforeResolve、afterEach
  • 路由独享的守卫:beforeEnter
  • 组件内的守卫:beforeRouteEnter、beforeRouteUpdate、beforeRouteLeave

Vue-Router导航守卫

在一些场景,比如:最常见的登录权限验证,当用户满足条件时,才让其进入导航;否则就取消跳转,并跳到登录页面让其登录。

为此有很多种方法可以植入路由的导航过程:

  • 全局的
  • 单个路由独享的
  • 组件级的

全局路由钩子

vue-router全局有三个路由钩子;

  • router.beforeEach 全局前置守卫,进入路由之前
  • router.beforeResolve 全局解析守卫(2.5.0+),在 beforeRouteEnter 调用之后调用
  • router.afterEach 全局后置钩子,进入路由之后

具体使用∶

  • beforeEach(判断是否登录了,没登录就跳转到登录页)
  • afterEach (跳转之后滚动条回到顶部)
router.beforeEach((to, from, next) => { let ifInfo = Vue.prototype.$common.getSession('userData'); // 判断是否登录的存储信息
    if (!ifInfo) { // sessionStorage里没有储存user信息    
        if (to.path == '/') { //如果是登录页面路径,就直接next() next(); } else { //不然就跳转到登录      
            Message.warning("请重新登录!"); window.location.href = Vue.prototype.$loginUrl; } } else { return next(); } });
router.afterEach((to, from) => { // 跳转之后滚动条回到顶部  
    window.scrollTo(0,0); });

单个路由独享钩子

beforeEnter 如果不想全局配置守卫的话,可以为某些路由单独配置守卫,有三个参数∶ to、from、next

export default [ { path: '/', name: 'login', component: login, beforeEnter: (to, from, next) => { console.log('即将进入登录页面') next() } } ]

组件内钩子

beforeRouteUpdate、beforeRouteEnter、beforeRouteLeave这三个钩子都有三个参数∶to、from、next

  • beforeRouteEnter∶ 进入组件前触发
  • beforeRouteUpdate∶ 当前地址改变并且改组件被复用时触发,举例来说,带有动态参数的路径foo/∶id,在 /foo/1 和 /foo/2 之间跳转的时候,由于会渲染同样的foa组件,这个钩子在这种情况下就会被调用
  • beforeRouteLeave∶ 离开组件被调用

注意点,beforeRouteEnter组件内还访问不到this,因为该守卫执行前组件实例还没有被创建,需要传一个回调给 next来访问,例如:

beforeRouteEnter(to, from, next) { next(target => { if (from.path == '/classProcess') { target.isFromProcess = true } }) }

10.Vue-router跳转和location.href有什么区别?

  • 使用location.href= /url 来跳转,简单方便,但是刷新了页面;
  • 使用history.pushState( /url ) ,无刷新页面,静态跳转;
  • 引进 router ,然后使用router.push( /url )来跳转,使用了 diff 算法,实现了按需加载,减少了 dom 的消耗。
  • 其实使用 router 跳转和使用history.pushState()没什么差别的,因为vue-router就是用了history.pushState() ,尤其是在history模式下。

参考文章

https://juejin.cn/post/6964779204462247950

https://mp.weixin.qq.com/s/7TLVBK2A73-1f7yOPMWMHg

原文地址:https://mp.weixin.qq.com/s/bp2LebO1yO8_VmRW4kMD9w

延伸 · 阅读

精彩推荐
  • vue.jsVue+Bootstrap实现简易学生管理系统

    Vue+Bootstrap实现简易学生管理系统

    这篇文章主要为大家详细介绍了Vue+Bootstrap实现简易学生管理系统,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...

    程序猿玖月柒5532022-01-20
  • vue.jsvue 使用rules对表单字段进行校验的步骤

    vue 使用rules对表单字段进行校验的步骤

    这篇文章主要介绍了vue 使用rules对表单字段进行校验的步骤,帮助大家更好的理解和使用vue,感兴趣的朋友可以了解下...

    紫藤萝yu9412021-12-20
  • vue.jsvue编写简单的购物车功能

    vue编写简单的购物车功能

    这篇文章主要为大家详细介绍了vue编写简单的购物车功能,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...

    狗子_Lyi6382021-12-28
  • vue.jsvue中配置scss全局变量的步骤

    vue中配置scss全局变量的步骤

    这篇文章主要介绍了vue中配置scss全局变量的步骤,帮助大家更好的理解和使用vue框架,感兴趣的朋友可以了解下...

    吃火鸡的馒头4492021-12-21
  • vue.js基于element-ui封装表单金额输入框的方法示例

    基于element-ui封装表单金额输入框的方法示例

    这篇文章主要介绍了基于element-ui封装表单金额输入框的方法示例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需...

    Huup_We12492021-12-27
  • vue.jsVue 集成 PDF.js 实现 PDF 预览和添加水印的步骤

    Vue 集成 PDF.js 实现 PDF 预览和添加水印的步骤

    这篇文章主要介绍了如何在 Vue 中集成 Mozilla/PDF.js ,实现自定义的 PDF 预览器,以及给被预览的 PDF 添加水印...

    asing1elife11632022-01-06
  • vue.js在Vue.js中加载字体的正确方法

    在Vue.js中加载字体的正确方法

    这篇文章主要介绍了在Vue.js中加载字体的正确方法,本文通过实例图文相结合给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的...

    程序员张张9372022-02-24
  • vue.js用vite搭建vue3应用的实现方法

    用vite搭建vue3应用的实现方法

    这篇文章主要介绍了用vite搭建vue3应用的实现方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下...

    Asiter7522022-01-22