您现在的位置是:首页 >其他 >vue-router使用网站首页其他

vue-router使用

m0_45127388 2023-05-28 00:00:03
简介vue-router使用

路由:目的就是将组件映射到路由上,路由知道哪里渲染组件,更方便创建单页面引用(更新视图但不需要重新请求页面)
一、vue3.x及更低版本
二、vue4.x(适用于vue3项目中)
(1)安装路由包
(2)在单独的文件中配置路由方便管理及引入全局使用

import { createRouter, createWebHashHistory, RouteRecordRaw } from 'vue-router';
const router = createRouter({
	history: createWebHashHistory(),
	routes: staticRoutes,
});

export const staticRoutes: Array<any> = [
	{
		path: '/login',
		name: 'login',
		component: () => import('@/views/login/index.vue'),
		meta: {
			title: i18n.global.t('message.staticRoutes.signIn'),
		},
	},
	{
		path: '/404',
		name: 'notFound',
		component: () => import('@/views/error/404.vue'),
		meta: {
			title: i18n.global.t('message.staticRoutes.notFound'),
		},
	},
];
// 可以设置全局导航守卫等,并将配置的路由导出
export default router;

(3)引入全局使用(一般在main.js中,在根组件引入)

import router from './router';
import App from './App.vue';
const app = createApp(App);
app.use(router)

知识点梳理
1、基础知识点
(1)router-link (链接路由 类似a标签)
(2)router-view(路由出口,渲染当前url匹配的组件会在router-view处渲染)
(3)使用
全局注册的路由可通过this. r o u t e r 和 t h i s . router和this. routerthis.route
在实例中使用4.x中需要手动引入并使用

import { useRoute, useRouter } from 'vue-router';
const route = useRoute();
const router = useRouter();
// 添加路由项
router.push({ path: , query:  });
router.push({ name: , params:  });
// 拿当前路由(url信息)
route.path、route.query(url存在参数)、route.params、route.hash等

(4)动态路由匹配(使用:进行匹配):场景:有时我们对同一个组件User会对所有用户进行渲染,但是用户id不同。当动态路由被匹配时,它的动态路由参数会在this.$route.params上(所以使用时通过params传参进行匹配)
针对这种动态路由匹配相同的组件时,为了提高性能,组件不会销毁再创建,导致组件的生命周期不会调用。
办法:在组件上监听动态参数处理逻辑 $route.params 或者利用组件导航守卫beforeRouteUpdate在组件上等更新前做逻辑

{ path: '/users/:id', component: User },

(5)匹配所有路由或404not found路由

const routes = [
  // 将匹配所有内容并将其放在 `$route.params.pathMatch` 下
  { path: '/:pathMatch(.*)*', name: 'NotFound', component: NotFound },
  // 将匹配以 `/user-` 开头的所有内容,并将其放在 `$route.params.afterUser` 下
  { path: '/user-:afterUser(.*)', component: UserGeneric },
]

this.$router.push({
  name: 'NotFound',
  // 保留当前路径并删除第一个字符,以避免目标 URL 以 `//` 开头。
  params: { pathMatch: this.$route.path.substring(1).split('/') },
  // 保留现有的查询和 hash 值,如果有的话
  query: this.$route.query,
  hash: this.$route.hash,
})

(6)路由path不区分大小写,且后面可加/,但是可通过路由语法进行设置
(7)嵌套路由:router-view嵌套及配置route时嵌套(嵌套的命名路由:设备name字段)
一种使用场景是:方便管理,将大导航中的路由统一配置在一个地方,只需要一个路由出口,因为path写的是完整的(外层没配置组件,如下)
另一种使用场景是:当外层下有不同的内组件,需要切换时,例如User/profile User/post(在app中有路由出口,在User中有路由出口)

		{
				component: undefined,
				hidden: false,
				meta: {
					icon: 'el-icon-s-home',
					title: 'GIS配置',
					breadArr: ['可视化配置', 'GIS配置'],
				},
				name: 'GISConfig',
				path: '/visconfig/GISConfig',
				children: [
					{
						component: 'visconfig/GISConfig/layerConfig/index',
						hidden: false,
						meta: {
							icon: 'el-icon-s-home',
							title: '图层配置',
							breadArr: ['可视化配置', 'GIS配置', '图层配置'],
						},
						name: 'layerConfig',
						path: '/visconfig/GISConfig/layerConfig',
					},
					{
						component: 'visconfig/GISConfig/labelTypeConfig/index',
						hidden: false,
						meta: {
							icon: 'el-icon-s-home',
							title: '标签类型配置',
							breadArr: ['可视化配置', 'GIS配置', '标签类型配置'],
						},
						name: 'labelTypeConfig',
						path: '/visconfig/GISConfig/labelTypeConfig',
					},
				],
			},

// 第二种场景案例
<div id="app">
  <router-view></router-view>  // app根部路由出口
</div>
const User = {
  template: '<div>User {{ $route.params.id }}</div>',
}
const routes = [{ path: '/user/:id', component: User }]
const User = {
  template: `
    <div class="user">
      <h2>User {{ $route.params.id }}</h2>
      <router-view></router-view> // User中的路由出口
    </div>
  `,
}
// 配置嵌套的路由(以 / 开头的嵌套路径将被视为根路径)
const routes = [
  {
    path: '/user/:id',
    component: User,
    children: [
      {
        // 当 /user/:id/profile 匹配成功
        // UserProfile 将被渲染到 User 的 <router-view> 内部
        path: 'profile',
        component: UserProfile,
      },
      {
        // 当 /user/:id/posts 匹配成功
        // UserPosts 将被渲染到 User 的 <router-view> 内部
        path: 'posts',
        component: UserPosts,
      },
    ],
  },
]

(8)编程式导航:除了使用router-link语法,可以利用router实例方法,用代码实现

// 路由实例 $router 或者手动引入
// 1、实例方法push(会向 history 栈添加一个新的记录,所以,当用户点击浏览器后退按钮时,会回到之前的 URL)点击 <router-link :to="..."> 相当于调用 router.push(...)   注意:提供了 path,params 会被忽略
this.$router.push // 该方法的参数可以是一个字符串路径,或者一个描述地址的对象
// 字符串路径
router.push('/users/eduardo')
// 带有路径的对象
router.push({ path: '/users/eduardo' })
// 命名的路由,并加上参数,让路由建立 url
router.push({ name: 'user', params: { username: 'eduardo' } })
// 带查询参数,结果是 /register?plan=private
router.push({ path: '/register', query: { plan: 'private' } })
// 带 hash,结果是 /about#team
router.push({ path: '/about', hash: '#team' })

// 2、实例方法replace 替换当前路由(同push方法,但是不会添加到history记录中)
// 声明式
<router-link :to="..." replace>	  
// 编程式
router.replace(...) // 参数同push参数可以是字符串路径,也可以是描述地址的对象
router.push({ path: '/home', replace: true }) // 直接在push中增加replace参数

// 3、实例方法go(横跨历史,在历史堆栈中前进或后退多少步)同window.history.go(n)
// 向前移动一条记录,与 router.forward() 相同
router.go(1)
// 返回一条记录,与 router.back() 相同
router.go(-1)
// 前进 3 条记录
router.go(3)
// 如果没有那么多记录,静默失败
router.go(-100)
router.go(100)

router.push、router.replace 和 router.go 是 window.history.pushState、window.history.replaceState 和 window.history.go 的翻版,它们确实模仿了 window.history 的 API
(9)命名路由 配置路由时增加name字段,这样路由的相关操作不用书写path路径
(10)命名视图(同级展示多个视图而不是嵌套展示时使用)与嵌套命名视图(不常用,组合使用即可)

<router-view class="view left-sidebar" name="LeftSidebar"></router-view>
<router-view class="view main-content"></router-view>  // 不设默认default
<router-view class="view right-sidebar" name="RightSidebar"></router-view>
const router = createRouter({
  history: createWebHashHistory(),
  routes: [
    {
      path: '/',
      components: {
        default: Home,
        // LeftSidebar: LeftSidebar 的缩写
        LeftSidebar,
        // 它们与 `<router-view>` 上的 `name` 属性匹配
        RightSidebar,
      },
    },
  ],
})

(11)重定向和别名

// 1、重定向是通过路由配置routes增加redirect实现的(导航守卫不会对这种home有效果,只添加在目标路由上)。用户访问 /home 时,URL 会被 / 替换,然后匹配成 /,渲染对应的组件
const routes = [{ path: '/home', redirect: '/' }]  // home 重定向到 /  (可以是path)
const routes = [{ path: '/home', redirect: { name: 'homepage' } }]  // (可以是配置)
const routes = [
  {
    // /search/screens -> /search?q=screens
    path: '/search/:searchText',
    redirect: to => {  // 可以是函数返回重定向路由  的path或对象配置
      // 方法接收目标路由作为参数
      // return 重定向的字符串路径/路径对象
      return { path: '/search', query: { q: to.params.searchText } }
    },
  },
]

// 2、相对重定向
const routes = [
  {
    // 将总是把/users/123/posts重定向到/users/123/profile。
    path: '/users/:id/posts',
    redirect: to => {
      // 该函数接收目标路由作为参数
      // 相对位置不以`/`开头
      // 或 { path: 'profile'}
      return 'profile'
    },
  },
]

// 3、别名:另一个名字。将 / 别名为 /home,当用户访问 /home 时,URL 仍然是 /home,但会被匹配为用户正在访问 /所对应组件
const routes = [{ path: '/', component: Homepage, alias: '/home' }]
{ path: '', component: UserList, alias: ['/people', 'list'] },  // 可以定义多个别名

(12)路由组件传参

// 将props传递给路由组件:前面讲到动态匹配路由,会最终暴露在params上,但这种方式只能用于特定的url   /home:id
// 可以替换成props形式,确保任何组件可用(直接传参数也行,不必非得是动态路由匹配的)。
const User = {
  // 请确保添加一个与路由参数完全相同的 prop 名
  props: ['id'],
  template: '<div>User {{ id }}</div>'
}
const routes = [{ path: '/user/:id', component: User, props: true }]
// 布尔模式 props:true,route.params 将被设置为组件的 props
// 对象模式 当 props 是一个对象时,它将原样设置为组件 props(对于静态的props时适合用)
const routes = [
  {
    path: '/promotion/from-newsletter',
    component: Promotion,
    props: { newsletterPopup: false }  // 组件中的props也是此对象
  }
]
// 函数模式
const routes = [
  {
    path: '/search',
    component: SearchUser,
    props: route => ({ query: route.query.q })  // 可实现静态值与路由值相结合
  }
]

(13) 不同的历史模式:在进行路由实例常见时history配置允许我们在不同的历史模式中进行选择(html5和hash两种)

// 1、hash模式  http://10.233.54.161/preview.html#/device/type
import { createRouter, createWebHistory, createWebHashHistory } from 'vue-router'
onst router = createRouter({
	history: createWebHashHistory(),
	routes: staticRoutes,
});

// html5模式
const router = createRouter({
  history: createWebHistory(),
  routes: [
    //...
  ],
})

总结:两种实现原理
hash模式:#及后面的部分(同window.location.hash获取到的部分)原理是利用onhashchange 事件,可通过 window 对象监听
(hash 虽然包含在浏览器的url中,但是它不会包括在HTTP请求中,所以改变hash不会重新加载页面;每一次改变hash,都会在浏览器的访问历史中增加一个记录,所以可以实现前端路由“更新视图但不重新请求页面”的功能,同时也可使用浏览器的 前进、后退 功能)

history 模式(需要浏览器支持):利用html5提供的history API来实现url的变化,history.pushState()和history.repalceState()这两个 API 可以在不进行刷新的情况下,操作浏览器的历史纪录。使用 pushState 事件来监听 url 的变化,从而对页面进行跳转(渲染)
(使用 history 模式时,需要配合后端的支持,因为url路径修改后,会请求服务器的数据,如果服务器中没有相应的响应或者资源,则会出现404错误;所以需要在服务器上配置一个默认路径,当url匹配不到任何静态资源的时候,返回到同一个页面(可配置成首页 index.html ))

2、进阶
(1)导航守卫:通过跳转、取消的方式守卫导航(全局的,单个路由独享的,或者组件级的)

// 全局导航守卫(用于所有的路由)
// 1、 全局前置守卫 router.beforeEach
router.beforeEach((to, from,next) => {
  // 书写校验逻辑,校验完成执行next(),或者跳转至指定的登录页等.如果遇到了意料之外的情况,可能会抛出一个 Error。这会取消导航并且调用 router.onError() 注册过的回调
  // 返回 false 以取消导航
  return false
})

// 2、全局解析守卫router.beforeResolve (和 router.beforeEach 类似,因为它在每次导航时都会触发,但是确保在导航被确认之前,同时在所有组件内守卫和异步路由组件被解析之后,解析守卫就被正确调用) 用于获取数据或执行任何其他操作(如果用户无法进入页面时你希望避免执行的操作)的理想位置。
router.beforeResolve(async to => {
  if (to.meta.requiresCamera) {
    try {
      await askForCameraPermission()
    } catch (error) {
      if (error instanceof NotAllowedError) {
        // ... 处理错误,然后取消导航
        return false
      } else {
        // 意料之外的错误,取消导航并把错误传给全局处理器
        throw error
      }
    }
  }
})

// 3、全局后置守卫 router.afterEach(它不会再改变导航本身了,因为已经导航跳转了)它们对于分析、更改页面标题、声明页面等辅助功能以及许多其他事情都很有用
router.afterEach((to, from, failure) => {
  if (!failure) sendToAnalytics(to.fullPath)
})


// 路由独享的守卫(给需要的路由设置,适用于每个路由)
// 进入路由时触发 beforeEnter,它只在改变导航时触发,改变参数等不会触发
const routes = [
  {
    path: '/users/:id',
    component: UserDetails,
    beforeEnter: (to, from) => {
      // reject the navigation
      return false
    },
  },
]


// 组件内的守卫
beforeRouteEnter
beforeRouteUpdate
beforeRouteLeave
const UserDetails = {
  template: `...`,
  beforeRouteEnter(to, from) {
    // 在渲染该组件的对应路由被验证前调用
    // 不能获取组件实例 `this` !
    // 因为当守卫执行时,组件实例还没被创建!
  },
  beforeRouteUpdate(to, from) {
    // 在当前路由改变,但是该组件被复用时调用
    // 举例来说,对于一个带有动态参数的路径 `/users/:id`,在 `/users/1` 和 `/users/2` 之间跳转的时候,
    // 由于会渲染同样的 `UserDetails` 组件,因此组件实例会被复用。而这个钩子就会在这个情况下被调用。
    // 因为在这种情况发生的时候,组件已经挂载好了,导航守卫可以访问组件实例 `this`
  },
  beforeRouteLeave(to, from) {
    // 在导航离开渲染该组件的对应路由时调用
    // 与 `beforeRouteUpdate` 一样,它可以访问组件实例 `this`
  },
}

(2)路由元信息:任意信息附加在路由上

{
		path: '/noPermission',
		name: 'noPermission',
		component: () => import('@/views/error/noPermission.vue'),
		meta: {
			title: '无权限',
			auth: ['admin', 'test'],
		},
	},
// 访问:
一个路由匹配到的所有路由记录会暴露为 $route 对象的$route.matched 数组。我们需要遍历这个数组来检查路由记录中的 meta 字段,但是 Vue Router 还为你提供了一个 $route.meta 方法

(3)数据获取(进入路由后,需要在服务器获取数据)
方式1:导航完成之后获取:先完成导航,然后在接下来的组件生命周期钩子中获取数据。在数据获取期间显示“加载中”之类的指示。
方式2;导航完成之前获取:导航完成前,在路由进入的守卫中获取数据,在数据获取成功后执行导航(beforeRouteEnter 守卫中获取数据,当数据获取成功后只调用 next 方法)

(4)组合式API
组合式api中模板中可直接使用 r o u t e r 及 router及 routerroute但在setup中需要手动引入
在setup中使用一样但是名字变了 onBeforeRouteLeave onBeforeRouteUpdate
(5)过渡动效

// 所有路由进行过渡
<router-view v-slot="{ Component }">
  <transition name="fade">
    <component :is="Component" />
  </transition>
</router-view>

// 单个路由的过渡(每个有不同的过渡效果),将元信息与name结合
const routes = [
  {
    path: '/custom-transition',
    component: PanelLeft,
    meta: { transition: 'slide-left' },
  },
  {
    path: '/other-transition',
    component: PanelRight,
    meta: { transition: 'slide-right' },
  },
]
<router-view v-slot="{ Component, route }">
  <!-- 使用任何自定义过渡和回退到 `fade` -->
  <transition :name="route.meta.transition || 'fade'">
    <component :is="Component" />
    // <component :is="Component" :key="route.path" />  // 强制在复用视图进行过渡
  </transition>
</router-view>

(6)滚动行为(路由切换时如何滚动页面,保持在原位置还是顶部等)在支持 history.pushState 的浏览器中可用

const router = createRouter({
  history: createWebHashHistory(),
  routes: [...],
  scrollBehavior (to, from, savedPosition) {
    // return 期望滚动到哪个的位置
    // 始终滚动到顶部
    // return { top: 0 }
    // 始终在元素 #main 上方滚动 10px
    return {
      // el: document.getElementById('main'),
      el: '#main',
      top: -10,
    }
  }
})

// 还可以设置滚动到瞄点或者延迟滚动
const router = createRouter({
  scrollBehavior(to, from, savedPosition) {
    if (to.hash) {
      return {
        el: to.hash,
        behavior: 'smooth',
      }
    }
  }
})
const router = createRouter({
  scrollBehavior(to, from, savedPosition) {
    return new Promise((resolve, reject) => {
      setTimeout(() => {
        resolve({ left: 0, top: 0 })
      }, 500)
    })
  },
})

(7)路由懒加载:利用import导入组件,实现路由懒加载

完整的导航解析流程
(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 的回调函数,创建好的组件实例会作为回调函数的参数传入(最后是导航被确认后到组件上的进入)

vue-router 3.x到vue-router4.x的变化
(1)new Router 变成 createRouter
(2)history 配置取代 mode
(3)删除了*通配符路由

风语者!平时喜欢研究各种技术,目前在从事后端开发工作,热爱生活、热爱工作。