2
现在的前端框架都会配备一个路由框架,vue有vue-router,react有react-router,angular也有angular-route,当然还有一些优秀插件来完善这些router的做更多的事情

安装

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,看下方图片渲染结果:
image.png

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: "用户" } }
    ]
})

image.png

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包

image.png

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')
})

image.png

路由组件内守卫

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,执行顺序大概是这样的

image.png

当我点击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组件所执行的顺序

image.png

1、beforeRouteLeave //路由组件内守卫
2、beforeEach // 全局前置守卫-路由进入开始
3、beforeEnter // 导航路由独享守卫
4、beforeRouteEnter // 路由组件内前置守卫
5、beforeResolve //全局解析守卫
6、afterEach // 全局后置钩子

更多关于vue-route

动态组件 & 异步组件
滚动行为
导航守卫实践与解析

关于我

https://www.vipbic.com/.png


羊先生
1.9k 声望821 粉丝