本文已参与「新人创作礼」活动,一起开启掘金创作之路。
先附上vue-router 官方文档访问地址https://router.vuejs.org/zh/
@TOC
@[TOC](vue2 写法)
import Vue from 'vue'
import VueRouter from 'vue-router'
import Layout from '../layout'
import modules from './modules/require'
Vue.use(VueRouter)
const routes = [{
redirect: '/',
path: '/index',
name: 'layout',
component: Layout,
children: [
...modules
]
},
{
path: '/', // 官网
name: 'home',
component: () => import('@/views/home')
}
]
const router = new VueRouter({
mode: 'history',
base: process.env.BASE_URL,
routes,
// scrollBehavior () {
// 浏览器保存记录对象
// history.pushState(null, null, document.URL)
// }
})
const VueRouterPush = VueRouter.prototype.push
VueRouter.prototype.push = function push(to) {
return VueRouterPush.call(this, to).catch(err => err)
}
export default router
modules------require.js
const files = require.context('./', false, /\.js$/)
let routerArr = []
files.keys().forEach(item => {
if (item !== './require.js') {
routerArr = routerArr.concat(files(item).default)
}
})
export default typeof routerArr[0] === 'undefined' ? '' : routerArr
modules文件夹里面是各个路由的js文件,js里面写的便是改路由
例如:license.js
export default [{
path: '/license',
name: 'license',
icon: 'license-menu',
meta: {
isMenu: true,
title: '授权信息',
active_tab: '/index',
keepAlive: true,
roles: [1, 2, 3]
},
component: () => import('@/views/license')
}]
Layout 是封装的公共布局,包块但不限于导航栏、菜单栏、个人中心等等。
我们都知道vue-router 有router.beforeEach这样一个全局的路由钩子,在这个方法里面可以做路由拦截个重定向,这个钩子可以在router.js里面 写,也可以在main.js里面写,看个人习惯。
// 全局路由钩子
router.beforeEach((to, from, next) => {
const pathName = to.name
// 判断该路由是否需要登录权限
if (pathName)) {
} else {
next() // 确保一定要有next()被调用
return
}
})
@TOC router.js
import { createRouter, createWebHistory } from "vue-router";
import BasicLayout from "../layouts/basicLayout.vue";
const routes = [
{
path: "/",
name: "index",
meta: { title: "首页" },
component: BasicLayout,
redirect: "/home",
children: [
{
path: "/home",
name: "home",
meta: { title: "首页", isHideMenu: false },
component: () =>
import("../views/home/index.vue"),
}
],
},
];
const router = createRouter({
history: createWebHistory(),
routes,
});
export default router;
BasicLayout 文件便是所封装的公共组件,因为这个是demo 所以简单处理没有进行拆分
@TOC
简单说一下 举一个例子作为说明,比如说我们常用到的菜单栏
@TOC
<template>
<div class="sider-container">
<!-- collapsible 开关 -->
<a-layout-sider v-model="isCollapsed">
<a-menu
mode="inline"
style="width: 224px; min-height: 100vh; padding-top: 23px"
:inline-collapsed="isCollapsed"
:selectedKeys="selectedKeys"
:openKeys="openKeys"
@openChange="changeMenu"
@click="changeRoute"
class="menu-box"
>
<template v-for="item in menuList">
<template v-if="item.meta && item.meta.isMenu && item.meta.roles.includes(userInfo.role)">
<!-- 有子菜单 -->
<template v-if="item.children">
<a-sub-menu :key="item.path">
<span slot="title">
<icon v-if="item.icon" :name="item.icon" />
<span>{{ item.meta.title }}</span>
</span>
<template v-for="column in item.children">
<a-menu-item
:key="column.path"
v-if="column.meta && column.meta.isMenu && column.meta.roles.includes(userInfo.role)"
>
<span class="laout-level_1">
<i class="point">.</i>
<span >{{ column.meta.title }}</span>
</span>
</a-menu-item>
</template>
</a-sub-menu>
</template>
<!-- 没有子菜单 -->
<template v-else>
<a-menu-item :key="item.path">
<icon v-if="item.icon" :name="item.icon" />
<span>{{ item.meta.title }}</span>
</a-menu-item>
</template>
</template>
</template>
</a-menu>
</a-layout-sider>
</div>
</template>
<script>
export default {
props: {
routes: {
type: Array,
default: () => [],
},
},
data() {
return {
menuOrderList: [],
menuList: [],
isCollapsed: false,
firstTab: [],
selectedKeys: [this.$route.path],
openKeys: [],
userInfo:{},
expireOption:"route"
};
},
mounted() {
//想要排序就自己定个顺序,不需要就算了
//menuOrderList
// 路由顺序调整
this.routes.map(item => {
if (this.menuOrderList.includes(item.meta.title)) {
this.menuList[this.menuOrderList.indexOf(item.meta.title)] = item
}
})
// 展开菜单
if (this.$route.meta.openKeys) {
this.openKeys = this.$route.meta.openKeys
}
// 选中菜单 有二级
if(this.$route.meta.path){
this.selectedKeys = [this.$route.meta.path]
}else{
this.selectedKeys = [this.$route.path]
}
},
methods: {
changeMenu (key) {
this.openKeys = key
},
changeRoute ({key}) {
if (this.$route.path === key) {
return
}
this.openKeys = ['/' + key.split('/')[1]]
this.$emit('chooseItem', key);
this.$router.push({
path: key
})
},
},
watch:{
$route () {
if(this.$route.meta.path){
this.selectedKeys = [this.$route.meta.path]
}else{
this.selectedKeys = [this.$route.path]
}
},
}
};
</script>
<style lang="less" scoped>
.laout-level_1 {
position: relative;
z-index: 2;
.point {
position: absolute;
left: -14px;
top: -19px;
font-size: 30px;
}
.sub-title {
padding-left: 12px;
}
}
.svg-icon {
margin-right: 8px;
vertical-align: -3px !important;
}
.menu-box {
.ant-menu-item-selected {
.svg-icon {
fill: @primary-color !important;
}
}
}
</style>
@TOC
<template>
<a-layout>
<a-layout-header :style="{ position: 'fixed', zIndex: 99, width: '100%' }">
<div class="logo" />
<a-menu
v-model:theme="state.navTheme"
v-model:mode="state.mode"
v-model:selectedKeys="state.selectedKeys"
:style="{ lineHeight: '64px' }"
@click="changeRoute"
>
<div v-for="item in state.menuData" :key="item.name">
<a-menu-item :key="item.name" v-if="!item.meta.isHideMenu">
<span>{{ item.meta.title }}</span>
</a-menu-item>
</div>
</a-menu>
</a-layout-header>
<a-layout-content :style="{ padding: '0px 50px', marginTop: '96px' }">
<div
:style="{
background: '#fff',
padding: '24px',
minHeight: '780px',
textAlign: 'left',
}"
>
<router-view></router-view>
</div>
</a-layout-content>
<a-layout-footer :style="{ textAlign: 'center' }">
COPYRIGHT © 2021
</a-layout-footer>
</a-layout>
</template>
<script>
import { reactive, defineComponent, watch } from "vue";
import { useRouter, useRoute } from "vue-router";
import { clearMenuItem, getMenuData } from "../utils/getMenuData";
export default defineComponent({
setup() {
const router = useRouter();
const route = useRoute();
// 获取路由菜单
const { menuData } = getMenuData(clearMenuItem(router.getRoutes()));
// 定义参数
const state = reactive({
collapsed: false, // default value
navTheme: "dark",
mode: "horizontal",
selectedKeys: [], //默认选择菜单
menuData,
});
// 切换路由
function changeRoute({ key }) {
router.push({
path: key,
});
}
state.selectedKeys = [route.name];
watch(
() => route.name,
(newValue, oldValue) => {
if (newValue == "ruleResult") {
state.selectedKeys = [];
} else {
state.selectedKeys = [newValue];
}
}
);
return {
state,
changeRoute,
};
},
});
</script>
<style lang="less">
#components-layout-demo-basic {
text-align: left;
}
#components-layout-demo-basic .ant-layout-header,
#components-layout-demo-basic .ant-layout-footer {
line-height: 1.5;
}
#components-layout-demo-basic > .ant-layout {
margin-bottom: 48px;
}
#components-layout-demo-basic > .ant-layout:last-child {
margin: 0;
}
#components-layout-demo-basic > .header {
width: 100%;
height: 80px;
line-height: 80px;
color: #ffffff;
}
</style>
代码中很明显的区别,vue3说白了就是治懒癌的,按需引入,想用什么直接引入就可以了。
上述代码 可以明显的看出来vue2写法和vue3写法的区别,所以总结一下:
@TOC
我们在vue2里面想要跳转路由的的时候,可以写<router-link to="home">Home</router-link>
我们也可以用this.$router.push({})
但是在vue3中引入了setup,在setup中呢this是不存在的,不信的话可以打印一下,this是undefined
所以在vue3中我们先要引入import { useRouter, useRoute } from "vue-router";
用法如下:
const router = useRouter();
router.push({})
@TOC
我们在vue2中获取当前路由的时候用 this.$route,也可以watch监听一下
watch:{
$route () {
},
}
在vue3中this是undefiend,所以我们获取方式发生了改变,用法如下
const route = useRoute();
watch(
() => route.name,
(newValue, oldValue) => {
if (newValue == "ruleResult") {
state.selectedKeys = [];
} else {
state.selectedKeys = [newValue];
}
}
);