VueRouter基本使用以及底层原理理解

653 阅读6分钟

前言

本文整理了VueRouter的基本使用以及底层原理理解相关知识总结,如果对答案有不一样见解的同学欢迎评论区补充讨论,当然有问题,也欢迎在评论区指出。

一、vueRouter基本使用

从上到下匹配路由,匹配不到路由就会报错,所以一般会在最后一个路由设置为通配符路由。

1、路由懒加载( 动态加载路由 )

(一)未使用懒加载
import Vue from 'vue';
import Router from 'vue-router';
import HelloWorld from '@components/HelloWorld';
Vue.use(Router);
export default new Router({ 
    routes:[
        {    
            path:'./',
            name:'HelloWorld',
            component:HelloWorld,
            //路由的元信息
            meta:{keepAlive:true}
        }
    ]
})
(二)使用import异步引入组件

有时候我们想把某个路由下的所有组件都打包在同个异步块 (chunk) 中。只需要使用 命名 chunk,一个特殊的注释语法来提供 chunk name (需要 Webpack > 2.4)。

// 下面2行代码,没有指定webpackChunkName,每个组件打包成一个js文件。
/* const Home = () => import('@/components/home')
const Index = () => import('@/components/index')
const About = () => import('@/components/about') */
// 下面2行代码,指定了相同的webpackChunkName,会合并打包成一个js文件。 把组件按组分块
const Home =  () => import(/* webpackChunkName: 'ImportFuncDemo' */ '@/components/home')
const Index = () => import(/* webpackChunkName: 'ImportFuncDemo' */ '@/components/index')
const About = () => import(/* webpackChunkName: 'ImportFuncDemo' */ '@/components/about')
​
// 打包的webpackChunkName的js文件
{
  path: '/about',
  component: About
}, {
  path: '/index',
  component: Index
}, {
  path: '/home',
  component: Home
}
(三)使用require异步引入组件
{
  path: '/home',
  name: 'home',
  component: resolve => require(['@/components/home'],resolve)
},{
  path: '/index',
  name: 'Index',
  component: resolve => require(['@/components/index'],resolve)
},{
  path: '/about',
  name: 'about',
  component: resolve => require(['@/components/about'],resolve)
} 
(四)使用webpack提供的require.ensure()异步引入组件
//语法:require.ensure(dependencies: String[], callback: function(require), chunkName: String)
{
  path: '/home',
  name: 'home',
  component: r => require.ensure([], () => r(require('@/components/home')), 'demo')
}, {
  path: '/index',
  name: 'Index',
  component: r => require.ensure([], () => r(require('@/components/index')), 'demo')
}, {
  path: '/about',
  name: 'about',
  component: r => require.ensure([], () => r(require('@/components/about')), 'demo-01')
}

2、router-link和router-view组件使用

//App.vue
<template>
  <div id="app">
    <div id="nav">
        <!-- 使用 router-link 组件来导航. -->
        <!-- 通过传入 `to` 属性指定链接. -->
        <!-- <router-link> 默认会被渲染成一个 `<a>` 标签 -->
      <router-link to="/home">Home</router-link> |
      <router-link to="/about">About</router-link> | 
      <router-link :to="{ name: 'user', params: { userId: 123 }}">User</router-link>
    </div>
    <!-- 路由出口 -->
    <!-- 路由匹配到的组件将渲染在这里 -->
    <router-view/>
  </div>
</template>

3、this.$router和this.$route使用

在任何组件内可以通过 this.$router 访问路由器,也可以通过 this.$route 访问当前路由

$router是VueRouter的实例,在script标签中想要导航到不同的URL

  • vue组件内(el-menu)切换路由
//导航栏跳转
<el-menu
     :default-active="this.$route.path" 
     router mode="horizontal"
</menu>
  • this.$router.push()切换路由的两种方式
this.$router.push({
    path:'/fillinformation',
    query: {
        applicationNo: this.applicationNo,
        contractNo:this.contractNo
    }
})
this.$route.query.applicationNo//页面跳转后获取携带参数applicationNo参数
//此用法参数会展示在跳转地址上
this.$router.push({
    name: 'clientdetail',
    params: {
        clientCode: clientCode,
        clientType: clientType
    }
})
 this.$route.params.clientCode//页面跳转后获取携带参数clientCode
//此用法参数不会展示在跳转地址
  • this.$router.replace({path:'home'});//替换路由,没有历史记录
  • this.$router.go(-1),在 history 记录中向前或者后退多少步,类似 window.history.go(n)

$route为当前router跳转对象。可以从中获取当前路由的name,path,query,parmas等

  • $route.path 字符串,等于当前路由对象的路径,会被解析为绝对路径,如 "/home/news"
  • $route.params 对象,包含路由中的动态片段和全匹配片段的键值对
  • $route.query对象,包含路由中查询参数的键值对。例如,对于 /home/news/detail/01?favorite=yes ,会得到$route.query.favorite == 'yes'
  • $route.router 路由规则所属的路由器(以及其所属的组件)。
  • $route.matched 数组,包含当前匹配的路径中所包含的所有片段所对应的配置参数对象。
  • $route.name 当前路径的名字,如果没有使用具名路径,则名字为空。

4、捕获所有路由或 404 Not found 路由(处理页面)

常规参数只会匹配被 / 分隔的 URL 片段中的字符。如果想匹配任意路径,我们可以使用通配符 (*),匹配所有路径:

{
  // 会匹配以 `/user-` 开头的任意路径
  path: '/user-*'
}
{
  // 会匹配所有路径,优先级从上到下查找路由,都没有的时候全部指向 404页面
  path: '*',
  redirect:"/404"
}

以上这种找不到页面,重定向到404页面,有个严重缺陷:

  • 缺点:如果项目中添加了判断token失效跳出登录然后记住当前页面,登录时候自动跳转到上次token失效的页面,就会有问题,如果故意输入不存在的路由就会在404,失效退出然后登陆后就会自动跳到这个404页面,那样就尴尬了;如果没有这个功能,用上述方法还是比较好使的

解决方法:

  • 在index.js文件中注释刚刚加入的path:* 这些东西
  • 在router.beforeEach 里面使用 to.matched 匹配出的路由个数来作为判断条件,匹配不到路由就跳转到404页面

pemmision.js

import router from './router'
import { getCookie } from './utils/auth'

// 通过beforeEach钩子来判断用户是否登陆过 有无token
const whiteList = ['/login'] // 不重定向白名单
// const userInfo = getUserInfo()

router.beforeEach((to, from, next) => {
    console.log(to.matched)
    // 判断是否有登录过
    if (getCookie('userId_dev')) {
        // 如果包含userId_dev 从登录页面跳转 直接跳转到首页 /
        if (to.path === '/login') {
            next()
        } else {
            if (to.matched.length === 0) {
                next('/404') // 判断此跳转路由的来源路由是否存在,存在的情况跳转到来源路由,否则跳转到404页面
            }
            next() // 如果匹配到正确跳转
        }
        // 没有登录
    } else {
        if (whiteList.indexOf(to.path) !== -1) {
            next()
        } else {
            // 还没有登录过 则跳转到登录界面
            // next('/login')
            if (to.path.slice(1) !== '') {
                if (to.matched.length === 0) {
                    router.push({
                        path: '/login'
                    })
                } else {
                    router.push({
                        path: '/login',
                        query: {
                            redirect: to.path.slice(1)
                        }
                    })
                }
            } else {
                router.push({
                    path: '/login'
                })
            }
        }
    }
})

5、重定向

重定向也是通过 routes 配置来完成,下面例子是从 /a 重定向到 /b

const router = new VueRouter({
  routes: [
    { path: '/a', redirect: '/b' }
  ]
})

重定向的目标也可以是一个命名的路由:

const router = new VueRouter({
  routes: [
    { path: '/a', redirect: { name: 'foo' }}
  ]
})

甚至是一个方法,动态返回重定向目标:

const router = new VueRouter({
  routes: [
    { path: '/a', redirect: to => {
      // 方法接收 目标路由 作为参数
      // return 重定向的 字符串路径/路径对象
    }}
  ]
})

6、导航守卫

完整的导航解析流程

  1. 导航被触发。
  2. 在失活的组件里调用 beforeRouteLeave 守卫。
  3. 调用全局的 beforeEach 守卫。
  4. 在重用的组件里调用 beforeRouteUpdate 守卫 (2.2+)。
  5. 在路由配置里调用 beforeEnter
  6. 解析异步路由组件。
  7. 在被激活的组件里调用 beforeRouteEnter
  8. 调用全局的 beforeResolve 守卫 (2.5+)。
  9. 导航被确认。
  10. 调用全局的 afterEach 钩子。
  11. 触发 DOM 更新。
  12. 调用 beforeRouteEnter 守卫中传给 next 的回调函数,创建好的组件实例会作为回调函数的参数传入。

7、路由过渡动效

看官方router.vuejs.org/zh/guide/ad…

二、vueRouter底层原理

在使用 Vue 等前端渲染时,通常会有 hash 路由和 history 路由两种路由方式。其实现方式也有所不同

  1. hash 路由:监听 url 中 hash 的变化,然后渲染不同的内容,这种路由不向服务器发送请求,不需要服务端的支持;
  2. history 路由:监听 url 中的路径变化,需要客户端和服务端共同的支持;

1、hash 路由

当页面中的 hash 发生变化时,会触发 hashchange 事件,因此我们可以监听这个事件,来判断路由是否发生了变化,如果变化了,调用onHashChange函数,实现页面的切换

window.addEventListener('hashchange', () => {
    history.onHashChange()
})

针对于 HTML5History 和 HashHistory 特殊处理,因为在这两种模式下有可能存在进入时候的不是默认页,需要根据当前浏览器地址栏里的 path 或者 hash 来激活对应的路由,此时就是通过调用 transitionTo 来达到目的

history.transitionTo(getHash(), () => {
    window.addEventListener('hashchange', () => {
        history.onHashChange()
    })
})

2、history 路由

在 history 路由中,我们一定会使用 window.history 中的方法,常见的操作有:

  • back():后退到上一个路由
  • forward():前进到下一个路由,如果有的话
  • go(number):进入到任意一个路由,正数为前进,负数为后退
  • pushState(obj, title, url):前进到指定的 URL,不刷新页面
  • replaceState(obj, title, url):用 url 替换当前的路由,不刷新页面

popstate可监听路由变化

  • pushState 和 replaceState 被调用时,是不会触发触发popstate 事件
  • 前三个方法会触发popstate事件,实现页面的切换

调用以上几种方式时,仅修改页面的 URL,而不发送请求

location.href 和 location.replace 切换时要向服务器发送请求

总结

觉得写得好的,对你有帮助的,可以分享给身边人,知识越分享越多,千万不要吝啬呀

后续更新vue底层相关原理讲解,请关注我,整理好,分享给你们,我们一起学前端