Vue Router详解

326 阅读5分钟

Vue Router 是 vue.js 的官方插件,用来快速实现单页应用。

单页应用

SPA(Single Page Application)单页面应用程序,简称单页应用。

单页,指的是网站的 “所有” 功能都在单个页面中进行呈现。

具有代表性的有后台管理系统、移动端、小程序等。

优点:

  • 前后端分离开发,提高了开发效率。
  • 业务场景切换时,局部更新结构。
  • 用户体验好,更加接近本地应用。

缺点:

  • 不利于 SEO。
  • 初次首屏加载速度较慢。
  • 页面复杂度比较高。

前端路由

前端路由,指的是 URL 与内容间的映射关系。

设置前端路由必备的条件就是:URL、内容、映射关系。

image.png

比如上面的单页每个内容对应一个URL的映射关系就是前端路由。

设置前端路由的两种方式:

  • Hash方式
  • History方式

Hash方式

通过 hashchange 事件监听 hash 变化,并进行网页内容更新,就是设置前端路由的Hash方式。

Hash 是 URL 的组成部分,代表符号是#,一般用作锚点在页面内的跳转,当你在看本文时,点击文章目录的各个部分,你会发现地址栏中的 URL 是变化的。这样不会引起整个页面跳转的特性,很适合用作前端路由。

<div>
  <a href="#/">首页</a>
  <a href="#/category">分类页</a>
  <a href="#/user">用户页</a>
</div>
<div id="container">
  这是首页功能
</div>
<script>
  // 准备对象,用于封装 hash 功能。
  var router = {
    // 路由存储位置: 保存了 url 与 内容处理函数的对应关系
    routes: {},
    // 定义路由规则的方法
    route: function (path, callback) {
      this.routes[path] = callback;
    },
    // 初始化路由的方法
    init: function () {
      var that = this;
      window.onhashchange = function () {
        // 当 hash 改变,我们需要得到当前新的 hash
        var hash = location.hash.replace('#', '');
        // 根据 hash 触发 routes 中的对应 callback
        that.routes[hash] && that.routes[hash]();
      };
    }
  };
  var container = document.getElementById('container');
  // 定义路由规则
  router.route('/', function () {
    container.innerHTML = '这是首页功能';
  });
  router.route('/category', function () {
    container.innerHTML = '这是分类功能';
  });
  router.route('/user', function () {
    container.innerHTML = '这是用户功能';
  });
  // 初始化路由
  router.init();
</script>

特点总结:

  • Hash 方式兼容性好
  • 地址中具有 # 符号,不太美观
  • 前进后退功能较为繁琐

要解决地址不美观、前进后退的问题,则可使用前端路由的另一种方式,History方式。

History方式

History 方式采用 HTML5 提供的新功能实现前端路由。

在操作时需要通过 history.pushState() 变更 URL并执行对应操作。

前进后退功能,首先需要在更改 url 时保存路由标记。通过 popstate 事件监听前进后退按钮操作,并检测 state。调用初始化方法监听前进后退操作并处理。

<div>
  <a href="/">首页</a>
  <a href="/category">分类</a>
  <a href="/user">用户</a>
</div>
<div id="container">
  这是首页功能
</div>
<script>
  var router = {
    // 存储路由的对象
    routes: {},
    // 定义路由的方法
    route (path, callback) {
      this.routes[path] = callback;
    },
    // 用于触发指定的路由操作
    go (path) {
      // 更改 url
      history.pushState({ path: path }, null, path);
      // 触发路由对应的回调函数
      this.routes[path] && this.routes[path]();
    },
    // 设置初始化方法,用来检测前进后退按钮的功能
    init () {
      var that = this;
      window.addEventListener('popstate', function (e) {
        var path = e.state ? e.state.path : '/';
        that.routes[path] && that.routes[path]();
      });
    }
  };
  router.init();
  // 设置 a 标签的功能
  var links = document.querySelectorAll('a');
  var container = document.querySelector('#container');
  links.forEach(function (ele) {
    ele.addEventListener('click', function (event) {
      router.go(this.getAttribute('href'));
      event.preventDefault();
    });
  });
  // 路由规则
  router.route('/', function () {
    container.innerHTML = '首页功能';
  });
  router.route('/category', function () {
    container.innerHTML = '分类功能';
  });
  router.route('/user', function () {
    container.innerHTML = '用户功能';
  });
</script>

优点:

  • 地址格式比hash方法美观
  • 实现前进后退比较方便 缺点:
  • 因为是HTML5功能,兼容性稍差
  • 刷新时需要后端配合处理,防止刷新访问不到路径

Vue Router使用

Vue Router是 Vue.js 官方的路由管理器,让构建单页面应用变得易如反掌。

官方文档

基本使用

安装

方式一、直接下载 / CDN

  • 最新版本:https://unpkg.com/vue-router/dist/vue-router.js
  • 指定版本:https://unpkg.com/vue-router@3.4.9/dist/vue-router.js 方式二、npm 方式
npm install vue-router

引入

<!-- 因为vue-router.js是vue.js的插件,所以必须先引入vue.js -->
<script src="lib/vue.js"></script>
<script src="lib/vue-router.js"></script>

使用

<div id="app">
  <!-- 设置用于进行路由操作的组件 -->
  <router-link to="/">首页</router-link>
  <router-link to="/user">用户</router-link>
  <router-link to="/category">分类</router-link>
  <!-- 路由显示区域 -->
  <router-view></router-view>
</div>
<!-- 因为vue-router.js是vue.js的插件,所以必须先引入vue.js -->
<script src="lib/vue.js"></script>
<script src="lib/vue-router.js"></script>
<script>
  // 定义组件
  var Index = {
    template: `<div>首页功能</div>`
  };
  var User = {
    template: `<div>用户功能</div>`
  };
  var Category = {
    template: `<div>分类功能</div>`
  };
  // 定义路由规则
  var routes = [
    { path: '/', component: Index },
    { path: '/user', component: User },
    { path: '/category', component: Category }
  ];
  // 创建 Vue Router 实例
  var router = new VueRouter({
    routes
  });
  // 创建 Vue 实例,注入 router
  var vm = new Vue({
    el: '#app',
    // router
  });
</script>

命名视图

如果导航后,希望同时在同级展示多个视图(组件),这时就需要进行命名视图。

<div id="app">
  <router-link to="/">首页</router-link>
  <router-link to="/user">用户</router-link>
  <router-view name="sidebar"></router-view>
  <!-- 没有设置 name 的 router-view 默认 name 为 default-->
  <router-view></router-view>
</div>

需要在路由规则中进行设置

// 定义路由规则
var routes = [
  {
    path: '/',
    components: {
      // router-view 的 name : 组件配置对象
      default: Index,
      sidebar: SideBar1
    }
  },
  {
    path: '/user',
    components: {
      default: User,
      sidebar: SideBar2
    }
  }
];

动态路由

当我们需要将某一类 URL 都映射到同一个组件,就需要使用动态路由。

定义路由规则时,将路径中的某个部分使用 : 进行标记,即可设置为动态路由。

// 设置路由规则
var routes = [
  { 
    // 表示'/user/1','/user/2','/user/3'等都指向该路由
    path: '/user/:id', component: User
  }
];

:部分对应的信息称为路径参数,存储在 vm.$route.params 中。

// 设置组件 
var User = {
  template: `<div>这是用户 {{ $route.params.id }} 的功能</div>`
};

侦听路由参数

如果要响应路由的参数变化,可以通过 watch 监听 $route。

// 设置组件 
var User = {
  template: `
    <div>
      这是用户 {{ $route.params.id }} 的功能
      <input type="text">
    </div>`,
  watch: {
    $route (route) {
      // console.log(route);
      console.log(route.params.id)
    }
  }
};

路由传参

数据可以通过 $route.params 进行接收,但这样会导致组件和数据高度耦合,如果以后组件复用在其他位置、使用父组件或其他组件传递数据,就不好处理。

这时我们需要通过路由的 props 设置数据,并通过组件的 props 接收数据。

// 设置路由规则
var routes = [
  {
    path: '/user/:id',
    component: User
  },
  {
    path: '/category/:id'
    component: Category,
    props: true
  }
];

定义组件

var User = {
  template: `<div>这是用户 {{ $route.params.id }} 功能</div>`
};
var Category = {
  props: ['id'],
  template: `<div>这是分类 {{ id }} 功能</div>`
};

当包含多个命名视图时,需要将路由的 props 设置为对象。

// 设置路由规则
var routes = [
  {
    path: '/user/:id',
    component: User
  },
  {
    path: '/category/:id',
    components: {
      default: Category,
      sidebar: SideBar,
      sidebar2: SideBar2
    },
    props: {
      default: true,
      sidebar: false,
      sidebar2: {
        // 如果希望设置静态数据,可将 props 中的某个组件对应的选项设置为对象,
        // 内部属性会绑定给组件的 props。
        a: '状态1',
        b: '状态2'
      }
    }
  }
];

嵌套路由

实际场景中,路由通常由多层嵌套的组件组合而成,这时需要使用嵌套路由配置。

方法是使用 children 来进行嵌套路由中的子路由设置。

var routes = [
  {
    path: '/user',
    component: User,
    children: [
      { // 表明/user/hobby,path属性值不需要加斜杠/
        path: 'hobby',
        component: UserHobby
      },
      { // 表明/user/info
        path: 'info',
        component: UserInfo,
        children: [
          { // 表明/user/info/school
            path: 'school',
            component: UserInfoSchool
          },
        ]
      }
    ]
  }
]

编程式导航

编程式导航,指的是通过方法设置导航。

router.push() 用来导航到一个新 URL。

vm.$router.push('/user')
// 对象结构参数
vm.$router.push({path: '/user'})
vm.$router.push({path: '/user/123'})

<router-link>to 属性使用绑定方式时也可使用属性对象结构。Vue在解析的时候会调用router.push()来处理。

<router-link :to="{ path: '/user/700' }">用户700</router-link>

命名路由

通过设置路由时添加 name 属性,来使我们更方便地对路由进行处理。

var routes = [
  {
    path: '/user/:id/info/school',
    name: 'school',
    component: School
  }
];

router.push() 中通过 name 导航到对应路由,参数通过 params 设置。

vm.$router.push({ name: 'school', params: { id: 10 } })

也可以在 <router-link> 中使用。

<router-link :to="{ name: 'school', params: { id: 10 } }">学校10</router-link>

注意,path 方式和 name 方式不能混用。

重定向

使用 redirect 属性可以对路由进行重定向,用于防止意料之外的情况。

var routes = [
  {
    path: '/',
    component: Index
  },
  {
    path: '/category/:id',
    component: Category
  },
  //  对不合理的路由访问,进行redirect重定向到根目录
  {
    path: '/category',
    redirect: '/'
  }
];

别名

别名是一种路径美化的方式。使用 alias 属性对路径进行美化,使用户看到的地址栏路径简短美观。

var routes = [
  {
    path: '/user/:id/info/school/:date',
    name: 'school',
    component: School,
    alias: '/:id/:date'
  }
]

使用

<router-link :to="{ name: 'school', params: { id: 10, date: '0612'} }">学校信息</router-link>
<router-link to="/20/1234">学校信息2</router-link>

导航守卫

用于在每条路由执行之前,进行的操作。实际应用在未登录用户,跳转登录等场景。

// 设置导航守卫
router.beforeEach(function (to, from, next) {
  // console.log(to, from);
  // next() 导航守卫结束,继续下一步导航
  // next(false) 终止下一步导航
  if (to.path === '/user') {
    // 路由跳转
    next('/category');
  } else {
    next();
  }
})

每个导航守卫需要执行一次next(),相当于导航守卫操作结束,否则无法进入导航路由。

History模式

Vue Router 默认使用 Hash 模式,因为兼容性更好,但是也提供了History 模式的设置。

需要通过 Vue Router 实例的 mode 选项来设置,这样 URL 会更加美观,但同样需要后端支持避免问题,防止刷新访问不到路径。

var router = new VueRouter({
  mode: 'history',
  routes: [
    { path: '/', component: Index },
    { path: '/user', component: User },
    { path: '/category', component: Category },
  ]
})