前端理由导航(vue-router)
前端路由的产生
前端路由是后来发展到SPA(单页应用)时才出现的概念。
SPA 就是一个 web 项目只有一个 HTML 页面,一旦页面加载完成,SPA 不会因为用户的操作而进行页面的重新加载或跳转。
优点:前后端的彻底分离,不刷新页面,用户体验较好,页面持久性较好。
缺点:初次加载耗时多,前进后退路由管理,SEO 难度较大。
前端路由实现起来其实不难,本质是监听 URL 的变化,然后匹配路由规则,在不刷新的情况下显示相应的页面。
前端有两种路由模式,理解这两种路由模式,能更好理解
实例:
// router/index.js
const router = new VueRouter({
// 指定理由模式
mode: 'history',
// 路由配置
routes: [
{
},
{
path: '*',
redirect: '/404'
}],
});
vueRouter
提供了灵活的路由配置和导航功能
功能包括:
- 路由映射:可以将 url 映射到 Vue组件,实现不同 url 对应不同的页面内容。
- 嵌套路由映射:可以在路由下定义子路由,实现更复杂的页面结构和嵌套组件的渲染。
- 动态路由:通过路由参数传递数据。你可以在路由配置中定义带有参数的路由路径,并通过 $route.params 获取传递的参数。
- 模块化、基于组件的路由配置:路由配置是基于组件的,每个路由都可以指定一个 Vue 组件作为其页面内容,将路由配置拆分为多个模块,在需要的地方引入。
- 路由参数、查询、通配符:通过路由参数传递数据,实现页面间的数据传递和动态展示。
- 导航守卫:Vue Router 提供了全局的导航守卫和路由级别的导航守卫,可以在路由跳转前后执行一些操作,如验证用户权限、加载数据等。
- 展示由 Vue.js 的过渡系统提供的过渡效果:可以为路由组件添加过渡效果,使页面切换更加平滑和有动感。
- 细致的导航控制:可以通过编程式导航(通过 JavaScript 控制路由跳转)和声明式导航(通过 ****组件实现跳转)实现页面的跳转。
- 路由模式设置:Vue Router 支持两种路由模式:HTML5 history 模式或 hash 模式。
- 可定制的滚动行为:当页面切换时,Vue Router 可以自动处理滚动位置。定制滚动行为,例如滚动到页面顶部或指定的元素位置。
- URL 的正确编码:Vue Router 会自动对 URL 进行正确的编码。
路由组件
router-view:router-view 将显示与 url 对应的组件。
keep-alive 可以设置以下props属性:
- include - 字符串或正则表达式。只有名称匹配的组件会被缓存
- exclude - 字符串或正则表达式。任何名称匹配的组件都不会被缓存
- max - 数字。最多可以缓存多少组件实例
keep-alive是vue中的内置组件,能在组件切换过程中将状态保留在内存中,防止重复渲染DOM。
<!-- 不缓存视图,对于编辑或者需要及时跟新数据的操作,可以使用缓存,增加 activated-->
<router-view v-if="!keepAlive"></router-view>
<!-- 缓存视图 要增加 activated -->
<keep-alive :max="4" :include="['a', 'b']">
<router-view />
</keep-alive>
$route
$route: 是当前路由信息对象,获取和当前路由有关的信息。 route 为属性是只读的,里面的属性是 immutable (不可变) 的,不过可以通过 watch 监听路由的变化。
基础属性:
fullPath: "" // 当前路由完整路径,包含查询参数和 hash 的完整路径
hash: "" // 当前路由的 hash 值 (锚点)
matched: [] // 包含当前路由的所有嵌套路径片段的路由记录
meta: {} // 路由文件中自赋值的meta信息
name: "" // 路由名称
params: {} // 一个 key/value 对象,包含了动态片段和全匹配片段就是一个空对象。
path: "" // 字符串,对应当前路由的路径
query: {} // 一个 key/value 对象,表示 URL 查询参数。跟随在路径后用'?'带的参数
$router
router:是vueRouter实例对象,是一个全局路由对象,通过this.router: 是 vueRouter 实例对象,是一个全局路由对象,通过 this.router:是vueRouter实例对象,是一个全局路由对象,通过this.router 访问路由器, 可以获取整个路由文件或使用路由提供的方法。
// 导航守卫
router.beforeEach((to, from, next) => {
/* 必须调用 `next` */
})
router.beforeResolve((to, from, next) => {
/* 必须调用 `next` */
})
router.afterEach((to, from) => {})
动态导航到新路由
router.push // 用于跳转到指定的路由
router.replace // 用于替换当前的路由
router.go(n) // 在路由历史记录中前进或后退n个步骤。参数`n`可以是一个正数或负数,正数表示前进,负数表示后退。
router.back() // 后退一个步骤,等同于`router.go(-1)`
router.forward() // 前进一个步骤,等同于`router.go(1)`
需求实例:
需求描述:
在tab栏切换时,不同tab页面中,表单的列表示不同信息
实现
1、点击tab栏切换,访问指定路由
<template>
<el-tabs
v-model.trim="activeTab"
@tab-click="changeActiveTab">
<el-tab-pane
v-for="item in tabList"
:key="item.name"
:label="item.label"
:name="item.name">
</el-tab-pane>
</el-tabs>
</template>
<script>
methods:{
activatedOrCreated() {
const tabs = [
{label: '语音', name: 'audio', sessionType: 2, type: 'audio'},
{label: '文本', name: 'text', sessionType: 1, type: 'text'},
{label: '富文本', name: 'workorder', sessionType: 3, type: 'workorder'}
];
// 将对象中的 `type` 属性值,过滤出来
const { activeTab, tabList } = this.$helper.filterType(tabs);
// 暂存在data中,如果是第一次进入,从路由中获取,进入后操作获取操作中`type`值
this.activeTab = this.$route.query.type || activeTab;
this.tabList = tabList;
},
changeActiveTab() {
if (this.activeTab === 'text') {
// 用于跳转到指定的路由
this.$router.push({path: '/smart-check?type=text&active=taskTab'});
} else if (this.activeTab === 'audio') {
this.$router.push({path: '/smart-check?type=audio&active=taskTab'});
} else {
this.$router.push({path: '/smart-check?type=workorder&active=taskTab'});
}
}
}
};
</script>
2、表格中
当一个路由被匹配时,它的 内容传递参数 将在每个组件中以 this.$route.query 的形式暴露出来
</template>
<el-table>
// 当tab栏切换到 语音 时,此列才显示
<el-table-column
v-if="orderScoreLoding === 'audio' "
min-width="100"
label="订单得分">
<template slot-scope="scope">
{{ scope.row.orderScore }}
</template>
</el-table-column>
<el-table>
</template>
export default {
data(){
return {orderScoreLoding: ''}
},
mounted() {
// 获取到当前路由查询参数中的'type'值
this.orderScoreLoding = this.$route.query.type;
}
}
routes 懒加载(动态加载路由)
把不同路由对应的组件分割成不同的代码块,当路由被访问时才去加载对应的组件 即为路由的懒加载,可以加快项目的加载速度,提高效率
const router = new VueRouter({
routes: [
{
path: '/home',
name: 'Home',
component:() = import('../views/home')
}
]
})
动态路由
- 动态路由的创建,主要是使用 path 属性过程中,使用动态路径参数,路径参数 用冒号 : 表示。
const routes = [
{
path: '/user/:id'
name: 'User'
components: User
}
]
vue-router 通过配置 params传参 和 query传参 来实现动态路由
params 传参
- 必须使用 命名路由 name 传值
- 参数不会显示在 url 上
- 浏览器强制刷新时传参会被清空
// 传递参数
this.$router.push({
name: Home,
params: {
number: 1 ,
code: '999'
}
})
// 接收参数
const p = this.$route.params
query 传参
- 可以用 name 也可以使用 path 传参
- 传递的参数会显示在 url 上
- 页面刷新是传参不会丢失
// 方式一:路由拼接
this.$router.push('/home?username=xixi&age=18')
// 方式二:name + query 传参
this.$router.push({
name: Home,
query: {
username: 'xixi',
age: 18
}
})
// 方式三:path + name 传参
this.$router.push({
path: '/home',
query: {
username: 'xixi',
age: 18
}
})
// 接收参数
const q = this.$route.query
路由守卫
全局路由守卫
路由守卫主要用来通过跳转或取消跳转
的方式守卫导航
全局前置路由守卫(beforeEach(to,from, next))
router.beforeEach(async (to, from, next) => {
// 清除面包屑导航数据
store.commit('common/SET_BREAD_NAV', [])
// 是否白名单
if (isWhiteList(to)) {
next()
} else {
// 未登录,先登录
try {
if (!store.state.user.userInfo) {
// 通过 vuex 中的方法获取用户信息
await store.dispatch('user/getUserInfo')
// 登录后判断,是否有角色, 无角色 到平台默认页
if (!store.state.user.userInfo.permissions || !store.state.user.userInfo.permissions.length) {
next({ path: '/noPermission' })
}
}
// 登录后判断,是否有访问页面的权限
if (!hasVisitPermission(to, store.state.user.userInfo)) {
next({ path: '/404' })
} else {
next()
}
} catch (err) {
$error(err)
}
}
})
在全局前置路由导航中清除面包屑导航数据的目的:
为了确保每次路由切换时都能重新生成正确的面包屑导航路径。面包屑导航通常是根据用户的导航行为和路由路径动态生成的。当用户从一个页面导航到另一个页面时,路由路径会发生变化,面包屑导航也需要相应地更新。如果不清除面包屑导航数据,那么在每次路由切换时,旧的面包屑导航数据可能会被保留下来,导致显示的面包屑导航路径不正确。
全局解析路由守卫(beforeResolve(to,from, next))
当发生路由切换时,会触发beforeResolve
导航守卫
触发时机:在所有组件内守卫和异步路由组件被解析之后,导航确定之前,解析守卫就被正确调用。
即在 beforeEach
和 组件内 beforeRouteEnter
之后,afterEach
之前调用
router.beforeResolve 是获取数据或执行任何其他操作的理想位置
router.beforeResolve(async to => {
// 如果目标路由配置的`meta`字段包含`requiresCamera`属性,表示该路由需要使用相机权限
if (to.meta.requiresCamera) {
try {
// 调用`askForCameraPermission()`方法来请求相机权限
await askForCameraPermission()
} catch (error) {
if (error instanceof NotAllowedError) {
// ... 处理错误,然后取消导航
return false
} else {
// 意料之外的错误,取消导航并把错误传给全局处理器
throw error
}
}
}
})
全局后置路由守卫(afterEach(to,from))
在路由跳转完成后触发
router.afterEach((to, from) => {
// 在路由完成跳转后执行,实现分析、更改页面标题、声明页面等辅助功能
sendToAnalytics(to.fullPath)
})
独享路由守卫(beforeEnter(to,from, next))
独享路由守卫可以直接在路由配置上定义,但是它只在进入路由时触发,不会在 params、query 或 hash 改变时触发
const routes = [
{
path: '/users/:id',
component: UserDetails,
// 在路由配置中定义守卫
beforeEnter: (to, from,next) => {
next()
},
},
]
路由守卫的重用
function removeQueryParams(to) {
if (Object.keys(to.query).length)
return { path: to.path, query: {}, hash: to.hash }
}
function removeHash(to) {
if (to.hash) return { path: to.path, query: to.query, hash: '' }
}
const routes = [
{
path: '/users/:id',
component: UserDetails,
beforeEnter: [removeQueryParams, removeHash],
},
{
path: '/about',
component: UserDetails,
beforeEnter: [removeQueryParams],
},
]
组件路由守卫
在组件内使用的钩子函数,类似于组件的生命周期
- beforeRouteEnter(to,from, next) -- 进入前
- beforeRouteUpdate(to,from, next) -- 路由变化时
- beforeRouteLeave(to,from, next) -- 离开后
export default{
data(){
...
},
// 在渲染该组件的对应路由被验证前调用
beforeRouteEnter (to, from, next) {
// 此时 不能获取组件实例 this
// 因为当守卫执行前,组件实例还没被创建
next((vm)=>{
// next 回调 在 组件 beforeMount 之后执行 此时组件实例已创建,
// 可以通过 vm 访问组件实例
console.log('组件中的路由守卫==>> beforeRouteEnter 中next 回调 vm', vm)
)
},
// 可用于检测路由的变化
beforeRouteUpdate (to, from, next) {
// 在当前路由改变,但是该组件被复用时调用 此时组件已挂载完可以访问组件实例 `this`
// 举例来说,对于一个带有动态参数的路径 /foo/:id,在 /foo/1 和 /foo/2 之间跳转的时候,
// 由于会渲染同样的 Foo 组件,因此组件实例会被复用。而这个钩子就会在这个情况下被调用。
console.log('组件中的路由守卫==>> beforeRouteUpdate')
next()
},
// 在导航离开渲染该组件的对应路由时调用
beforeRouteLeave (to, from, next) {
// 可以访问组件实例 `this`
console.log('组件中的路由守卫==>> beforeRouteLeave')
next()
}
}
转载自:https://juejin.cn/post/7251857574526140474