安装
npm i vue-router -S
基本使用
import Vue from './view-global'; import VueRouter from 'vue-router' import App from './micro/app.vue' import Home from './micro/home' import Login from './micro/login' import About from './micro/about' import User from './micro/user' const router = new VueRouter({ mode: 'hash', routes: [ { path: '/home', name: 'Home', component: Home, meta: { title: "主页" } }, { path: '/login', name: 'Login', component: Login, meta: { title: "登录" } }, { path: '/about', name: 'About', component: About, meta: { title: "关于" } }, { path: '/user', name: 'User', component: User, meta: { title: "用户" } } ] }) Vue.use(VueRouter) new Vue({ router, render: h => h(App), }).$mount('#app') // app.vue <template> <div> <router-link to="/home" class="default-styles"> Home </router-link><br> <router-link to="/login" class="default-styles">Login</router-link><br> <router-link :to="{ path: '/about' }" class="default-styles">About</router-link><br> <router-link :to="{ name: 'User'}" class="default-styles">User</router-link> <router-view/> </div> </template> <style lang="less" scoped> .default-styles{ color: gray; font-size: 14px; } .router-link-active{ color: red; font-size: 26px; } </style>
router-link-active
vueRouter
会自动添加这个类,当点击Home和Login来回切换时,发现html结构,a 标签有一个样式类 .router-link-active
也在来回切换, 原来这是当router-link 处于选中状态时,因此我们也可以利用这个类来改变选中时的状态,如选中时,让它变成红色,字体放大,如果未选中的也可在 router-link
直接给它添加一个 class
就可以了,比如自定义一个default-styles
router-link
属性有 to 、replace、 append、 tag、 active-class、 exact 、 event、 exact-active-class
to(必选参数):类型string/location
//字符串形式 <router-link to="/home">Home</router-link> //动态绑定 v-bind <router-link :to="'/home'">Home</router-link> <router-link :to="{ path: '/home' }">Home</router-link> <router-link :to="{ name: 'User'}">User</router-link> // 带参数 参数在url 获取参数 this.$route.query <router-link :to="{ name: 'User', query: {color: 'red' }}">query带参数</router-link> // 带参数 获取参数 this.$route.params <router-link :to="{ name: 'User', params: { color: 'red' }}">params带参数</router-link><br> tag 类型string,默认值a // 如果想要 <router-link> 渲染成某种标签,例如 <li> <router-link :to="'/home'" tag="li">Home</router-link> // 如果此时我们想要在这个li标签中添加a标签,如下所示,可以不为a标签添加href属性即可哦 <router-link :to="{ name: 'User', params: { color: 'red' }}" tag="li"> <a>User</a> </router-link>
可以看渲染结果,在这种情况下,<a>
将作为真实的链接 (它会获得正确的 href 的),而 "激活时的CSS类名" 则设置到外层的 li
,看下方图片渲染结果:
active-class : 类型 string
默认值:router-link-active
// 修改激活选中的class类名 <router-link :to="{ name: 'User', query: {color: 'red' }}" active-class="activeClass">带参数</router-link> // 全局修改 const router = new VueRouter({ mode: 'hash', linkActiveClass: 'activeClass', // 全局配置 routes: [ { path: '/home', name: 'Home', component: Home, meta: { title: "主页" } }, { path: '/login', name: 'Login', component: Login, meta: { title: "登录" } }, { path: '/about', name: 'About', component: About, meta: { title: "关于" } }, { path: '/user', name: 'User', component: User, meta: { title: "用户" } } ] })
exact-active-class 类型 string
默认值:router-link-exact-active
配置当链接被精确匹配的时候应该激活的 class。注意默认值也是可以通过路由构造函数选项 linkExactActiveClass 进行全局配置的,看起来有点模糊,举个栗子,例如:
<router-link to="/article" active-class="router-active"></router-link> // 当用户访问 /article/1 时会被激活为 <a href="#/article" class="router-active"></a> // 修改一下 <router-link to="/article" exact-active-class="router-active"></router-link> // 当用户访问 /article/1 时会被激活为,不会激活这个link的class <a href="#/article"></a>
router-link 默认情况下的路由是模糊匹配
exact 类型 boolean
默认值:false
"是否激活" 默认类名的依据是 inclusive match (全包含匹配), 举个例子
<li><router-link to="/">全局匹配</router-link></li> <li><router-link to="/" exact>exact严格匹配</router-link></li>
简单点说,第一个的话,如果地址是/a,或/a/b,……都会匹配成功,
但加上exact,只有当地址是/时被匹配,其他都不会匹配成功
event 类型: string | Array<string> 默认值: click
<router-link to="/article" event="mouseover">article</router-link>
如果我们不加event
,那么默认情况下是当我们点击article
的时候,跳转到相应的页面,但当我们加上event
的时候,就可以改变触发导航的事件,比如鼠标移入事件
replace 类型: boolean
默认值: false
设置 replace 属性的话,当点击时,会调用 router.replace() 而不是 router.push(),于是导航后不会留下 history 记录
push replace go 之间的区别
router.push() :导航跑到不同的URL,这个方法会向history栈添加一个新的记录,所以,当用户点击浏览器后退按钮时,则回到之前的url
router.replace(): 跟router.push作用是一样的,但是,它不会向history添加新记录,而是跟它的方法名一样替换掉当前的history记录
router.go(n): 这个方法的参数是一个整数,意思是在history记录中向前或者后退多少步,类似window.history.Go(n)
append 类型: boolean
默认值: false
设置 append 属性后,则在当前 (相对) 路径前添加基路径
<router-link to="a" append>Home</router-link>
设置append属性后,则在当前路径前添加基路径,例如,我们从/a导航到一个相对路径b,如果没有配置append,则路径为/b,如果配了,则为/a/b
快速设置title
Router.beforeEach((to,from,next) => { window.document.title= to.meta.title })
滚动行为
使用前端路由,当切换到新路由时,想要页面滚到顶部,或者是保持原先的滚动位置,就像重新加载页面那样。 vue-router 能做到,而且更好,它让你可以自定义路由切换时页面如何滚动
// 2.8.0 新增 const router = new VueRouter({ scrollBehavior (to, from, savedPosition) { return new Promise((resolve, reject) => { setTimeout(() => { resolve({ x: 0, y: 0 }) }, 500) }) } })
利用路由元信息更细颗粒度地控制滚动,可以查看github官方列子
路由懒加载
官方描述:当打包构建应用时,JavaScript 包会变得非常大,影响页面加载。如果我们能把不同路由对应的组件分割成不同的代码块,然后当路由被访问的时候才加载对应组件,这样就更加高效了
加载方式
非按需加载
{ path: '/user', name: 'User', component:require(['./micro/user'], resolve), meta: { title: "用户" } }
按需加载
{ path: '/user', name: 'User', component: (resolve) => require(['./micro/user'], resolve), meta: { title: "用户" } } // 或者使用webpack的require.ensure技术 { path: '/user', name: 'User', component: resolve => require.ensure([], () => resolve(require('./micro/user')), 'user'), meta: { title: "用户" } }
按需加载会在页面第一次请求的时候,把相关路由组件块的js添加上;非按需加载则会把所有的路由组件块的js包打在一起。
当业务包很大的时候建议用路由的按需加载(懒加载),可以看我网站,打开控制台,当点击左侧菜单,会发现加载相对应的js包
require.ensure
参数解释
第一个参数的依赖关系是一个数组,代表了当前需要进来的模块的一些依赖
第二个参数回调就是一个回调函数其中需要注意的是,这个回调函数有一个参数要求,通过这个要求就可以在回调函数内动态引入其他模块值得注意的是,虽然这个要求是回调函数的参数,理论上可以换其他名称,但是实际上是不能换的,否则的的的的WebPack就无法静态分析的时候处理它
第三个参数errorCallback比较好理解,就是处理错误的回调
第四个参数chunkName则是指定打包的组块名称
异步组件
// setTimeout 演示ajax const User= Vue.component('later', function (resolve) { setTimeout(function () { require(['./user.vue'], resolve) }, 3000); }); components: { User },
注意异步组件页面渲染的时候会跳动,使用下方高级异步组件,解决体验问题
// 更高级的异步组件 const AsyncComponent = () => ({ // 需要加载的组件 (应该是一个 `Promise` 对象) component: import('./MyComponent.vue'), // 异步组件加载时使用的组件 loading: LoadingComponent, // 加载失败时使用的组件 error: ErrorComponent, // 展示加载时组件的延时时间。默认值是 200 (毫秒) delay: 200, // 如果提供了超时时间且组件加载也超时了, // 则使用加载失败时使用的组件。默认值是:`Infinity` timeout: 3000 })
hash模式和history模式
hash模式:
在浏览器中符号“#”,#以及#后面的字符称之为hash,用window.location.hash读取; 特点:hash虽然在URL中,但不被包括在HTTP请求中;用来指导浏览器动作,对服务端安全无用,hash不会重加载页面。 hash 模式下,仅 hash 符号之前的内容会被包含在请求中,如 https://www.vipbic.com,因此对于后端来说,即使没有做到对路由的全覆盖,也不会返回 404 错误
简单实现hash
<div id="app"> <a class="hash a">#a.html</a> <a class="hash b">#b.html</a> </div> <script> // 再点击时注意观看浏览器地址栏 document.querySelectorAll('.hash').forEach( function(item, index) { item.addEventListener('click',(e)=>{ e.preventDefault() var link = item.textContent; location.hash= link; },false) }); //每点一下都会触发-hashchange window.addEventListener('hashchange', (e)=>{ console.log({ location: location.href, state: location.hash, e:e }) }); </script>
history模式:
history采用HTML5的新特性;且提供了两个新方法:pushState(),replaceState()可以对浏览器历史记录栈进行修改,以及popState事件的监听到状态变更。 history 模式下,前端的 URL 必须和实际向后端发起请求的 URL 一致,如
http://www.xxx.com/items/id。后端如果缺少对 /items/id 的路由处理,将返回 404 错误。Vue-Router 官网里如此描述:“不过这种模式要玩好,还需要后台配置支持……所以呢,你要在服务端增加一个覆盖所有情况的候选资源:如果 URL 匹配不到任何静态资源,则应该返回同一个 index.html 页面,这个页面就是你 app 依赖的页面。
简单实现history
<div id="app"> <a class="api a">a.html</a> <a class="api b">b.html</a> </div> <script> // 再点击时注意观看浏览器地址栏 document.querySelectorAll('.api').forEach( function(item, index) { item.addEventListener('click',(e)=>{ e.preventDefault() var link = item.textContent; window.history.pushState({name:'api'},link,link); },false) }); // 下面说一下replaceState,其实跟pushState差不多,只是replaceState()修改当前历史记录条目,而不是创建一个新的。 // 在点击浏览器的后腿前进按钮时会触发-popstate window.addEventListener('popstate', (e)=>{ console.log({ location: location.href, state: e.state, e:e }) }); </script>
vue-router导航守卫的类型
1、全局守卫
2、路由独享守卫
3、路由组件内的守卫
全局守卫
vue-router全局有三个守卫
router.beforeEach 全局前置守卫 进入路由之前
router.beforeResolve 全局解析守卫(2.5.0+) 在beforeRouteEnter调用之后调用
router.afterEach 全局后置钩子 进入路由之后
全局导航守卫的执行顺序
例如从 /Home 页面跳转到 /User,全局导航守卫执行顺序大概是这样的
router.beforeEach((to, from, next) => { console.log('全局前置守卫: beforeEach') next() }) router.beforeResolve((to, from, next) => { console.log('全局解析守卫: beforeResolve') next() }) router.afterEach((to, from) => { console.log('全局后置钩子: afterEach') })
路由组件内守卫
beforeRouteEnter 进入路由前, 在路由独享守卫后调用 不能 获取组件实例 this,组件实例还没被创建
beforeRouteUpdate (2.2) 路由复用同一个组件时, 在当前路由改变,但是该组件被复用时调用 可以访问组件实例 this
beforeRouteLeave 离开当前路由时, 导航离开该组件的对应路由时调用,可以访问组件实例 this
beforeRouteEnter(to, from, next) { console.log('进入前:beforeRouteEnter') next() }, beforeRouteUpdate(to, from, next) { console.log('路由改变时:beforeRouteUpdate') // 比如user/123,切到user/456,才会触发 next() }, beforeRouteLeave(to, from, next) { console.log('离开前:beforeRouteLeave') next() }
路由组件内守卫的执行顺序
例如从 /Home 页面跳转到 /User 然后在跳到Home,执行顺序大概是这样的
当我点击User
时回调用路由组件内守卫的beforeRouteEnter
,然后当我点击About
时,会调用beforeRouteLeave
beforeRouteEnter
也可以先获取数据,等数据获取出来才真正的显示页面。
需要注意:这里是拿不到组件this,因为没有通过next之前,组件没有被创建,所以用this === undefined
,不能调用上的任何东西
beforeRouteUpdate注意点
对于user/123,切到user/456 这样的路由页面,一般在mounted获取数据,第二次进来的时候,不会被触发(数据不会更新),所以数据的初始化获取不要用mounted,最好使用beforeRouteUpdate
,或者使用watch,watch相对比较麻烦,而且不能控制路由跳转的行为
beforeRouteLeave
beforeRouteLeave:可以用来表单填写成功,是否保存提示等
导航路由独享守卫
const router = new VueRouter({ mode: 'history', routes: [ { path: '/user', component: User, beforeEnter: function guardRoute(to, from, next) { // 参数用法什么的都一样,调用顺序在全局前置守卫后面,所以不会被全局守卫覆盖 console.log('导航路由独享守卫:beforeEnter') next() } } ] })
总结路由执行顺序
比如从User
组件跳转到About
组件所执行的顺序
1、beforeRouteLeave //路由组件内守卫
2、beforeEach // 全局前置守卫-路由进入开始
3、beforeEnter // 导航路由独享守卫
4、beforeRouteEnter // 路由组件内前置守卫
5、beforeResolve //全局解析守卫
6、afterEach // 全局后置钩子