npm i vue-router-tab -S
yarn add vue-router-tab
示例:
// @/main.js 入口
// router-tab 组件依赖 vue 和 vue-router
import Vue from 'vue'
import Router from 'vue-router'
// 引入组件和样式
import RouterTab from 'vue-router-tab'
import 'vue-router-tab/dist/lib/vue-router-tab.css'
import App from './App.vue'
import router from './router'
Vue.config.productionTip = false
Vue.use(RouterTab)
new Vue({
router,
render: (h) => h(App)
}).$mount('#app')
配置参考: RouterTab Props
::: danger
RouterTab
仅支持单例模式,请勿在同一个页面中引入多个 RouterTab
组件!
:::
示例:
<!-- @/components/layout/Frame.vue 布局框架 -->
<template>
<div class="app-header">头部</div>
<div class="app-body">
<div class="app-side">侧边栏</div>
<router-tab/>
</div>
</template>
RouterTab
内置路由以支持操作 iframe 页签meta
信息,来设置页签的标题、图标、提示和路由页签规则配置参考: Route.meta 路由元信息
示例:
// @/router.js 路由
import Vue from 'vue'
import Router from 'vue-router'
// RouterTab 内置路由
import { RouterTabRoutes } from 'vue-router-tab'
// 引入布局框架组件
import Frame from './components/layout/Frame.vue'
// 异步加载页面组件
const importPage = view => () => import(/* webpackChunkName: "p-[request]" */ `./views/${view}.vue`)
Vue.use(Router)
export default new Router({
routes: [{
path: '/',
redirect: '/page1',
// 父路由组件内必须包含 <router-tab>
component: Frame,
// 子路由里配置需要通过页签打开的页面路由
children: [
// 引入 RouterTab 内置路由以支持操作 iframe 页签
...RouterTabRoutes,
{
path: '/page/:id',
component: importPage('Page'),
meta: {
title: '页面', // 页签标题
icon: 'icon-user', // 页签图标,可选
tips: '这是一个页面', // 页签提示,可选,如未设置则跟title一致
aliveId: 'fullPath', // 路由打开页签规则,可选
}
},
{
path: '/404',
component: importPage('404'),
meta: {
title: '找不到页面',
icon: 'icon-page'
}
},
{
path: '*',
redirect: '/404'
}
]
}
RouterTab
通过响应路由变化来新增或切换页签,直接使用 vue-router
提供的方法即可。
参考文档:Vue Router 导航
router-link
组件方式打开和切换到页签
<router-link to="/page/1">页面1</router-link>
全新打开页签 (刷新已有页签)
<router-link to="/page/2" @click.native="$routerTab.refresh('/page/2')">页面2”<router-link>
router.push
、router.replace
、router.go
注意:在 Vue 实例内部,您可以通过 $router 访问路由实例。因此您可以调用 this.$router.push。
// 如果需要全新打开请加上:
// this.$routerTab.refresh('/page/1')
this.$router.push('/page/1')
您可以通过 RouterTab
的实例方法 routerTab.close(location, fullMatch?)
来关闭页签
::: tip
在 Vue 实例内部,您可以通过 $routerTab
访问路由页签实例。因此您可以调用 this.$routerTab.close
。
当 RouterTab
组件只有一个页签,或者初始页签的 closable
配置为 false
,页签关闭操作将不生效。
:::
关闭当前页签
this.$routerTab.close()
关闭指定页签
// 关闭指定 fullPath 的页签
this.$routerTab.close('/page/1')
// 关闭指定 location 的页签
this.$routerTab.close({
name: 'page',
params: {
id: 2
}
})
模糊关闭页签
// 关闭与给定地址共用页签的地址,即使地址不完全匹配
// 默认 `alive-id` 规则下,类似 '/page/1?query=2' 这样的页签也能被匹配关闭
this.$routerTab.close('/page/1', false)
您可以通过 RouterTab
的实例方法 routerTab.refresh(location, fullMatch?)
来刷新页签
刷新当前页签
this.$routerTab.refresh()
刷新指定页签
// 刷新指定 fullPath 的页签
this.$routerTab.refresh('/page/1')
// 刷新指定 location 的页签
this.$routerTab.refresh({
name: 'page',
params: {
id: 2
}
})
模糊刷新页签
// 刷新与给定地址共用页签的地址,即使地址不完全匹配
// 默认 `alive-id` 规则下,类似 '/page/1?query=2' 这样的页签也能被匹配刷新
this.$routerTab.close('/page/1', false)
您可以通过 RouterTab
的实例方法 routerTab.refreshAll(force?)
来刷新所有页签
刷新所有页签
this.$routerTab.refreshAll()
强制刷新所有页签,忽略页面组件的 beforePageLeave
配置
this.$routerTab.refreshAll(true)
RouterTab
支持通过 iframe 页签嵌入外部网站。
::: warning
该功能需要引入 RouterTab
内置路由,请参考 基础 - 路由配置
:::
打开 iframe 页签
// 三个参数分别为:链接、页签标题、图标
this.$routerTab.openIframeTab('https://www.baidu.com', '百度', 'icon-web')
关闭 iframe 页签
this.$routerTab.closeIframeTab('https://www.baidu.com')
刷新 iframe 页签
this.$routerTab.refreshIframeTab('https://www.baidu.com')
router-tab
组件提供了 i18n
属性,用以配置自定义的国际化转换方法,从而实现路由页签的多语言展示。
目前支持国际化的字段有:页签的 title
和 tips
。
组件提供两种方式定义国际化字段:
'i18n:custom.i18n.key'
: 字符串方式, i18n:
前缀 + 国际化字段的 key
['custom.i18n.key', ...params]
: 数组方式,第一项为国际化字段的 key
,后面为参数列表。适用于动态的国际化内容。
配置自定义国际化方法
<template>
<router-tab :i18n="i18n" />
</template>
<script>
export default {
methods: {
// 自定义国际化转换方法
i18n (key, params) {
// $getI18n 为项目的公共国际化方法
return this.$getI18n(key, params)
}
}
}
</script>
路由配置国际化页签
{
path: '/page1',
component: pageComponent,
meta: {
title: 'i18n:routerTab.page1', // 通过配置 'i18n:key' 的方式来设置国际化字段 'routerTab.page1'
icon: 'icon-user', // 页签图标,可选
tips: 'i18n:routerTab.page1Tips', // 页签提示同样支持国际化
}
}
通过配置 router-tab
组件的 language
属性,可以设置组件显示的语言 (主要表现为页签右键菜单)。
RouterTab
默认语言是 zh-CN
,另外内置了 en
。
指定组件显示为英文
<router-tab language="en"/>
自定义的语言 (参考配置)
<router-tab :language="customLanguage"/>
export default {
data () {
return {
customLanguage: {
...
}
}
}
}
不同的页签维护着各自的页面缓存,而页签规则定义了不同的路由打开页签的方式。
path
(默认规则)
route => route.path
fullPath
route => route.fullPath.replace(route.hash, '')
通过配置 router-tab
组件的 alive-id
属性,您可以定义全局的页签规则
示例:
<router-tab :alive-id="route => route.fullPath.replace(route.hash, '')"/>
例子中,配置 alive-id
为 fullPath
去除 hash
部分。
根据该规则,page/1
和 page/1?query=2
、page/2
、page/2?query=2
这四个地址都是打开不同的页签。而 page/1
和 page/1#hash1
是同一个页签,因为它们忽略 hash
后的路径一致。
该规则已经内置在 RouterTab
中了,因此,您也可以直接这样使用:
<router-tab alive-id="fullPath"/>
通过配置路由的 meta.aliveId
属性,您可以针对特定路由定制页签规则
示例:
const route = {
path: '/my-page/:catalog/:type',
component: {
template: '<div>定制规则:{{$route.params.catalog}}/{{$route.params.type}}</div>'
},
meta: {
title: '定制规则',
aliveId (route) {
return `/my-page/${route.params.catalog}`
}
}
}
根据示例中的页签规则,/my-page/a/1
和 /my-page/a/2
打开的是同一个页签。而 /my-page/b/1
和 /my-page/b/2
则打开另外一个页签
前面的内容已经能满足大部分使用场景了,您还可以根据下面的内容实现更多功能。
您可以通过配置 router-tab
组件的 tab-transition
和 page-transition
属性,分别替换默认的页签和页面过渡效果
::: warning
如果是组件作用域内的 CSS(配置了 scoped
),需要在选择器前添加 /deep/
才能生效
页签项 .router-tab-item
默认设置了 transition
和 transform-origin
的样式,您可能需要覆盖它已避免影响到自定义的过渡效果
:::
页签过渡效果-示例:
<template>
<router-tab tab-transition="tab-scale"/>
</template>
<style lang="scss" scoped>
/deep/ .tab-scale {
&-enter-active,
&-leave-active {
transition: opacity .5s, transform .5s;
}
&-enter,
&-leave-to {
opacity: 0;
transform: scale(1.5);
}
}
</style>
页面过渡效果-示例:
<template>
<router-tab page-transition="page-fade"/>
</template>
<style lang="scss" scoped>
/deep/ .page-fade {
&-enter-active,
&-leave-active {
transition: opacity .5s;
}
&-enter,
&-leave-to {
opacity: 0;
}
}
</style>
您还可以使用对象的方式设置 tab-transition
和 page-transition
的值,以实现详细的过渡效果配置
配置参考: Vue - transition
<router-tab :tab-transition="{
name: 'my-transition',
'enter-class': 'my-transition-enter'
}"/>
通过 router-tab
组件的默认作用域插槽,我们可以自定义页签显示的内容
插槽的作用域提供以下属性供模板使用:
id
, title
, icon
, closable
等示例:
<router-tab>
<template v-slot="{ tab: { id, title, icon, closable }, tabs, index}">
<i v-if="icon" class="tab-icon" :class="icon"></i>
<span class="tab-title">{{title || '未命名页签'}}</span>
<span class="tab-badge">{{index}}</span>
<i class="tab-close el-icon-close" v-if="closable !== false &&tabs.length > 1" @click.prevent="$routerTab.close(id)"></i>
</template>
</router-tab>
通过配置 router-tab
组件的 tabs
属性,可以设置进入页面时默认显示的页签。
示例:
<router-tab :tabs="tabs"/>
export default {
data () {
return {
// 默认页签项
tabs: [
// 推荐 fullPath 方式配置默认页签项。RouterTab 会自动获取路由里的页签配置
'/page/1',
// 设置初始 title,用于需要动态更新页签信息的路由页面
{ to: '/page/2', title: '页面2', icon: 'icon-page' },
// 设置 closable 为 false,可以禁止页签被关闭
{ to: '/page/3', closable: false },
// 也可以 location 方式设置默认页签项
{
to: {
name: 'page',
params: { id: 4 },
query: { t: 2 }
}
},
// 此页面与'/page/2'的aliveId一致,将只保留先设置的页签
{ to: '/page/2?t=1', title: '页面2-1' }
]
}
}
}
RouterTab
会监听组件 this.routeTab
来动态更新页签信息。您可以通过设置 this.routeTab
来更改页签的标题、图标、提示。
示例:
export default {
name: 'goods',
data () {
return {
goodsName: '商品名',
goodsDesc: '商品简介',
routeTab: null // routeTab 存放在 data 中以支持响应
}
},
mounted () {
setTimeout(() => {
let { id } = this.$route.params
// 只更新页签标题
this.routeTab = `页面${id}动态标题`
// 更新多个页签信息
this.routeTab = {
title: `商品-${this.goodsName}`,
icon: 'el-icon-goods',
tips: this.goodsDesc
}
// 国际化页签标题
this.routeTab = {
// 以数组方式定义带参数列表的国际化,格式:['i18nKey', ...params]
title: ['routerTab.goods', this.goodsName]
}
}, 300)
}
}
当页签关闭、刷新或替换时会触发 beforePageLeave
,通过 Promise
的 resolve
和 reject
来允许或者阻止页签页面的离开。
::: warning
beforePageLeave
在组件的最外层,不是放在 methods
里
如果还需要在浏览器页面关闭或刷新前阻止,请使用
onbeforeunload
:::
示例:
export default {
// 页面离开前确认
beforePageLeave (resolve, reject, tab, type) {
// 离开类型
const action = {
close: '关闭',
refresh: '刷新',
replace: '替换'
}[type]
const msg = `您确认要${action}页签“${tab.title}”吗?`
// 值未改变,则直接离开页签
if (this.editValue === this.value) {
resolve()
return
}
// 值改变则确认提示
if (confirm(msg)) {
resolve()
} else {
reject('拒绝了页面离开')
}
/*
// 此处使用了 Element 的 confirm 组件
// 需将 closeOnHashChange 配置为 false,以避免路由切换导致确认框关闭
this.$confirm(msg, '提示', { closeOnHashChange: false })
.then(resolve)
.catch(reject)
*/
}
}