当前位置: 首页 > 工具软件 > Auto Rooter > 使用案例 >

Vue-router

有德业
2023-12-01

Vue-router


前端路由

Hash地址与组件之间的对应关系


前端路由的工作方式

用户点击了页面上的路由连接

导致了URL地址栏中的Hash值发生了变化

前端路由监听到了Hash地址的变化

前端路由吧当前Hash地址对应的组件渲染到浏览器中


前端路由的底层原理

–>通过浏览器的onhashchange事件监听Hash值的变化

在生命周期函数created()于组件生成时就立即用onhashchange事件监听Hash值的变化

再通过location.hash获取浏览器URL地址栏中的Hash值,根据Hash值对应修改data中的值

然后通过component标签is属性读取data中的对应值动态渲染组件





Vue-router的配置


vue-router是官方的路由解决方案,只能结合vue项目使用,实现管理SPA(单页面)项目中的组件切换

安装vue-router包

npm i vue-router@3.5.2 -S

创建路由模块

// src源代码目录下,新建router/index.js路由模块

// 导入 Vue和 VueRouter的包
import Vue from 'vue'
import VueRouter from 'vue-router'

// 调用 Vue.use() 函数,把 VueRouter 安装为 Vue 插件
Vue.use(VueRouter)

// 创建路由的实例对象
const router = new VueRouter({
	routes, 				// 路由数组 
	mode: 'history',		// mode为路由模式 history历史路由 hash 哈希路由
	base: 'app'	,			// 根路径 在项目所有路由路径前会默认加上根路径app
	scrollBehavior(to, from, position){
		// 滚动行为 记录浏览器的滚动记录 (只能记录根元素 无实际用处)
	}
})


// 向外共享路由的实例对象
export default router


导入并挂载路由模块

// main.js中

// 导入路由模块,为了拿到路由的实例对象
//在进行模块导入时,如果给定的是文件夹,则默认导入这个文件夹下的index.js,所以可以省略后面
import router from '@/router'

new Vue({
  render: h => h(App),
  //在Vue项目中,要想把路由用起来,必须把路由的实例对象,通过下面的方式进行挂载
  // router: 路由的实例对象
  router
}).$mount('#app')

声明路由链接和占位符

占位符

    <router-view></router-view>

只要在项目中安装和配置了vue-router,就可以使用router-view这个组件

是一个单纯的占位符,作用类似component,将对应的组件渲染到其中

路由链接

// 在src下的router/index.js路由模块

const router = new VueRouter({
	//routes是一个数组,作用:定义 "hash地址" 与 "组件"之间的对应关系
  routes: [
    // 路由规则
    // 路径省略#,会自动解析
    { path: '/1', component: one }, //component为需要展示在router-view标签中的组件
    { path: '/2', component: two },
    { path: '/3', component: three }
  ]
})

跳转标签

    <a href="#/3">组件3</a> <!-- old -->
    
    <router-link to="/3">组件3</router-link>  <!-- new -->
    
    <!-- 支持location对象写法 v-bind绑定 -->
    <router-link :to="{ path: '/3' }">组件3</router-link>  
    
	<!-- path路径跳转时不能用params携带参数 因为params参数就在path的值上面 -->
    <router-link :to="{ path: '/3', params: { id:456, c:789 }}">组件3</router-link> <!-- x 无效传参! -->

	<!-- path路径跳转时只能用query携带参数 -->
    <router-link :to="{ path: '/3', query: { id:456, c:789 }}">组件3</router-link>
    
	<!-- name名称跳转时可以用params携带参数 但路由必须配置动态参数 否则刷新会丢失 -->
    <router-link :to="{ name: 'three', params: { id:456, c:789 }}">组件3</router-link>

无论是name还是path的形式来指定params参数 在跳转之后的页面刷新之后都是只能得到访问url上面的对应的动态参数

当安装和配置了vue-router后,就可以使用router-link来替代普通的a链接(浏览器中会解析成a标签)

router-link属性

to属性与herf属性效果相同,且不需要加#,会自动解析

replace属性 替换路由栈中的页面

当跳转组件与router-link相匹配时,router-link会添加route-link-active的类名





Vue-router的使用


路由重定向

路由重定向是当用户访问地址A的时候,强制用户跳转到地址C,从而展示特定的组件页面

通过路由规则的redirect属性,指定一个新的路由地址,实现路由的重定向

// 在src下的router/index.js路由模块

import one from '@/components/1.vue'
import two from '@/components/2.vue'
import three from '@/components/3.vue'

const router = new VueRouter({
	// 在routes 数组中,声明路由的匹配规则
  routes: [
    // 当用户访问 / 的时候,通过redirect 属性跳转到 /1 对应的路由规则
    { path: '/', redirect: '/1' },
    { path: '/1', component: one },
    { path: '/2', component: two },
    { path: '/3', component: three }
  ]
})


嵌套路由

通过路由实现组件的嵌套展示,叫嵌套路由

点击父级路由链接显示模板内容,模板内容中又有子级路由链接

点击子级路由链接需要显示子级模板内容

    <!-- 在组件3模板中 -->
    <!-- 子级路由链接 -->
    <router-link to="/3/tab1">tab1</router-link>
    <router-link to="/3/tab2">tab2</router-link>
    
    <!-- 子级路由占位符 -->
    <router-view></router-view>
// 在src下的router/index.js路由模块
// 导入需要的模块并使用children属性声明子路由规则
import Tab1 from '@/components/tabs/Tab1.vue'
import Tab2 from '@/components/tabs/Tab2.vue'

const router = new VueRouter({
  routes: [
    // 路由规则
    { path: '/', redirect: '/1' },
    { path: '/1', component: one },
    { path: '/2', component: two },
    {	// three页面的路由规则(父级路由规则)
      path: '/3',
      component: three,
      children: [ //通过children属性,嵌套声明子级路由规则
      
      	//子路由路径不需要加/
      	
        { path: 'tab1', component: Tab1 }, //访问/3/tab1时展示Tab1组件
        { path: 'tab2', component: Tab2 }  //访问/3/tab2时展示Tab2组件
      ]
    }
  ]
})



命名路由

在设置路由规则时可以设置name属性用于跳转

// 在src下的router/index.js路由模块

const router = new VueRouter({
	//routes是一个数组,作用:定义 "hash地址" 与 "组件"之间的对应关系
  routes: [
    // 路由规则
    // 路径省略#,会自动解析
    { path: '/1', component: one, name:'1' }, //component为需要展示在router-view标签中的组件
    { path: '/2', component: two, name: '2'},
    { path: '/3', component: three, name:'3'}
  ]
})
    <router-link :to="{name: '3'}">组件3</router-link>  <!-- 使用name属性跳转需要v-bind -->



路由别名

在设置路由规则时可以设置alias别名同样可以进行跳转

设置路由别名时必须携带 ‘/’ ,当访问路由别名的路径时会加载对应的组件

// 在src下的router/index.js路由模块

const router = new VueRouter({
	//routes是一个数组,作用:定义 "hash地址" 与 "组件"之间的对应关系
  routes: [
    // 路由规则
    // 路径省略#,会自动解析
    { path: '/1', component: one, name:'1', alias:['/n','/c']}, 
    	// 可以设置为数组或单个值
    { path: '/2', component: two, name: '2', alias:'/abc'},
    { path: '/3', component: three, name:'3', alias:'/u/:id'}
    	// 可以动态路由配合使用
  ]
})



默认子路由

当需要页面默认显示其中一个子路由时

1.通过路由重定向

 {	// three页面的路由规则(父级路由规则)
      path: '/3',
      component: three,
      redirect: '/3/tab1'
      children: [ //通过children属性,嵌套声明子级路由规则
        { path: 'tab1', component: Tab1 }, //访问/3/tab1时展示Tab1组件
        { path: 'tab2', component: Tab2 }  //访问/3/tab2时展示Tab2组件
      ]
    }

当显示3组件时重定向到/3/tab1显示tab1组件

2.空路径

通过设置空路径也能实现

如果children数组中,某个路由规则的path为空字符串,则这条路由规则叫做默认子路由

 {	// three页面的路由规则(父级路由规则)
      path: '/3',
      component: three,
      redirect: '/3/tab1'
      children: [ //即显示3组件时默认显示Tab1
        { path: '', component: Tab1 }, 
        { path: 'tab2', component: Tab2 } 
      ]
}

对应的组件中的子路由链接也需要修改

    <!-- 在组件3模板中 -->
    <!-- 子级路由链接 -->
    <router-link to="/3">tab1</router-link>
    <router-link to="/3/tab2">tab2</router-link>
    
    <!-- 子级路由占位符 -->
    <router-view></router-view>


路由元信息meta

简单来说就是路由元信息,也就是每个路由身上携带的信息。

meta的作用是通过meta对象中的一些属性来判断当前路由是否需要进一步处理

const router = new VueRouter({
  routes: [
    {
      path: '/3',
      component: three,
      children: [
        {
          path: 'tab1',
          component: Tab1,
          meta: { isRecord: true }
        }
      ]
    }
  ]
})

根据上面的路由配置,/3/tab1 这个 URL 将会匹配父路由记录以及子路由记录。

一个路由匹配到的所有路由记录会暴露为 $route 对象 (还有在导航守卫中的路由对象) 的 $route.matched 数组。因此需要遍历 $route.matched 来检查路由记录中的 meta 字段。

全局守卫检索元字段

router.beforeEach((to, from, next) => {
  if (to.matched.some(record => record.meta.requiresAuth)) {
    // this route requires auth, check if logged in
    // if not, redirect to login page.
    if (!auth.loggedIn()) {
      next({
        path: '/login',
        query: { redirect: to.fullPath }
      })
    } else {
      next()
    }
  } else {
    next() // 确保一定要调用 next()
  }
})



动态路由

通过this.$router.addRoutes()可以实现动态添加路由数组

this.$router.addRoutes([
	{
		name: 'p1',
		path: '/p1',
		component: P1,
		beforeEnter: (to, from, next) => {
  			  // ...
		},
		meta: {
			auth: true
		}
	},
	{
		name: 'p2',
		path: '/p2',
		component: P2,
		beforeEnter: (to, from, next) => {
  			  // ...
		},
		meta: {
			auth: false
		}
	}
])

可以在原有的路由数组中通过方法新增路由数组,来实现多个路由的添加

this.$router.addRoutes()在3的路由版本中已废弃,但仍能使用。在4的路由版本中已不能使用

在4的路由版本中使用this.$router.addRoute()进行代替,只能添加一个路由规则,如果该路由规则有name属性,

并且已经存在一个与之相同的名字,则会进行覆盖

this.$router.addRoute({
		name: 'p1',
		path: '/p1',
		component: P1,
		beforeEnter: (to, from, next) => {
  			  // ...
		},
		meta: {
			auth: true
		}
	})



动态路由参数


当有模块下有子路径时就需要动态路由参数匹配

    <router-link to="/movie/1">电影1</router-link>
    <router-link to="/movie/2">电影2</router-link>
    <router-link to="/movie/3">电影3</router-link>

此时路由为/movie/1,与/movie不匹配就不能显示movie组件,要显示movie组件就需要配置路由规则

    { path: '/movie/1', component: Movie },
    { path: '/movie/2', component: Movie },
    { path: '/movie/3', component: Movie }

此时路由规则的复用性差


动态路由参数概念

把Hash地址中可变的部分定义为参数项,提高路由规则的复用性

在vue-router中使用( : )来定义路由参数项

如果没有定义参数项就使用this.$router跳转且携带params参数时 params参数刷新就会丢失

	//路由中的动态参数以 : 进行声明,冒号后面时动态参数的名称
    { path: '/movie/:id', component: Movie }

当需要用到后面的参数id时,通过this.$route.params.参数名 来获取

参数在vue实例中的$route对象中的params对象中

	<!-- this.$route 是路由的 参数对象 -->
	<!-- this.$router 是路由的 导航对象 -->
	<h3>{{ $route.params.id }}</h3>
	<!-- 在组件模板中使用vue实例中的属性可以不加this -->

props获取数据

通过 this.$route.params.参数名 来获取参数比较麻烦

可以通过props属性直接把参数赋值到props中

	//可以为路由规则开启 props 传参,从而方便的拿到动态参数的值
    { path: '/movie/:id', component: Movie,props: true }

组件中也要定义对应的props数据来接收

export default {
  name: 'app',
  props: ['id']
}

其他

注意1: 在hash地址中,/后面的参数项,叫做 路径参数
在路由 参数对象 中,需要使用 this.$route.params 来访问路径参数

注意2: 在hash地址中, ? 后面的参数项,叫做 查询参数
在路由 参数对象 中,需要使用 this.$route.query 来访问查询参数

注意3: 在 this.$route 中,path 只是路径部分; fullPath 是完整的地址
eg:
/movie/2?name=zs&age=20 是 fullPath 的值
/movie/2 是 path 的值



声明式导航&编程式导航


在浏览器中,点击链接实现导航的方式,叫做声明式导航

普通网页中点击a链接,vue项目中点击router-link都属于声明式导航

在浏览器中,调用API方法实现导航方法,叫做编程式导航

普通网页中调用location.href跳转到新页面的方式,属于编程式导航

vue-router中的编程式导航API

// 跳转到指定的hash地址,并增加一条历史记录
// 参数都是location对象或者是字符串

 this.$router.push('hash 地址')
 this.$router.push({ name: '3', params: { userId: 123 }}) // 命名式路由的跳转
// 跳转到指定的hash地址,并替换掉当前的历史记录

 this.$router.replace('hash 地址')
// 实现导航历史前进后退

 this.$router.go(数值 n)
// go(-1)表示后退一层 如果后退的层数超过上限,则原地不动
// 在历史记录中,后退到上一个页面

 this.$router.back()
// go(-1)表示后退一层 如果后退的层数超过上限,则原地不动
// 在历史记录中,前进到下一个页面

 this.$router.forward()
// go(-1)表示后退一层 如果后退的层数超过上限,则原地不动



全局守卫

导航守卫可以控制路由的访问权限,强迫路由跳转

全局前置守卫

每次发生路由的导航跳转时,都会触发全局前置守卫

因此在全局前置守卫中,可以对每个路由进行访问权限的控制

// 于router/index.js创建路由实例对象
const router = new VueRouter({...})

// 调用路由实例对象的beforeEach方法声明全局前置守卫
// 每次发生路由导航跳转时,都会触发 fn 回调
router.beforeEach(fn)

function fn (to, from, next) {
  // to 是将要访问的路由的信息对象
  // from 是将要离开的路由的信息对象
  // next 是一个函数,调用 next() 表示放行,允许这次路由导航
  next()
  //声明函数中先加 next() 如果不加next()则所有路由均被拦截
}

next( )函数的调用

next() 直接放行

next(‘/login’) 强制跳转到登录路由

next(false) 不允许跳转

当next跳转且需要携带参数时则需要传参一个对象

next({	
		//跳转路由为login且携带query参数
        path: '/login',
        query: { redirect: to.fullPath }
      })

全局后置钩子

// 于router/index.js创建路由实例对象
const router = new VueRouter({...})

// 调用路由实例对象的afterEach方法声明全局后置钩子
// 每次发生路由导航跳转后,都会触发 fn 回调
router.afterEach(fn)

function fn (to, from) {
  // to 是将要访问的路由的信息对象
  // from 是将要离开的路由的信息对象
}

全局解析守卫

router.beforeResolve注册一个全局解析守卫,和router.beforeEach类似,区别是在导航被确认之前

同时在所有组件内守卫和异步路由组件被解析之后,解析守卫就会被调用


局部守卫(组件内守卫)

export default {
  data(){
  	return { }
  },
  beforeRouteEnter (to, from, next) {
    // 在渲染该组件的对应路由被 confim 前调用
    // 不能获取组件实例'this'
    // 因为当守卫执行前,组件实例还没被创建
  },
  beforeRouteUpdate(to, from, next) {
    // 在当前路由改变,但是该组件被复用时调用
    // 举例来说,对于一个带有动态参数的路径 /3/:id 在/3/1和/3/2 之间跳转时
    // 由于会渲染同样的 3 组件,因此组件实例会被复用,而这个钩子就会在这种情况下被调用
    // 可以访问组件实例'this'
  },
  beforeRouteLeave (to, from, next) {
    // 导航离开该组件的对用路由时调用
    // 可以访问组件实例'this'
  }
}

beforeRouteUpdate可以监听同组件的路由参数的变化,但在2.2新增

在旧版本中需要监听同组件的路由参数的变化,只能通过侦听器watch实现

// 如果当前路由跳转到当前路由仅仅是参数发生了变化,不会引起created和mounted的生命周期执行
// 也不会触发 beforeRouterEnter再次执行

watch: {
	$route(nv){
		console.log(nv)
	}
}



独享守卫

const router = new VueRouter({
  routes: [
    { path: '/1', component: one }, 
    {
 		path: '/2',
  		component: two,
  		beforeEnter: (to, from, next) => {
   			 // 声明在路由配置里的 独享守卫 在进入此路由之前会触发
  		}
	}
  ]
})

权限路由

用全局前置守卫可以在用户访问需要权限的路由时进行处理

当需要权限访问的路由过多时,可以声明为一个数组,封装为一个json文件

然后用路由地址与数组进行比较,判断是否为需要处理

//路由封装为json文件中的数组pathArr
//to为全局前置守卫中的需要跳转到的路由 拿到路径path后进行检索
pathArr.indexOf(to.path) !== -1
//则为需要权限才能访问的路由





路由切换时记录滚动条信息

使用keepalive组件使路由内的组件缓存,防止刷新重新发送请求获取数据

监听元素的滚动条滚动事件,将滚动的距离存入sessionStorage中(防抖)

当重新进入页面时设置元素的滚动距离

使用lodash类库进行操作

<div class="sc" style="height: 80vh;overflow-y: auto" ref="sc" @scroll="fn1">
    <!-- ... -->
</div>
  // 使用 lodash 实现防抖 获取最后一次执行的结果 存入sessionStorage中
methods: {
	fn1: _.debounce((ev) => {
      console.log(ev.target.scrollTop)
      window.sessionStorage.scheight = ev.target.scrollTop;
   }, 200)
},
// 在activated生命周期函数 在组件激活时 获取到sessionStorage中存入的滚动距离 设置为元素的滚动距离
activated() {
    const { scheight } = window.sessionStorage;
    if (scheight) {
      this.$refs.sc.scrollTop = window.sessionStorage.scheight;
    }
 }

 类似资料: