VUE路由

177 阅读11分钟

1. 路由懒加载模式

(1)方案一(常用):使用箭头函数+import动态加载

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

(2)方案二:使用箭头函数+require动态加载

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

原理: Vue中的路由懒加载是指在需要的时候才加载对应的路由组件,而不是在应用初始化时一次性加载所有的路由组件,这样可以提升应用的加载速度和性能。懒加载的原理主要涉及Webpack的动态import和 Vue Router的异步组件。

具体的步骤如下:

  1. 配置Webpack的动态import:在Webpack中,我们可以使用动态import来实现代码的按需加载。针对Vue路由,我们可以使用() => import('路径')的方式来动态导入对应的组件。例如:
const Foo = () => import('./Foo.vue');
  1. 在路由配置中使用异步组件:在Vue Router的路由配置中,可以使用component字段来指定路由对应的组件,这个组件可以是同步加载的组件,也可以是异步加载的组件。对于异步加载的路由组件,我们可以将其配置为一个返回Promise的函数,并在函数内部使用动态import来异步加载对应的组件。例如:
const Foo = () => import('./Foo.vue');
const routes = [
  { path: '/foo', component: Foo }
];

这样配置后,在访问该路由时,对应的组件会动态地被加载。

总的来说,Vue路由懒加载的原理就是通过Webpack的动态import功能来异步加载组件,以实现按需加载和提升应用性能。

2. $route$router的区别是什么?

  • router为VueRouter的实例,是一个全局路由对象,包含了路由跳转的方法、钩子函数等。
  • route 是路由信息对象 || 跳转的路由对象,每一个路由都会有一个route对象,是一个局部对象,包含path,params,hash,query,fullPath,matched,name等路由信息参数。

3. 路由的hash和history模式的区别

Vue-Router有两种模式:hash模式history模式。默认的路由模式是hash模式。

1. hash模式

简介: hash模式是开发中默认的模式,它的URL带着一个#,例如:www.abc.com/#/vue,它的hash值就是#/vue

特点:hash值会出现在URL里面,但是不会出现在HTTP请求中,对后端完全没有影响。所以改变hash值,不会重新加载页面。这种模式的浏览器支持度很好,低版本的IE浏览器也支持这种模式。hash路由被称为是前端路由,已经成为SPA(单页面应用)的标配。

原理: 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关联起来了。

2. history模式

简介: history模式的URL中没有#,它使用的是传统的路由分发模式,即用户在输入一个URL时,服务器会接收这个请求,并解析这个URL,然后做出相应的逻辑处理。 特点: 当使用history模式时,URL就像这样:abc.com/user/id。相比hash模式更加好看。但是,history模式需要后台配置支持。如果后台没有正确配置,访问时会返回404。 API: history api可以分为两大部分,切换历史状态和修改历史状态:

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

虽然history模式丢弃了丑陋的#。但是,它也有自己的缺点,就是在刷新页面的时候,如果没有相应的路由或资源,就会刷出404来。

如果想要切换到history模式,就要进行以下配置(后端也要进行配置):

const router = new VueRouter({
  mode: 'history',
  routes: [...]
})

3. 两种模式对比

调用 history.pushState() 相比于直接修改 hash,存在以下优势:

  • pushState() 设置的新 URL 可以是与当前 URL 同源的任意 URL;而 hash 只可修改 # 后面的部分,因此只能设置与当前 URL 同文档的 URL;
  • pushState() 设置的新 URL 可以与当前 URL 一模一样,这样也会把记录添加到栈中;而 hash 设置的新值必须与原来不一样才会触发动作将记录添加到栈中;
  • pushState() 通过 stateObject 参数可以添加任意类型的数据到记录中;而 hash 只可添加短字符串;
  • pushState() 可额外设置 title 属性供后续使用。
  • hash模式下,仅hash符号之前的url会被包含在请求中,后端如果没有做到对路由的全覆盖,也不会返回404错误;history模式下,前端的url必须和实际向后端发起请求的url一致,如果没有对用的路由处理,将返回404错误。

hash模式和history模式都有各自的优势和缺陷,还是要根据实际情况选择性的使用。

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

(1)param方式

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

1)路由定义

//在APP.vue中
<router-link :to="'/user/'+userId" replace>用户</router-link>    

//在index.js
{
   path: '/user/:userid',
   component: User,
},

2)路由跳转

// 方法1:
<router-link :to="{ name: 'users', params: { uname: wade }}">按钮</router-link

// 方法2:
this.$router.push({name:'users',params:{uname:wade}})

// 方法3:
this.$router.push('/user/' + wade)

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

(2)query方式

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

1)路由定义

//方式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
    }
  });
}
复制代码

2)跳转方法

// 方法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=' + jsmes)
复制代码

3)获取参数

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

5. Vue-router 路由守卫

有的时候,需要通过路由来进行一些操作,比如最常见的登录权限验证,当用户满足条件时,才让其进入导航,否则就取消跳转,并跳到登录页面让其登录。 为此有很多种方法可以植入路由的导航过程:全局的,单个路由独享的,或者组件级的

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

1. 全局路由钩子

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

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

三个参数:to,from,next

To:即将进入的路由对象(目标路由对象)

From:当前导航要离开的路由

Nex:next('/'):一个回调函数,一定要调用这个方法,不然路由不会继续往下走

具体使用∶

  • beforeEach(判断是否登录了,没登录就跳转到登录页)
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();  
    }
})
  • afterEach (跳转之后滚动条回到顶部)
router.afterEach((to, from) => {  
    // 跳转之后滚动条回到顶部  
    window.scrollTo(0,0);
});

2. 单个路由独享钩子

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

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

3. 组件内钩子

  • 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        
        }      
    })    
}

7.vue-router.4

▸ onBeforeRouteLeave(leaveGuard): void

添加一个导航守卫,不论当前位置的组件何时离开都会触发。类似于 beforeRouteLeave,但可以在任意组件中使用。当组件被卸载时,该守卫会被移除。

参数

名称类型描述
leaveGuardNavigationGuardNavigationGuard

返回值

void


onBeforeRouteUpdate

▸ onBeforeRouteUpdate(updateGuard): void

添加一个导航守卫,不论当前位置何时被更新都会触发。类似于 beforeRouteUpdate,但可以在任何组件中使用。当组件被卸载时,该守卫会被移除。

参数

名称类型描述
updateGuardNavigationGuardNavigationGuard

返回值

void

6. Vue路由钩子在生命周期函数的体现

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

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

集合:

(1)离开组件时的守卫 beforeRouteLeave

(2)全局前置守卫 router.beforeEach

(3)路由更新时的守卫 beforeRouteUpdate 

(4)调用独享守卫 beforeEnter

(5)进入组件前的守卫 beforeRouteEnter

(6)全局解析守卫router.beforeResolve

(7)全局后置钩子router.afterEach

(8)进入组件前的守卫 beforeRouteEnter

C3->A1->C2->B1->C1->A2->A3->C1

7. vue-router与location.href的用法区别

  1. vue-router使用pushState进行路由更新,静态跳转,页面不会重新加载;location.href会触发浏览器,页面重新加载一次
  2. vue-router使用diff算法,实现按需加载,减少dom操作
  3. vue-router是路由跳转或同一个页面跳转;location.href是不同页面间跳转;
  4. vue-router是异步加载this.$nextTick(()=>{获取url});location.href是同步加载

8. 路由组件

<router-link>: 实现路由之间的跳转,对应 html 中的a 标签,但是与a 标签不同的是,跳转的时候并不会刷新页面,
<router-view>: 是一个组件的名字,未来 url 匹配到对应的 path 时,对应的 component 就会被渲染到 <router-view></router-view> 标签中

9. 路由跳转的方法

1.  router-link

  1. 不带参数
<router-link :to="{name:'home'}">
<router-link :to="{path:'/home'}"> //name,path都行, 建议用name

注意:router-link中链接如果是'/'开始就是从根路由开始,如果开始不带'/',则从当前路由开始。

  1. 带参数
<router-link :to="{name:'home', params: {id:1}}">
  • params传参数 (类似post)
  • 路由配置 path: "/home/:id" 或者 path: "/home:id"
  • 不配置path ,第一次可请求,刷新页面id会消失
  • 配置path,刷新页面id会保留
  • html 取参 $route.params.id
  • script 取参 this.$route.params.id
<router-link :to="{name:'home', query: {id:1}}">
  • query传参数 (类似get,url后面会显示参数)
  • 路由可不配置
  • html 取参 $route.query.id
  • script 取参 this.$route.query.id

2.  this.$router.push() (函数里面调用)

  1. 不带参数
this.$router.push('/home')
this.$router.push({name:'home'})
this.$router.push({path:'/home'})
  1. query传参
this.$router.push({name:'home',query: {id:'1'}})
this.$router.push({path:'/home',query: {id:'1'}})
  • html 取参 route.query.id
  • script取参this.route.query.id
  • script 取参 this.route.query.id 
  • script取参this.route.query.id
  1. params传参
this.$router.push({name:'home',params: {id:'1'}}) // 只能用 name
  • 路由配置 path: "/home/:id" 或者 path: "/home:id" ,
  • 不配置path ,第一次可请求,刷新页面id会消失
  • 配置path,刷新页面id会保留
  • html 取参 $route.params.id
  • script 取参 this.$route.params.id
  1. query和params区别 query类似 get, 跳转之后页面 url后面会拼接参数,类似?id=1, 非重要性的可以这样传, 密码之类还是用params刷新页面id还在

params类似 post, 跳转之后页面 url后面不会拼接参数 , 但是刷新页面id 会消失 

3.  this.$router.replace() (用法同上,push)

4.  this.$router.go(n) ()

this.$router.go(n)

向前或者向后跳转n个页面,n可为正整数或负整数

区别

  • this.$router.push跳转到指定url路径,并想history栈中添加一个记录,点击后退会返回到上一个页面
  • this.$router.replace 跳转到指定url路径,但是history栈中不会有记录,点击返回会跳转到上上个页面 (就是直接替换了当前页面)
  • this.$router.go(n) 向前或者向后跳转n个页面,n可为正整数或负整数

10. params和query的区别

用法:query要用path来引入,params要用name来引入,接收参数都是类似的,分别是 this.$route.query.paththis.$route.params.name

url地址显示:query更加类似于ajax中get传参,params则类似于post,说的再简单一点,前者在浏览器地址栏中显示参数,后者则不显示

注意:query刷新不会丢失query里面的数据 params刷新会丢失 params里面的数据。