01、什么是前端路由?

前端路由的一个大背景就是当下流行的单页应用SPA,一些主流的前端框架,如vue、react、angular都属于SPA,那什么是SPA呢?

1.1、SPA

SPA(single-page application)单页面应用,就是浏览器只加载了一个URL地址,一个页面,应用的所有功能、交互都在这个页面内进行。而实现单页面应用的基础就是ajax,通过异步请求动态的切换页面内容、实现交互,页面整体没有刷新。这避免了页面URL跳转,用户体验也不会中断,就像原生应用一样,体验比较好。越来越多的系统在使用SPA,尤其是WebApp中使用广泛。

SPA单页应用对应的就是多页应用MPA,当然两者不是非此即彼的,主要基于业务需求,是可以共存的。

区别 单页面应用(SPA) 多页面应用(MPA)
页面组成 一个主页,包含多个页面片段 多个主页面
刷新方式 局部刷新 整页刷新
url模式 hash哈希模式 、history历史模式 history历史模式
SEO搜索引擎优化 难实现,采用页面静态化方式优化 容易实现
数据传递 同一应用内,容易 通过url、cookie、localStorage等传递,复杂
渲染性能 首次加载资源多稍慢,切换快,体验良好 切换加载资源,速度慢,用户体验差
转场动画 容易实现 好像实现不了
维护成本 相对容易 相对复杂

SPA的主要表现就是更新视图而不重新请求页面,要实现前端的页面的自主路由控制,而不会刷新页面,涉及两种主流的技术:hash模式、history模式,这算是前端路由的核心原理,简单了解一下吧!

1.2、#hash路由原理

hash( /hæʃ/ )是URL地址中#号后面的内容(包括#),原本的作用是用于HTML页面内部定位的描点,描点的变化不会导致页面重新加载。HTTP请求中也不会带#,所以刷新也不影响,这是浏览器端的本地行为。

  • 页面不刷新:hash的变化不会刷新页面,只会触发浏览器定位锚点,这是hash实现前端路由的基本原理。
  • 获取 hashwindow.location.hash
  • hash 变更事件window.hashchange监听hash变化。
  • 不同的hash会进入浏览器历史记录。

http://www.xxx.cn/#/about
http://www.xxx.cn/#/pro-info-list

所以,实现过程就比较简单了!

监测hash变化:通过hashchange事件监测hash变化 。

加载资源:根据hash值匹配不同资源进行加载、切换,在Vue中切换的其实就是不同的组件。

hash-简易路由示例:codepen

<div id="app2">
<ul id="nav" v-once>
<li v-for="item in navs" v-if="item.title"><a v-bind:href="'#/'+item.url">{{item.title}}</a></li>
</ul>
<div id="main">
<keep-alive>
<component v-bind:is="currentComponent"></component>
</keep-alive>
</div>
</div>
<script>
//components
const NotFound = { template: '<p>404!Page not found</p>' };
const Home = { template: '<p>首页<br>Home page</p>' };
const Product = { template: '<p>产品页面<br>Product page<br><input></p>' };
const About = { template: '<p>关于我们<br>About page</p>' };
//导航路由数据
function Route(title, url, name, component) {
this.title = title; this.url = url; this.component = component; this.name = name;
}
let routes = [
new Route("首页", "home", 'home', Home), new Route("商品", "protect", 'protect', Product),
new Route("招聘", "hr", null, null), new Route("关于", "about", 'about', About),
new Route(null, "not-found", 'not-found', NotFound)];
let components = {};
routes.forEach(item => { components[item.name] = item.component });
//app
let app2 = new Vue({
el: "#app2",
data: { currentRoute: window.location.hash, navs: Object.freeze(routes) },
computed: {
currentComponent: function () {
const com = this.navs.filter(item => '#/' + item.url === this.currentRoute)[0];
if (com && com.component) {
document.title = com.title;
return com.name;
}
return 'not-found';
}
},
components: components,
created: function () {
window.addEventListener("hashchange", () => {
this.currentRoute = window.location.hash;
});
}
});
</script>

1.3、history路由原理

history 是历史对象,存放当前文档页面(或框架)的会话历史记录(不是浏览器的所有历史记录)。

history 属性/方法 描述
length 会话历史列表的记录数量
state 表示历史堆栈顶部记录的状态值,可以是任意可序列化JavaScript对象,限制为2MB
pushState(stateObj, title[, url]) 向当前会话的历史堆栈中添加一条记录
replaceState(stateObj, title[, url]) 修改 history 对象的当前(栈顶)记录
back() 返回到(历史列表中)上一个URL地址。
forward() 前进,加载(历史列表中)下一个URL地址
go(number) 加载指定相对当前网页索引位置的历史列表URL地址,go(-1)等同于back()

pushStatereplaceState 是HTML5在history上新增的API,用来新增、修改当前文档的历史记录,这两个API就是用来实现SPA单页应用前端路由的关键。他们的参数相同:(stateObj, title[, url])

  • state:一个关联历史会话记录的状态对象,主要作用是在触发 popstate事件时作为参数传递,不需要可以为null,通过history.state可以获取到当前会话的state
  • title:新页面的标题,大部分浏览器都没有管他,可以空着。
  • url:网址,可以相对、绝对地址,但不可跨域。这个url会更新到浏览器地址栏,但并不会加载该url地址,也不检查是否存在,页面也不会刷新!对,要的就是你不刷新。

基于这两个API的特性来实现前端路由。用 pushState还是 replaceState呢?两者作用一样的,唯一的不同就是pushState会产生历史记录,可用于前进、后退。

监测url地址变化

  • popstate 事件:当state变化时触发该事件,在事件中获取当前url地址。pushState、replaceState并不会触发popstate事件,前进、后退、跳转才会触发。
  • 点击事件:绑定导航按钮的click事件,pushState()更新url

加载资源:根据url值匹配不同资源进行加载、切换。

注意,页面第一次加载的时候,不会触发popstate事件。

history-简易路由示例:codepen

<div id="app3">
<ul id="nav" v-once>
<li v-for="item in navs" v-if="item.title">
<a href="#" v-on:click.prevent="navClick(item)">{{item.title}}</a></li>
</ul>
<div id="main">
<keep-alive>
<component v-bind:is="currentComponent"></component>
</keep-alive>
</div>
</div>
<script>
//components
const NotFound = { template: '<p>404!Page not found</p>' };
const Home = { template: '<p>首页<br>Home page</p>' };
const Product = { template: '<p>产品页面<br>Product page<br><input></p>' };
const About = { template: '<p>关于我们<br>About page</p>' };
//导航路由数据
function Route(title, url, name, component) {
this.title = title; this.url = url; this.component = component; this.name = name;
}
let routes = [
new Route("首页", "home", 'home', Home), new Route("商品", "protect", 'protect', Product),
new Route("招聘", "hr", null, null), new Route("关于", "about", 'about', About),
new Route(null, "not-found", 'not-found', NotFound)];
let components = {};
routes.forEach(item => { components[item.name] = item.component }); //拦截history.pushState,触发一个事件。不拦截换其他方式也可以,比如点击事件里。
history.pushState = (function (type) {
let origin = history[type]; //用闭包来存储原来的方法
return function () {
let out = origin.apply(this, arguments);
let event = new Event(type); //触发一个自定义事件pushState
event.arguments = arguments;
window.dispatchEvent(event);
return out;
}
})('pushState');
//app
let app3 = new Vue({
el: "#app3",
data: { currentRoute: history.state?.url, navs: Object.freeze(routes) },
computed: {
currentComponent: function () {
const com = this.navs.filter(item => item.url === this.currentRoute)[0];
if (com && com.component) {
document.title = com.title;
return com.name;
}
return 'not-found';
}
},
components: components,
methods: {
navClick: function (route) {
history.pushState({ url: route.url }, null, route.url);
}
},
created: function () {
window.addEventListener("popstate", () => {
this.currentRoute = history.state?.url; //也可以用location.pathname获取前端url
});
window.addEventListener("pushState", () => { //监听自定义事件pushState
this.currentRoute = history.state?.url;
});
}
})
</script>

刷新页面时会重新加载当前(本地路由的)url地址,可能就404了,这就需要服务端支持,修改下nginx代理也是可以解决的。
history与hash的主要区别,就是不会出现一个#,看上去更加美观?好像也没啥区别吧!


02、开始vue-router

2.1、简介

Vue Router是Vue官方推出的路由组件,与Vue深度集成,支持hashhistory两种模式。

2.2、安装使用

  • 通过<script>标签直接引用vue-router.js
<script src="https://unpkg.com/vue@2/dist/vue.js"></script>
<script src="https://unpkg.com/vue-router@3/dist/vue-router.js"></script>
// 注册插件
Vue.use(VueRouter);
  • 通过 vue-cli 脚手架搭建vue的开发框架,集成了vue-router组件。
  • 注册插件:Vue.use(VueRouter)

03、vue-router3入门

3.1、Router选项

Router选项 描述
routes 路由记录配置信息,Array<RouteConfig>
mode 路由模式,默认hash,选项:hashhistory、abstract(NodeJS环境)
base url的基本路径,"/app/",只有history模式有效?
linkActiveClass <route-link>激活的class名称,默认值为router-link-active
linkExactActiveClass 精确匹配激活的class,默认值为router-link-exact-active ( exact /ɪɡˈzækt/ 精确)
scrollBehavior 路由切换完成后的滚动行为回调,函数 Func(to, from, savedPosition)
parseQuery/stringifyQuery 自定义查询字符串的解析/反解析函数
//创建路由器
let vrouter = new VueRouter({ routes: vroutes, mode: 'hash', base: '/vsystem/' });
routes.routeRouteConfig 初始化时配置用的路由记录RouteConfig ,在后续代码中使用的为路由对象
path 路由url路径 path: '/user'
component Component 组件,可用函数方式import懒加载组件,提高初始化的性能
components 命名视图组件,当有多个命名视图<router-view>时,也要配置对应的组件
name 给路由取个名字,自己用,没其他用途,可作为显示的中文标题
redirect 重定向路由,重定向到另外的path、route。如果带有query会解析路由出错?
alias path的别名,可一个或多个(数组Array<string>)别名,渲染组件一样
parent 父级路由,根级的parentundefined
children 子路由Array<RouteConfig>,组件内用<router-view>组件作为嵌套组件的容器
props 用于给Vue组件参数Props传值:boolean | Object | Function
- true:自动传递动态路径参route.params
- 对象,函数:把它们的结果赋值给组件props参数(按key)
beforeEnter(to, from, next) 执行路由前的一个钩子,私有的钩子,目的同全局的守卫钩子beforeEach
meta 路由元信息,自定义的个性化配置,在路由钩子中可以访问处理。meta:{title:'注册'}
️运行态的 $route路由对象 组件内this.$route访问,钩子函数、导航函数中的to、from、location都是此路由对象
path 路由url路径
fullPath 解析后的完整url,包含query
params 存放动态路径参数,{key:value }对象,组件内使用this.$route.params.id
query url查询参数,{key:value }对象
hash 当前路由的哈希hash
name 路由名称
meta 元数据记录
matched 匹配到的路由记录列表
interface RouteConfig = {
path: string,
component?: Component,
name?: string, // 命名路由
components?: { [name: string]: Component }, // 命名视图组件
redirect?: string | Location | Function,
props?: boolean | Object | Function,
alias?: string | Array<string>,
children?: Array<RouteConfig>, // 嵌套路由
beforeEnter?: (to: Route, from: Route, next: Function) => void,
meta?: any,
// 2.6.0+
caseSensitive?: boolean, // 匹配规则是否大小写敏感?(默认值:false)
pathToRegexpOptions?: Object // 编译正则的选项
}
//$route路由对象
{
name: "user-box", // 路由名称
fullPath: "/user/21/vip?key=admin",
hash: "", // 当前路由的哈希
matched: [{… }],
meta: {},
params: { id: '21', type: 'vip' },
path: "/user/21/vip",
query: { key: 'admin' }
}

简单的示例:

<style>
.router-link-active{ background-color: rgb(168, 240, 140); }
.nav-item{ margin: 0 10px; }
</style>
<div id="app">
<router-link v-for="r in this.$router.options.routes" :to="r.path" class="nav-item">{{r.name}}</router-link>
<router-view></router-view> <!-- 显示路由组件视图的容器,其实是就是一个动态组件 -->
</div>
<script>
//路由配置RouteConfig
let vroutes = [
{ path: '/user', name: '用户管理', component: { template: '<div>user component</div>' } },
{ path: '/login', name: '登录', component: { template: '<div>login component</div>' } }];
//创建路由器
let vrouter = new VueRouter({ routes: vroutes, mode: 'hash', base: '/vsystem/' });
//app
let app = new Vue({
el:"#app",
router: vrouter,
})
</script>

3.2、router实例-创建Router()

️router实例-属性 描述
app、apps Vue根实例,所有apps实例
options 参数选项
currentRoute 当前激活的路由信息对象
mode 路由模式:"hash" | "history" | "abstract"
START_LOCATION 初始导航的路由地址,route对象
️Router实例-方法 描述
全局的导航守卫 beforeEach、beforeResolve、afterEach
编程式导航 push(route)、replace(route)、go(index)、back()、forward()
resolve() 解析目标位置
addRoute(parent?, RouteConfig) 添加路由记录、子路由,还有批量添加的addRoutes(routes)
getRoutes() 获取所有活跃的路由记录列表 Array<RouteConfig>
onReady(callback,errorback) 完成初始化后调用,初始化错误则调用errorback
onError(callback) 路由过程中出错时触发,算是一个全局路由异常捕获
  1. 注册插件:Vue.use(VueRouter)
  2. 创建全局共享的router路由器实例,并配置路由记录。
  3. 注入router,在根Vue组件上注入router实例,然后所有地方都可以用 this.$router访问了.
  4. <router-link>显示路由导航,<router-view>显示组件视图。
  5. 愉快的使用了,在Vue组件中访问路由的几种途径:
    • this.$router,Vue中任意地方可以访问的路由器。
    • this.$route,组件所属的route路由对象。

创建一个路由:

<style>
#app4 a { margin: 0 5px; }
.router-link-active { background-color: blueviolet; color: #FFF; }
</style>
<div id="app4">
<p>{{$router.mode}}-->{{$router.currentRoute.name}}</p>
<div>
<router-link to="/user/001">用户001</router-link>
<router-link to="/login">登录</router-link>
<a href="#" @click.prevent="$router.push('/user/002')">a-用户0002</a>
</div>
<hr>
<router-view></router-view>
</div>
<script>
// 注册插件
Vue.use(VueRouter);
let isAuthenticated = true;
//组件
const UserBox = { Prop: ['userId'], template: '<p>用户信息:{{$route.params}}</p>' };
const Login = { template: '<p>用户登录:<br>用户名:<input></p>' };
//路由配置
let vroutes = [
{ path: '/user/:userId', name: '用户管理', component: UserBox },
{ path: '/login', name: '登录', component: Login, meta: { type: 'vip' } },
{ path: '/*', redirect: '/login' }];
//创建路由器
let vrouter = new VueRouter({ routes: vroutes, mode: 'hash', base: '/vsystem/' });
//路由器的钩子:做一个登录权限验证,并更新文档标题
vrouter.beforeEach((to, from, next) => {
if (to.name !== 'Login' && !isAuthenticated) next({ name: 'Login' });
else next();
document.title = to.name;
});
//app
let app4 = new Vue({
el: "#app4",
router: vrouter, //注入路由器,内部通过 this.$router 访问
})
</script>

3.3、path路径:string

path为路由的地址,当浏览器url地址与path匹配时,就会激活当前route路由对象,并显示器对应组件component/components

let u1 = { path: '/home', component: Home };
let u2 = { path: '/about', component: About };
let u3 = { path: '/user/register', component: Register };
let u3 = { path: '/*', component: NotFound404 };
//动态路径
let u1 = { path: '/user/:id/:type', component: UserBox }
//匹配的路径
<router-link to="/user/1/vip">用户1</router-link>

: 动态路径参数path中可以设置动态参数,冒号:开头,后面的为参数,支持多个顺序组装:path:'/path/:参数1/:参数2'。这里的参数有什么用呢?

  • 参数都会被放到到路由对象$route.params中。
  • 组件内部直接使用:$route.params.id
  • 通过参数专递,设置路由记录props:true,参数值$route.params会传递给组件的参数Props

*通配符*通配符匹配任意字符,可放到最后面匹配404,或重定向到默认路由。v4版本里删了,改用正则。

优先级:如果相同的path,匹配哪个呢?按照代码的顺序,先到先得。

3.4、router-link/router-view

  • <router-link>路由导航组件,绑定路由配置,执行路由跳转。<router-link>也是一个组件,实际是一个<a>元素。
  • <router-view>路由视图组件,用来显示渲染匹配的视图组件,内部是一个Vue动态组件。如果需要动画和缓存,可以外面嵌套<transition><keep-alive>使用。
<router-link> 描述
to path,路由的目标地址,字符串、路由对象。
replace 默认false=push,执行导航是用replace,还是push,对应history的两个Api
append 是否添加基路径base,默认false
tag 最终渲染的的标签,默认av4中删掉了,用v-slot实现自定义
active-class 激活的类class名
exact 是否精确匹配连接地址,默认false。就是说默认是模糊匹配连接地址的,只要包含就激活了
event 触发路由的事件类型,默认click。不怎么常用,v4版本中删掉了
v-slot 作用域插槽,用来接收暴露出来的数据,<router-link>支持插槽
<router-view>
name 命名视图,当有多个就需要名字了,如切换框架布局。在路由记录components中配置映射关系
<style>
#app a{ margin: 0 10px; }
.router-link-active { background-color: rgb(168, 240, 140); }
/* 动画css */
.v-enter, .v-leave-to { opacity: 0; }
.v-enter { transform: translateX(30px); }
.v-enter-active, v-leave-active { transition: all 1s; }
</style>
<div id="app">
<div>
<h4>router-link</h4>
<router-link to='/user/1/vip'>用户管理1</router-link>
<router-link to='/login'>登录</router-link>
<!-- url变了,但没有触发路由 -->
<a href="#user/2/vvip">a-用户2</a>
<a href="#" @click.prevent="$router.push('/user/003/vvip')">a-用户3</a>
</div>
<div>
<h4>v-for绑定</h4>
<router-link v-for="r in this.$router.options.routes" :to="r.path">{{r.name}}</router-link>
</div><hr>
<transition>
<keep-alive>
<router-view style="margin:10px"></router-view>
</keep-alive>
</transition>
</div>
<script>
//路由配置RouteConfig
let vroutes = [
{ path: '/user/:id/:type', name: '用户管理', component: { template: '<div>user component{{$route.params}}</div>' } },
{ path: '/login', name: '登录', component: { template: '<div>login component<br><input></div>' } }];
//创建路由器
let vrouter = new VueRouter({ routes: vroutes, mode: 'hash', base: '/vsystem/' });
//app
let app = new Vue({
el: "#app",
router: vrouter,
})
</script>

<router-link>需要监听原生事件时,要加上原生修饰符@click.native="nav_click"

3.5、编程式导航

除了使用申明式导航<router-link>组件,也可也使用编程式的导航方法自定义实现导航,就是调用router提供的导航方法。

router实例-导航方法
push(location, onComplete?, onAbort?) location可以是url字符,也可以是route对象
replace(location, onComplete?, onAbort?) 同上,不会添加history记录,
go(index)、back()、forward() 和浏览器的history操作一样的,历史页面里跳转
  • 参数location(route对象)如果使用了path,则会忽略params (param /ˈpærəm/ 参数)。
  • 回调 onComplete?、onAbort?,在 3.1.0+,push、replace支持了Promise,会返回一个Promise对象,可链式调用了:

this.$router.push('/user').then(onComplete).catch(onAbort)

<div>
<h4>a标签,编程式导航</h4>
<a href="#" @click.prevent="$router.push({path:'user/21/vip',query:{key:'admin'}})">用户1</a>
<a href="#" @click.prevent="navClick">click登录</a>
<a @.prevent href="#/login?key=hello">原生a</a>
<br>
<a href="#" @click.prevent="$router.back()">后退</a>
<a href="#" @click.prevent="$router.forward()">前进</a>
</div>
<script>
let app = new Vue({
el: "#app",
router: router,
methods: {
navClick() {
if (this.$router.currentRoute.path == '/login')
return;
this.$router.push('/login', null, () => { }); //提供一个空的onAbort
this.$router.replace('/login');
this.$router.push('/user').then(onComplete).catch(onAbort); //promise方式使用
//设置了path,params的设置就忽略了
this.$router.push({ path: '/login', query: { key: 'admin' }, params: { id: 100 } });
//也可以用name进行导航。注意后面的catch,因为push、replace都是用promise执行的
this.$router.push({ name: '登录', query: { key: 'admin' }, params: { id: 12 } }).catch(s => { });
}
}
})
</script>

️ 这里遇到一个小问题,就是通过编程事件导航的<a>链接重复点击报错:NavigationDuplicated

原来是vue-router的一个问题,3.*版本中引入了promise时也引入了这个bug,如果路由没变化(重复)就会抛出一个异常的promisev4.*版本都出来了,这个bug还没修复!<router-link>正常,只有导航编程才会。

解决方法

  1. 判断一下当前路由是否已存在。
  2. 提供一个空的onAbort回调,或者promise的方式捕获异常。
  3. 改造一下VueRouter.prototypepush方法。

3.6、导航守卫-钩子

在导航过程中提供多种守卫(钩子函数),需要注意的是,动态path参数、<keep-alive>都会复用组件,此时组件的生命周期就不完整了,需要根据实际情况选择合适的地方。

️router实例-全局钩子守卫 描述
beforeEach(to, from, next) 导航执行前,可通过next取消。可用来验证登陆权限,如果没认证则跳转到登陆
beforeResolve(to, from, next) beforeEach执行后,也是前置守卫
afterEach(to, from) 导航已经离开时触发,这里没有next(不可取消路由),因为已经离开了
路由配置记录route-的独有钩子
beforeEnter(to, from, next) 执行路由前调用
Vue组件中新增的-钩子守卫 Vue组件的钩子
beforeRouteEnter(to, from, next) 进入前:组件路由被confirmed(已确认)前,组件还没创建,不能获取this
beforeRouteUpdate(to, from, next) 只有动态path参数复用组件时才触发,更新当前路由。
beforeRouteLeave(to, from, next) 路由将要离开该组件前触发,this可用,next(false)可取消。

钩子的参数(to, from, next)

  • to: Route:目标路由对象。
  • from: Route:当前导航路由对象,也是要离开的。
  • next: Function:本次路由怎么执行?内置的回调,必须调用。
    • nex()/next(true):允许执行,并继续,全部钩子执行完毕,导航状态为confirmed(已确认)。
    • next(false)不执行,中断当前导航,重置导航到from。
    • next({route}):中断当前导航,并进行一个新的导航到route
    • 特殊next(callback)beforeRouteEnternext接收一个回调函数,参数为组件vm,可用来请求一些ajax数据,回调会在组件创建后调用。

导航守卫-钩子的生命周期流程图,守卫钩子测试代码: CodePen

使用意见

  • 如果只是对路由做校验或逻辑处理,建议用路由的全局钩子守卫beforeEach,若只是针对某个特定路由,则用路由记录的独有钩子beforeEnter
  • 如果是需要基于组件做一些操作,如数据加载、未保存提示,则用Vue组件的路由守卫钩子。
  • 在复用组件时,通过watch监测路由对象$route的变化也是一个途径。

04、vue-router4 区别

  • 函数创建createRouter({ }),没有之前的类创建了。
  • mode路由模式:mode没了,变成了函数创建historycreateWebHistory()createWebHashHistory()
const router = createRouter({
history:createWebHashHistory() / createWebHashHistory(),
routes: [],
})
  • base放到了上面的创建函数参数里。
  • 实例函数router.onReady() 改为 IsReady(),该方法返回一个Promise
  • <router-view> 支持了插槽v-slot,支持就算了,关键是影响有点大。
    • <keep-alive><transition> 只能通过插槽嵌入到<router-view>里面,不像之前是放到外面的。
    • <router-view> 组件的模板也只能通过v-slot + 动态组件来实现了。
<router-view v-slot="{ Component }">
<transition>
<keep-alive>
<component :is="Component" />
</keep-alive>
</transition>
</router-view>
  • <router-link>tag 没了,通过插槽实现。
  • 所有的导航现在都是异步的。

05、其他问题

如何构建多级菜单的导航?基本思路:

  • 首先是路由菜单数据,应该是后台数据库统一管理,包括菜单名称、编码、图标、路径path、上下级结构信息等等。
  • 菜单是多级的,这由功能架构来决定,路由还是一级的,因为视图区域是一致的。so,从上述数据中构建2份数据,一份实现多级Dom菜单,另外一份构建路由配置数据。

多标签怎么实现,可以管理使用多个标签?基本思路:

  • 首先要记录打开的路由信息,可以通过路由守卫拦截监测。
  • 用一个标签栏来显示这些打开的路由信息,自己实现切换路由即可。
  • 菜单、标签相互联动,标签删除时需按照一定规则路由到下一个标签上。
  • 刷新保存视图状态:vuex存储菜单、标签显示状态。


️版权申明:版权所有@安木夕,本文内容仅供学习,欢迎指正、交流,转载请注明出处!原文编辑地址-语雀

vue-router路由之路-极简教程的更多相关文章

  1. Asky极简教程:零基础1小时学编程,已更新前8节

    Asky极简架构 开源Asky极简架构.超轻量级.高并发.水平扩展.微服务架构 <Asky极简教程:零基础1小时学编程>开源教程 零基础入门,从零开始全程演示,如何开发一个大型互联网系统, ...

  2. vue与TypeScript集成配置最简教程

    https://blog.csdn.net/u014633852/article/details/73706459 https://segmentfault.com/a/119000001187808 ...

  3. Nginx 极简教程(快速入门)

    作者:dunwu github.com/dunwu/nginx-tutorial 推荐阅读(点击即可跳转阅读) 1. SpringBoot内容聚合 2. 面试题内容聚合 3. 设计模式内容聚合 4.  ...

  4. nginx极简教程

    Nginx 极简教程 本项目是一个 Nginx 极简教程,目的在于帮助新手快速入门 Nginx. examples 目录中的示例模拟了工作中的一些常用实战场景,并且都可以通过脚本一键式启动,让您可以快 ...

  5. Typora极简教程

    Typora极简教程 ” Markdown 是一种轻量级标记语言,创始人是约翰·格鲁伯(John Gruber).它允许人们 “使用易读易写的纯文本格式编写文档,然后转换成有效的 HTML 文档.” ...

  6. CentOS安装使用.netcore极简教程(免费提供学习服务器)

    本文目标是指引从未使用过Linux的.Neter,如何在CentOS7上安装.Net Core环境,以及部署.Net Core应用. 仅针对CentOS,其它Linux系统类似,命令环节稍加调整: 需 ...

  7. 前端MVC Vue2学习总结(八)——Vue Router路由、Vuex状态管理、Element-UI

    一.Vue Router路由 二.Vuex状态管理 三.Element-UI Element-UI是饿了么前端团队推出的一款基于Vue.js 2.0 的桌面端UI框架,手机端有对应框架是 Mint U ...

  8. Python 极简教程(八)字符串 str

    由于字符串过于重要,请认真看完并保证所有代码都至少敲过一遍. 对于字符串,前面在数据类型中已经提到过.但是由于字符串类型太过于常用,Python 中提供了非常多的关于字符串的操作.而我们在实际编码过程 ...

  9. 【转】Typora极简教程

    Typora极简教程 Typora download ” Markdown 是一种轻量级标记语言,创始人是约翰·格鲁伯(John Gruber).它允许人们 “使用易读易写的纯文本格式编写文档,然后转 ...

  10. NodeJS 极简教程 <1> NodeJS 特点 & 使用场景

    NodeJS 极简教程 <1> NodeJS 特点 & 使用场景 田浩 因为看开了所以才去较劲儿.   1. NodeJS是什么 1.1 Node.js is a JavaScri ...

随机推荐

  1. 工业互联网领域的企业,都已经接入了ERP或者MES系统了吗?

    肯定不是得啊!之前的两化,后来的企业上云,到当下的智能制造.数字化转型,不都是想把制造业(也就是你说的工业互联网企业)往这个方向推么,ERP和MES是企业数字化的一部分,但不是全部,当然有的企业(小工 ...

  2. C#-02 传入参数的一些用法2

    C#_02 参数应用2 一.关于 "ref" 局部变量和 "ref" 返回 在前面已经明白了 ref 关键词传递一个对象引用给方法调用,这样在方法中对对象修改过 ...

  3. C#-11 接口

    一 什么是接口 接口是指定一组函数成员而不实现它们的引用类型. class Program { static void FlyFunc(IFly obj) { obj.Fly(); } static ...

  4. PHP全栈开发(四): HTML 学习(1.基础标签+表格标签)

    简单的学习一下HTML 学习HTML采用在www.runoob.com上学习的方法. 而且该网站还提供在线编辑器. 然后HTML编辑器使用Notepad++ 记得上Emmet的官网http://emm ...

  5. NOI2018 D1T1 洛谷P4768 归程 (Kruskal重构树)

    实际上是一个最短路问题,但加上了海拔这个条件限制,要在海拔<水位线p中找最短路. 这里使用Kruskal重构树,将其按海拔建成小根堆,我们就可以在树中用倍增找出他不得不下车的点:树中节点有两个权 ...

  6. RAID5 IO处理之replace代码详解

    1 作用 从字面意思理解,replacement即是替换.我们知道硬盘都有一定的使用寿命,可以在硬盘失效之前通过该功能将就盘的数据迁移至新盘.因为replacement的流程是从旧盘中读出数据直接写入 ...

  7. 使用 Apache Hudi 实现 SCD-2(渐变维度)

    数据是当今分析世界的宝贵资产. 在向最终用户提供数据时,跟踪数据在一段时间内的变化非常重要. 渐变维度 (SCD) 是随时间推移存储和管理当前和历史数据的维度. 在 SCD 的类型中,我们将特别关注类 ...

  8. 9.MongoDB系列之创建副本集(二)

    1. 如何设计副本集 大多数:选取主节点时需要由大多数决定,主节点只有在得到大多数支持时才能继续作为主节点,写操作被复制到大多数成员时就是安全的写操作.这里的大多数定义为"副本集中一半以上的 ...

  9. 发送HTTP请求方法- 留着自用

    /** * 发送HTTP请求方法,目前只支持CURL发送请求 * @param string $url 请求URL * @param array $data POST的数据,GET请求时该参数无效 * ...

  10. 使用request对象进行简单的注册以及信息显示

    Request内置对象的使用 概述:request对象主要用于接收客户端发送的请求信息,客户端的请求信息被封装在request对象中,通过它才能了解到客户的需求,然后做出响应.封装了用户提交的信息.在 ...