专栏首页前端之旅Vue 全家桶学习笔记:Vue-router

Vue 全家桶学习笔记:Vue-router

1. 前端路由的出现

在以前,前后端是不分离的,这个阶段通常是由服务端渲染好页面(SSR),再发送页面给前端去展示;接着到了前后端分离的阶段,前端向静态资源服务器拿资源,再通过 js 渲染页面,此时仍然是一个 url 对应一份 html+css+js。再后来出现了 SPA 单页面应用的概念,实际上它只有一个页面,但给我们的体验是多页面之间的切换。

SPA 是基于路由和组件的,其中路由可以看作是它的一个路径管理器,路由和组件之间互相映射,路由的切换就是组件的切换。Vue 的前端路由也就是 vue-router。

2. vue-router 的模式

vue-router 提供了 hash 和 history 两种模式。

  • hash 模式:url 中带有 hash(#),hash 的改变并不会触发刷新或者请求,同时每一次改变 hash 后的部分,都会在浏览器的访问历史中增加一个记录 ,这使得我们可以来回切换;而要监听切换可以依靠 onhashchange 事件;
  • history 模式:html5 提供了 window.history 的 API,url 的改变同样不会触发页面刷新,并且由于历史记录是在 history 栈压入或弹出的,这使得我们可以来回切换。

实例化 vue-router 时会传入一个对象,可以给对象一个 option,如 mode:'history',从而决定 vue-router 使用哪种模式。

3. 安装

在安装 vue-cli 的时候可以顺便安装 vue-router,或者之后我们通过 npm install 的方式手动安装。

4. 使用

如果是通过脚手架安装 vue-router,src 下会多出一个 router 文件夹,里面的 index.js 帮我们生成了配置的基础结构。

index.js 大致是这样的:

// 导入要使用到的组件
import VueRouter from 'vue-router';     
import Vue from 'vue';                
import Home from '../components/Home.vue';
import About from '../components/About.vue';

Vue.use(VueRouter);        // 安装 vue-router

const routers = [                  // 配置路由和组件的映射关系
    {
       path:'/',
       redirect: '/home'
    }
    {
       path:'/home',
       components:Home
    },
    {
       path:'/about',
       components:About
    }
]

const router = new VueRouter({         // 实例化 VueRouter 对象
  routers
})

export default router;                // 导出 router 对象

同时,main.js 文件中,导入 router 并在 Vue 实例下挂载:

// main.js

import Vue from 'vue'
import App from './App.vue'
import router from './router'

Vue.config.productionTip = false

new Vue({
  router,
  render: function (h) { return h(App) }
}).$mount('#app')

接下来通过 <router-link><router-view> 使用路由:

// App.vue

<div id="app">
    <router-link to="/home">首页</router-link>        
    <router-link to="/about">关于</router-link>
    <router-view></router-view>
</div>

5. 路由嵌套(二级路由)

假设 home 路由下嵌套着两个子路由 newsmessage,即 /home/news/home/message,那么首先创建两个对应的组件,之后在 index.js 下配置好映射关系:

import HomeNews from '../components/HomeNews.vue'
import HomeMessage from '../components/HomeMessage.vue'

const routes = [                  // 配置 url和组件的映射关系
    {
       path:'/',
       redirect: '/home'
    }
    {
       path:'/home',
       component:Home,
       children:[
        {
           path:'/',                 // 默认展示 news
           redirect: 'news'
        }
        {
           path: 'news',
           component: HomeNews
        },
        {
           path: 'message',
           component: HomeMessage
        }
    ]
    },
    {
       path:'/about',
       component:About
    }
]

Home.vue 中使用子路由:

// Home.vue

<div>
    <h1>我是Home</h1>
    <router-link to="/home/news">news</router-link>
    <router-link to="/home/messaage">message</router-link>
    <router-view></router-view>
</div>

5. 动态路由匹配

有的时候,path 并不是固定的。比方 /user/Tom/user/Jack,这些 path 根据用户不同而不同,但都是展示用户页面的,我们希望满足这种格式的 path 都映射 User 组件,怎么办呢?可以在 path 中使用动态路径参数。

import HomeMessage from '../components/User.vue'

const routes = {
    {
        path:'/user/:userId',
        component: User
    }
}

注意,这里我们不再是写 /user/,而是写 /user/:userId,这是因为路径 user 后面的东西是动态的。那么,对于下面的三种 <router-link>,最后都可以成功映射到 User 组件:

// App.vue

<router-link to="/user/Tom"></router-link>           // 路径:/user/Tom
<router-link to="/user/Jack"></router-link>          // 路径:/user/Jack
<router-link :to="`/user/${userId}`"></router-link>   // 路径:/user/Bake
<router-view></router-view>

export default{
    data(){
        return {
            userId:'Bake'
        }
    }
}

如果想要在具体的 User.vue 中展示用户名,可以通过 $route.params 去访问:

// User.vue

<div>我的用户名是:{{$route.params.userId}}</div>
<div>我的用户名是:{{id}}</div>

export default{
    computed:{
        id(){
            return this.$route.params.userId;
        }
    }
}

6. 路由传参

6.1 基于动态路由

实际上,上面讲的动态路由就可以用来传递参数。上面例子的 path 还可以根据需要添加更多动态路径参数,如 '/user/:userId/:userJob/:userEmail',首先在 App.vue 拿到数据,传给<router-link>to,接着就可以在 User.vue 中通过 $route.params 去访问了。

6.2 给 to 传入对象

首先要明白,to 除了接收字符串之外,也可以接受对象。以前面的动态路由为例:

<!--可以这样写(直接传字符串)-->
<router-link :to="`/user/${userId}`"></router-link>

<!--也可以这样写(传对象,直接用字符串表示完整路径)-->
<router-link :to="{path:'`/user/${userId}`'}"></router-link>

那么,我们会自然想到,对于一个普通的路由,要传参的话把参数放在给 to 传入的对象中不就可以了吗?

于是可能会这么写:

<router-link :to="{path:'/article',params:{date:2020-1-1,title:'A new year'}}"></router-link>

然而这是错误的用法,事实上我们应该将 path 改为 name。不过在这之前,我们还是先给路由一个 name吧:

import Article from '../components/Article.vue'

// 首先给路由一个名字(命名路由)
const routes = {
    {
        path:'/article',
        name:'article',
        component: Article
    }
}

接着使用路由:

<router-link :to="{name:'article',params:{date:2020-1-1,title:'A new year'}}"></router-link>

获取参数:

<!--Article.vue-->

<div>{{$route.params.date}}</div>
<div>{{$route.params.title}}</div>
6.3 使用 query

这和上面的是差不多的,不同的是我们不使用 params,而是使用 query。query 实际上就是 url 中的查询参数。

这种情况下,我们给 to 传入的对象可以使用 path,也可以使用 name

<router-link :to="{name:'article',query:{date:2020-1-1,title:'A new year'}}"></router-link>
<!--等价于-->
<router-link :to="{path:'/article',query:{date:2020-1-1,title:'A new year'}}"></router-link>

获取参数:

<!--Article.vue-->

<div>{{$route.query.date}}</div>
<div>{{$route.query.title}}</div>

7. 路由跳转:声明式 VS 编程式

前面介绍的路由跳转/导航是通过声明式<router-link :to='...'> 实现的,我们也可以使用编程式this.$router.push('...') 实现:

// App.vue

<div id="app">
    <router-link to='/home'>可以点击这里进入首页</router-link> 
    <!--
    or: <router-link :to="{path:'/home'}">    
    -->
    <button @click="change">也可以点击这里进入首页</button>
    <router-view></router-view>
</div>

<script>
    export default{
        name:'App',
        methods:{
            change(){
                this.$router.push('/home');
              //or: this.$router.push({path:'/home'}) 
            }
        }
    }
</script>    

<router-link :to='...'> 实质上也是在内部调用了 push 方法,从而向 history 栈压入新记录,由于是栈的数据结构,所以可以自由前进和后退。除了 push,还有 replace(注意是直接替换而不是采用入栈出栈方式),go,这些和 window.history 的 API 是类似的。

this.$router.push('...') 同样接受字符串参数或者对象参数。

8. $router$route 的区别

  • $router 是我们 new 出来的 VueRouter 实例,它提供了一些跳转方法( pushreplacego )和钩子函数(后面导航守卫部分会讲解);
  • $route路由信息对象,可以理解为是当前活跃的路由,包括 path,params,hash,query,fullPath,matched,name 等路由信息参数。

9. 导航守卫

路由的导航守卫其实就是一些钩子函数,可以在路由跳转的流程中针对性地进行操作控制。

1. 全局守卫
  • 全局前置守卫:router.beforeEach((to,from,next) => {...})。可以在 index.js 或者路由组件中使用(通过 this.$router),next 必须调用。
  • 全局解析守卫: router.beforeResolve
  • 全局后置钩子:router.afterEach((to,from) => {...})。可以在 index.js 或者路由组件中使用(通过 this.$router),next 不需要调用。
2. 路由独享守卫

单个路由独享的守卫只有 beforeEnter 这一个,可以在配置路由时定义。

const router = new VueRouter({
  routes: [
    {
      path: '/home',
      component: Home,
      beforeEnter: (to, from, next) => {
        // ...
        next()  
      }
    }
  ]
})
3. 组件守卫

组件守卫只能在路由组件中定义:

  • beforeRouteEnter((to,from,next) => {...}): 进入路由前,此时实例还没创建,无法获取到 this
  • beforeRouteUpdate((to,from,next) => {...}) :路由复用同一个组件时触发,比如 /user/Tom/user/Jack
  • beforeRouteLeave((to,from,next) => {...}): 离开当前路由,此时可以用来保存数据,或数据初始化,或关闭定时器等等
4. 导航解析流程

10. 路由懒加载

懒加载也叫延迟加载,即在需要的时候进行加载,随用随载。在单页应用中,如果没有应用懒加载,运用webpack 打包后的文件将会异常的大,导致进入首页时,需要加载的内容过多,延时过长,不利于用户体验,而运用懒加载则可以将页面进行划分,需要的时候加载页面,可以有效的分担首页所承担的加载压力,减少首页加载用时

要使用路由懒加载,只需要替换掉原来的引用语句即可:

// import Home from '../components/Home.vue';
// import About from '../components/About.vue';

const Home = () => import('../components/Home');
const About = () => import('../components/About');

11. keep-alive

路由跳转的时候,比如 home -> about -> homehome 路由组件实际上是在不断地创建和销毁,我们可以用生命周期钩子函数证明这一点:

上图中,每次跳转的时候都会经历一次生命周期。但大部分时候,这种重新渲染是没有必要的,所以 Vue 提供了一个内置组件 keep-alive缓存组件内部状态,避免重新渲染

<keep-alive>
    <router-view></router-view>
</keep-alive>

keep-alive 包裹的路由/组件,状态会得到缓存。以上图为例,从 home 跳转到 about,home 不会被销毁,同样的,从 about 跳转到 home,about 不会被销毁,home 也不会被重新创建,而是用之前缓存好的组件。

  • keep-alive 提供了 activateddeactivated 两个钩子函数(在路由组件中定义),前者在当前路由组件激活时调用,后者在当前路由组件失活时调用。
  • keep-alive 提供了 3 个属性定义具体的缓存情况:
    • include 包含的组件(可以为字符串,数组,以及正则表达式,只有匹配的组件会被缓存)
    • exclude 排除的组件(以为字符串,数组,以及正则表达式,任何匹配的组件都不会被缓存)
    • max 缓存组件的最大值(类型为字符或者数字,可以控制缓存组件的个数)

参考:

从头开始学习vue-router

可能比文档还详细–VueRouter完全指北

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • Vue-router 学习笔记

    在以前,前后端是不分离的,这个阶段通常是由服务端渲染好页面(SSR),再发送页面给前端去展示;接着到了前后端分离的阶段,前端向静态资源服务器拿资源,再通过 js...

    Chor
  • 操作系统学习笔记-7:进程通信

    进程 A 无法直接访问进程 B 的地址空间,反之亦然,所以提供一块可以供 AB 访问的共享空间。这块共享空间属于互斥的临界资源。

    Chor
  • 使用 Vue 脚手架搭建项目

    vue-cli 也是一个 npm 包,可以帮助我们快速搭建起 vue 项目的脚手架。

    Chor
  • Vue-router 学习笔记

    在以前,前后端是不分离的,这个阶段通常是由服务端渲染好页面(SSR),再发送页面给前端去展示;接着到了前后端分离的阶段,前端向静态资源服务器拿资源,再通过 js...

    Chor
  • Vue-router从入门到弃坑

    html页面(依次引入vue.js,router.js以及个人配置的app.js)

    十月梦想
  • 15.vue中的路由vue-router

    npm install vue-router --save / cnpm install vue-router --save

    玩蛇的胖纸
  • vue.js路由实例讲解

    章鱼喵
  • 前端基础-Vue.js单页应用

    优缺点都很明显,但是我们都还没尝试过就来评价,就会显得空口无凭;接下来我们先来学习制作单页应用,然后再来进行点评;

    cwl_java
  • H3C Qos

    配置802.1p优先级到本地优先级映射表,将802.1p优先级3、4、5对应的本地优先级配置为2、6、4。保证访问服务器的优先级为研发部门(6)>管理部门(4...

    py3study
  • Vue-路由配置和使用步骤整理

    xing.org1^

扫码关注云+社区

领取腾讯云代金券