3

一文搞定常考Vue-Router知识点-51CTO.COM

 1 year ago
source link: https://developer.51cto.com/article/712291.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

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事件<a href="#search">search</a> ;
  • 直接使用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


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK