vue-router基本概念总结

一、vue-router是什么?

要搞明白这个问题,你必须先了解什么是单页应用(或叫单页Web应用),以下解释引自百度百科:

单页Web应用(single page web application,SPA),就是只有一张Web页面的应用,是加载单个HTML 页面并在用户与应用程序交互时动态更新该页面的Web应用程序。

单页应用不存在页面跳转,它本身只有一个HTML页面。我们传统意义上的页面跳转在单页应用的概念下转变为了body内某些元素的替换和更新,举个例子,假如我们有下面的页面index.html

<!DOCTYPE html>
<html>
  <head>...</head>
  
  <body>
    <login-page/>
  </body>
  
</html>

这里现在加载的是登录组件。随后用户进行了登录,我们在登录请求的回调函数中把body内的组件login-page替换成了欢迎页组件welcome-page,因此index.html就变成了下面的样子:

<!DOCTYPE html>
<html>
  <head>...</head>
  
  <body>
    <welcome-page/>
  </body>
  
</html>

显然index.html仍然是它本身,但是由于用户操作,它的整个body的内容从登录组件变成了欢迎页组件,我们实现了传统意义上的页面跳转。但是要注意,由于当前加载的仍然是原来的HTML页面,因此浏览器不会完全重载,已下载的公共资源(如框架代码、普通的js代码等)不会被重新执行。浏览器只会下载与新的组件相关的js代码到本地执行,继而完成页面内容的更新。

这样,从视觉上,页面已经进行了跳转。但实际上,页面只是随着用户操作,实现了局部内容更新。所以你可以看到,相对于传统的多页面应用,单页应用有着极高的“跳转速度”,因此备受欢迎。

vue-router就是vue框架下管理如何进行页面替换和更新的组件。借助vue-router,你可以清晰地掌控整个页面内容更新的过程,拥有像浏览器地址栏一样强大的前进和回退功能。

router的中文释义为“路由”,它是计算机网络中非常重要的概念,表示分组从源到目的地时,决定端到端路径的网络范围的进程。换句话说,就是分组数据包从源到目的地,经历了哪些网络节点。在单页应用中,它表示页面的更新过程中所经历的路径变化。由于没有页面跳转,因此路由无法直接映射到页面地址上,而是通过地址后面的hash值来体现的,举个例子,下面可能是登录页的地址:

https://xxxx/index.html#/login

#后面的值我们称为哈希值,即/login,它表示我们当前处在index.html页面内的login路径下。当用户执行了登录后,页面地址可能变化为:

https://xxxx/index.html#/welcome

我们看到,页面仍然是index.html,但是它后面的哈希值却变成了/welcome,这表示页面内部已经切换到了/welcome所对应的组件。而我们的模板可能是这样写的:

<!DOCTYPE>
<html>
  // 引入vue、vue-router以及登录、欢迎页等组件
  <head>
    ...
  </head>
  
  <body>
    <div id="app">
      <router-view/>
    </div>
    
    <script>
      const router = new VueRouter({  // 定义路由
        routes: [
          { path: '/', redirect: '/login' }, //默认打开的页面
          { path: '/login', component: login-page },
          { path: '/welcome', component: welcome-page }
        ]
      })

	  const app = new Vue({
		router   // 向vue实例导入路由
	  }).$mount('#app');
    </script>
  </body>
</html>

html我们指定当地址中的哈希值为/时,重定向到/login,而/login/welcome则分别对应我们的登录组件login-page和欢迎页组件welcome-page<router-view/>vue-router使用的占位组件,它将根据哈希值动态被替换为对应的组件。

因此当你在地址栏输入https://xxxx/index.html,会首先匹配到空路由/,随后被重定向到/loginvue-router会将该路由对应的组件login-page替换到占位组件<router-view/>的位置。当用户执行了登录,并执行类似以下的操作时:

  this.$axios.post('xxx', data).then(res => {
    this.$router.push('/welcome');
  })

该语句向路由堆栈中添加了新的值/welcome,因此vue-router会将对应的welcome-page组件替换到占位组件<router-view/>。从视觉上,登录页被替换成了欢迎页,而内部机制上,就是完成了一次组件替换,没有发生页面重载。

vue-router的作用就是对单页应用中的上述过程进行管理,下面我们简单来看一下它的基本用法。

二、基本用法

关于vue-router的基本语法,vue-router官网上有详尽的讲解,有需要的可以移步官网进行深入学习。这里我们进行一下大致的介绍。

1. 起步

要使用vue-router,首先需要安装和引入它。

npm install vue-router --save  // 安装

impoer VueRouter from 'vue-router'; // 引入

const router = new VueRouter({
  routes: [
    { path: '/login', component: login-page },
    { path: '/welcome', component: welcome-page },
    ...  // 为每个路由指定对应的组件
  ]
})

const app = new Vue({
  router   // 向vue实例导入路由
}).$mount('#app');

现在我们已经定义好了路由映射规则,并在vue实例中引入了它。当我们修改路由堆栈中的值时,vue-router就会按照这套规则将抽象组件<router-view/>替换为对应的组件。

此时的HTML模板可能像下面一样:

  <body>
    <div id="app">
      <router-view/>
    </div>
  </body>

那我们怎么触发路由的变化呢?

当我们在创建vue实例并传入router实例对象时,vue会自动将这个router对象作为vue实例的$router属性,即:

const app = new Vue({
  router   // 向vue实例导入路由
}).$mount('#app');

// app.$router => vue-router实例对象

变量app是一个vue实例,接下来我们便可以通过它的$router属性去触发路由变化了。比如我们想修改路由,可以通过pushreplace方法:

app.$router.push('/welcome');

// app.$router.replace('/welcome');

push方法会将前一个路由/login保存在堆栈中,而replace则是直接替换上一个路由,因此在路由回退时两者的行为是不同的。

上面的语法称为编程式的导航,也就是通过js代码进行导航。除此之外,还可以通过路由组件的方式:

  <p>
    <!-- 使用 router-link 组件来导航. -->
    <!-- 通过传入 `to` 属性指定链接. -->
    <router-link to="/login">Go to login-page</router-link>
    <router-link to="/welcome">Go to welcome-page</router-link>
  </p>

这两个router-link组件是由vue-router定义的,它们在页面上会被渲染为一个a标签。每个组件带有一个to属性,表示点击该组件需要跳转的路由地址,点击该组件的行为与执行push方法的结果是一致的。这种方式常用于导航栏。

2. 动态路由

在实际应用中,我们可能需要将多个路由地址映射到同一个组件。比如我们现在有一个商品列表,点击某个商品就会进入该商品的详细信息页面。由于商品详细信息的格式是一致的,因此我们只编写了一个组件goods-detail

假设商品列表的路由地址为/goods,而/goods/pants/goods/shoes这样的路由地址都是某个商品对应的详细信息页面,它们使用的都是同一个组件goods-detail

这样的需求我们就需要用到动态路由了。此时我们可以像下面一样定义路由规则:

const router = new VueRouter({
  routes: [
    // 动态路径参数 以冒号开头
    { path: '/goods/:type', component: goods-deatil }
  ]
})

现在只要是符合/goods/xxx这种模式的路由,都会被这个规则捕获,加载goods-detail组件,并且路由中商品的类型被定义为了type属性。我们可以在goods-detail组件中通过路由参数对象$route获取这个值:
goods-detail

<template>
  <div>
    <p>{{ $route.params.type }}</p>
  </div>
</template>

当路由值为/goods/shoes时,组件内部会渲染成:

<div>
    <p>shoes</p>
  </div>

注意,$router$route并不是同一个属性,前者是路由实例对象本身,而后者是该实例对象对应的参数对象,一般来说,操作路由应该使用$router,如push、replace等,获取参数应该用$route,如获取params值。

3. 嵌套路由

当多个路由地址的前缀路径相同时,嵌套路由可以减少路由规则定义的复杂性。比如,如果没有嵌套路由,我们可能会像下面一样定义一组路由:

const router = new VueRouter({
  routes: [
    { path: '/goods/detail', component: goods-deatil },
    { path: '/goods/pay', component: goods-pay },
    { path: '/goods/feedback', component: goods-feedback }
  ]
})

使用嵌套路由,你可以写成下面的格式:

const router = new VueRouter({
  routes: [
    // 动态路径参数 以冒号开头
    { path: '/goods', 
      children: [
        { path: 'detail', component: goods-deatil },
        { path: 'pay', component: goods-pay },
        { path: 'feedback', component: goods-feedback }
      ]
  ]
})

children属性作为/goods的子路由存在,它定义的路由会被拼接到上一级路由后面,即/goods/detail/goods/pay/goods/feedback

4. 命名路由

在定义路由规则时,你可以为路由指定一个名字,这样你就可以不用在每次切换路由时指定路由地址了,如:

const router = new VueRouter({
  routes: [
    { path: '/goods/detail', 
      name: 'detail',
      component: goods-deatil },
  ]
})

现在'/goods/detail'这个路径已经被命名为detail,因此你可以像下面一样跳转到这个路由:

<router-link :to="{name: 'detail', params: { id: '123'}}">detail</router-link>
// 或
this.$router.push({name: 'detail', params: { id: '123' }})

5. 命名视图

一个页面可以存在多个router-view占位组件,那么当切换一个路由时,如何为每个router-view指定要渲染的组件呢?答案就是使用命名视图,为每个router-view指定一个名字,然后分别定义组件,如:

const router = new VueRouter({
  routes: [
    { path: '/goods', 
      component: {
        default: goods-content, // 默认视图
        nav: goods-nav
      } },
  ]
})

而页面模板可能像下面这样:

<div>
  <router-view/>
 
  <router-view name="nav"/>
</div>

当执行this.$router.push('/goods')时,第一个router-view会被替换为goods-content组件,而第二个会被替换为goods-nav组件。

6. 重定向和别名

重定向,顾名思义,就是将一个路由定向到另一个路由,我们在处理根路由时常常会用到。比如在最开始的例子中,我们只有/login/welcome这两个组件,没有/对应的组件。这样当用户在地址栏输入https://xxxx/index.htmlhttps://xxxx/index.html#/时,页面会显示为白页。

假如我们希望让/自动加载/login的组件,可能需要下面的额外定义:

{ path: '/', component: login-page },

但我们想的是这时应用应该自动将路由直接置为/login,于是我们会这样写:

{ path: '/', redirect: '/login' },

它表示路由地址/现在将会被重定向到/login,所以当用户输入https://xxxx/index.html,地址栏的值会直接变成https://xxxx/index.html#/login,这就是一次路由重定向。

别名则是给某个路由另起一个名字。如:

{ 
  path: '/login', 
  component: login-page, 
  alias: '/userLogin' 
}

现在输入https://xxxx/index.html#/loginhttps://xxxx/index.html#/userLogin都会匹配该路由。加载login-page组件。

7. 路由传参

当我们在路由跳转时向路由组件传递了参数,除了可以像上面一样使用$route对象来获取外,还可以通过props属性,将参数转化为props。如:

{ path: '/user/:id', component: User, props: true },

props: true意味着现在与路由相关的参数都被作为props传入组件,因此你可以这样获取参数:

const User = {
  props: ['id'],
  template: '<div>User {{ id }}</div>'
}

路由中的id参数将作为props中的id属性传入。

除了使用布尔值,props还可以使用对象或函数。

传入对象时,它会原样被设置为组件属性:

{ path: '/user/:id', component: User, props: { state: 'active' } }

组件内:

const User = {
  props: ['state'],
  template: '<div>User {{ state }}</div>'
}

传入函数时,可以基于路由参数对象,返回更复杂的参数:

{ path: '/search', component: SearchUser, props: (route) => ({ query: route.query.q }) }

URL/search?q=vue会将{query: 'vue'}作为属性传递给 SearchUser 组件。

8. History模式

vue-router默认使用hash模式进行路由导航,因此在切换路由时,页面并不会跳转。但是有人可能觉得,在url中添加一个#看起来并不美观,希望既可以不重载页面,又可以不在地址中出现丑陋的#,HTML5的History接口为这种需求提供了可能。

这种模式的实现需要后端的配合,比如对于http://yoursite.com/user/id这样的地址,我们希望它加载index.html页面,就需要在后端配置好,否则就会返回404页面。

前端收到index.html页面后,自行根据url进行路由导航。想了解具体的实现,请参考MDN - History

总结

本文只是关于vue-router的基本语法介绍,还有一些进阶用法没有涉及,包括导航守卫、路由元信息、过渡动效、数据获取、滚动、路由懒加载等,需要进一步学习请参考vue-router官网

发布了49 篇原创文章 · 获赞 110 · 访问量 3万+

猜你喜欢

转载自blog.csdn.net/qq_41694291/article/details/105472396