若依Vue2.0框架学习之路由跳转

发布时间 2023-08-30 15:19:49作者: 一米五怎么你了

免责声明

  • 新项目在若依前后端分离(Vue2.6.x + Springboot2.2.x)模板基础上进行项目开发,苯人真得学学模板吧!理解错误概不负责,毕竟ko no 菜鸡一枚 da。
  • 我们幼儿园有适合自己看的注释拉满文档!
  • 本篇只涉及路由跳转,什么登录流程和权限管理先往后稍稍。

0. next()

前情提要:假设当前导航是从 A 到 B,新的导航是从 A 到 C。

0.1 next()

放行,允许从 A 到 B。

0.2 next(false)

不仅中断当前导航,也没有新的导航。

0.3 next('/xxx') 或 next({ path:'/xxx',... })

中断当前导航,执行新的导航;即又会进入一次前置守卫。

0.4 next(error) 2.4.0+

如果传入 next 的参数是一个 Error 实例,则导航会被终止且该错误会被传递给 router.onError() 注册过的回调。(船新的api)

1. Vue.config.js 项目启动

const port = process.env.port || process.env.npm_config_port || 80 // 端口

// 省略版
module.exports = {
// ...
  devServer: {
    host: '0.0.0.0',
    port: port,
    // ...
  }
}

项目启动 http://localhost:80/

2. 路由跳转

2.1 router/index.js 路由配置

关注根路径的重定向

// ==== 省略版 ====

import Vue from 'vue'
import Router from 'vue-router'

Vue.use(Router)
/* Layout */
import Layout from '@/layout'

// 公共路由
export const constantRoutes = [
  {
    path: '/redirect',
    component: Layout,
    hidden: true,
    children: [
      {
        path: '/redirect/:path(.*)',
        component: () => import('@/views/redirect')
      }
    ]
  },
  {
    path: '/login',
    component: () => import('@/views/login'),
    hidden: true
  },
  {
    path: '/404',
    component: () => import('@/views/error/404'),
    hidden: true
  },
  // ''也可以匹配上'/'的
  {
    path: '',
    component: Layout,
    // 重定向到子路由
    redirect: 'index',
    children: [
      {
        path: 'index',
        component: () => import('@/views/index'),
        name: 'Index',
        meta: { title: '首页', icon: 'dashboard', affix: true }
      }
    ]
  },
]

export default new Router({
  mode: 'history', // 去掉url中的#
  routes: constantRoutes
})

2.2 路由前置守卫

2.2.1 未登录(cookie 中无 token),直接访问非白名单内某配置了的页面,会跳转到 login 页

permission.js
关注 1. 跳转到了login页 2. fullPath为 /login?redirect=/xxx

import router from './router'
import store from './store'
import { Message } from 'element-ui'
import NProgress from 'nprogress'
import 'nprogress/nprogress.css'
import { getToken } from '@/utils/auth'
import { isRelogin } from '@/utils/request'

NProgress.configure({ showSpinner: false })
// 只关心路由前置守卫以及路由跳转捏
const whiteList = ['/login', '/register']
// 0. 项目启动 http://localhost:80/ 在router.js 中配置了,重定向到子路由 index,导航是从 / 到 /index
router.beforeEach((to, from, next) => {
  NProgress.start()
  if (getToken()) {
	// 略
  } else {
    // a 1. 没有token 3. 没有token
    if (whiteList.indexOf(to.path) !== -1) {
     // a 4. to.path == /login 在白名单
	 // a 5. next() 跳转到 login 页,fullPath 为 /login?redirect=/index 至此 finished
      next()
    } else {
      // 2. 此时 to 路由对象的 path 是 /index 不在白名单 
      next(`/login?redirect=${to.fullPath}`) // 否则全部跳转到登录页,且此处参数redirect是为了后面点击登录验证成功后直接跳转到本来想访问的页面
      // 新的 to 路由对象的 fullPath 为 /login?redirect=/index
      NProgress.done()
    }
  }
})

router.afterEach(() => {
  NProgress.done()
})

2.2.2 接上续,点击登录通过验证,跳转到原目标页,这里以 index 举例

views/login.vue

<script>
name: "Login",
data() {
	return {
	  // ...
	  redirect: undefined
	};
},
watch: {
//  经过重定向 + 守卫,此时的 $route: {  path:"/login",query: {redirect: "/index"},fullpath: "/login?redirect=%2Findex",...}
	$route: {
	  handler: function(route) {
		this.redirect = route.query && route.query.redirect;
	  },
	  immediate: true
	}
},

handleLogin() {
  this.$refs.loginForm.validate(valid => {
	if (valid) {
	// ...
	  this.$store.dispatch("Login", this.loginForm).then(() => {
	  // 暂时只关注这一行,点击登录按钮,验证成功后,跳转到 index.vue to : { path : /index }
		this.$router.push({ path: this.redirect || "/" }).catch(()=>{});
	  }).catch(() => {
		this.loading = false;
		if (this.captchaEnabled) {
		  this.getCode();
		}
	  });
	}
  });
}
</script>

permission.js

import router from './router'
import store from './store'
import { Message } from 'element-ui'
import NProgress from 'nprogress'
import 'nprogress/nprogress.css'
import { getToken } from '@/utils/auth'
import { isRelogin } from '@/utils/request'

NProgress.configure({ showSpinner: false })
// 只关心路由前置守卫捏
const whiteList = ['/login', '/register']
// 0. 接上文,此时 to : {path : /index}
router.beforeEach((to, from, next) => {
  NProgress.start()
  if (getToken()) {
  // b 1. 有token 4. 有token
    to.meta.title && store.dispatch('settings/setTitle', to.meta.title)
    if (to.path === '/login') {
      next({ path: '/' })
      NProgress.done()
    } else {
      // 判断当前用户是否已拉取完 user_info 信息(因为即使没有也会赋一个默认的,所以只要是 0 肯定没拉)
      // b 2. 没拉取完,先发送请求获取(这里涉及 vuex 和权限,不展开)
      if (store.getters.roles.length === 0) {
        isRelogin.show = true
        store.dispatch('GetInfo').then(() => {
          isRelogin.show = false
          store.dispatch('GenerateRoutes').then(accessRoutes => {
            router.addRoutes(accessRoutes) 
            // b 3. fulfilled 
            // 此时新的路由为 { path: '/index',replace :true} 这里的 replace属性使得当前的push方法变成像this.$router.replace一样 不会向 history 添加新纪录
            next({ ...to, replace: true }) 
          })
        }).catch(err => {
            // rejected
            store.dispatch('LogOut').then(() => {
              Message.error(err)
              // 登出后,此时 cookie 里的 token 已清除;因此同2.2.1
              // 但是这里为什么不直接写 next({ path: '/login' }) ???
              next({ path: '/' })
            })
          })
      // b 5. 拉取完,直接放行 { path: '/index',replace :true } 跳转到原目标页
      } else {
        next()
      }
    }
  } else {
    if (whiteList.indexOf(to.path) !== -1) {
      next()
    } else {
      next(`/login?redirect=${to.fullPath}`) 
      NProgress.done()
    }
  }
})

router.afterEach(() => {
  NProgress.done()
})

2.2.3 已登录(cookie 中有 token),再访问 login 页,会重定向到 index 页

import router from './router'
import store from './store'
import { Message } from 'element-ui'
import NProgress from 'nprogress'
import 'nprogress/nprogress.css'
import { getToken } from '@/utils/auth'
import { isRelogin } from '@/utils/request'

NProgress.configure({ showSpinner: false })

const whiteList = ['/login', '/register']

router.beforeEach((to, from, next) => {
  NProgress.start()
  if (getToken()) {
  // c 有 token 再访问 login 页 1. has token 3. has token 6. has token
    to.meta.title && store.dispatch('settings/setTitle', to.meta.title)
    if (to.path === '/login') {
    // c 2. 访问 login 页 匹配上''会重定向,直接改变了to,因此5.的path是 /index
      next({ path: '/' })
      NProgress.done()
    } else {
      // 判断当前用户是否已拉取完 user_info 信息(因为即使没有也会赋一个默认的,所以只要是0肯定没拉,好吧这里也涉及权限,暂时不用管)
      // c 4. 因为直接改变url模拟 肯定会刷新页面,因此走没拉取完 
      // 注: user_info 存在 vuex 里,vue本质是单页面, 路由的跳转不会刷新页面,但重新输入url会
      if (store.getters.roles.length === 0) {
        isRelogin.show = true
        store.dispatch('GetInfo').then(() => {
          isRelogin.show = false
          store.dispatch('GenerateRoutes').then(accessRoutes => {
            router.addRoutes(accessRoutes) 
            // c 5. to: { path: '/index' }
            next({ ...to, replace: true }) 
          })
        }).catch(err => {
            store.dispatch('LogOut').then(() => {
              Message.error(err)
              next({ path: '/' })
            })
          })
      } else {
        // 7. 拉取完了 { path: '/index', replace: true } 放行
        next()
      }
    }
  } else {
    if (whiteList.indexOf(to.path) !== -1) {
      next()
    } else {
      next(`/login?redirect=${to.fullPath}`) 
      NProgress.done()
    }
  }
})

router.afterEach(() => {
  NProgress.done()
})