前端路由VueRouter


前端路由 VueRouter

1.单⻚⾯应⽤与前端路由

⼀、本节⼤纲

  • 单⻚⾯应⽤ SPA 介绍
  • 前端路由的两种实现⽅式
  • 单⻚⾯应⽤的优缺点

⼆、单⻚⾯应⽤ SPA 介绍

在说单⻚⾯应⽤(SPA:Single Page Application)之前,有必要和⼤家⼀起回顾⼀下,Web 应⽤开发的⼏个阶段,这样更有助于我们来理解单⻚⾯应⽤。

在 Web 应⽤较早的时候,在 Java 的领域⼤家最常使⽤的是 Servlet、JSP。这两种技术实现的 Web 应⽤, 都脱离不了⼀个事实:那就是浏览器和服务端之间的唯⼀的沟通⽅式就是 html。这个阶段没有所谓的 Web 应⽤数据接⼝,只有⻚⾯视图请求服务。也就是说,客户端发送⼀个请求,服务端根据请求去操作 数据库,然后在 java 代码⾥⾯写数据和 html 代码进⾏组合渲染,最终将⼀个 html ⻚⾯返回个浏览器端。 周⽽复始。

久⽽久之,程序员对于在 Java 代码⾥⾯写 html 已经感到⼗分厌恶,从⽽有了后来的⼤量 MVC 的框架:如 struts 等。在⼀定程度上实现了数据与视图的解耦。但是这些框架的出现只是在开发上更加⽅便,仍然没有改变 html ⻚⾯需要在服务端渲染的事实。这时对于前端来说,⼀种⾰命性的技术出现了,那就是 Ajax。

Ajax 不是⼀种新技术,但的确是⼀种⾰命性的技术。

这个时候,程序员开发 Web 应⽤,⼀个功能请求服务端得到⼀个初始的 html ⻚⾯,然后在这个⻚⾯上的所有操作都可以通过 Ajax 进⾏,返回如 JSON 格式数据,再⽤ Ajax 和 CSS 技术对这个⻚⾯进⾏前端的渲染,局部刷新。

然⽽这并不能满⾜这样的⼀个强烈的需求:后端程序员不想写不会写视图层内容,能不能把视图层开发完全交给前端。很多逻辑能⼒超群的后端程序员,写不好 css、html,效率极低。

那么⼤家就想:能不能实现前后端分离,以后后端程序员只写数据接⼝,不写视图渲染程序。专业的⼈做专业的事,往往效率是最⾼的。⽽且另⼀个重要的原因就是:前端视图渲染缓解了服务器压⼒,所以 ⽐后端视图渲染的效率更⾼。

这个时候,前端迎来了历史上最快速的发展,各种前端框架层出不穷,其中最具代表性的就是 Vue.js、 ReactJs、AngularJS 三⼤框架,这三⼤框架⽆⼀例外的⽀持组件化开发和前端路由。组件化开发和前端路由的出现,为单⻚⾯应⽤和前后端分离的实现提供了基础。组件提供了内容边界,前端路由实现了组件的切换,即视图显示的切换。

⽤⽩话解释⼀下。单⻚⾯应⽤就是在进⼊应⽤之后第⼀次请求,就将所有的组件视图⼀次性加载到客户端,之后通过前端路由切换组件,通过后端服务获取组件数据的这样⼀种应⽤。也就是说,整个应⽤只有⼀个 html,之后的每⼀次 url 变化或者 http 请求,都只是切换 html 中显示的组件和组件的数据内容。

三、前端路由的两种实现⽅式

前端路由的出现,使单⻚⾯应⽤成为可能,对于 Vue ⽽⾔,前端路由就是 Vue-Router。我们在传统的开发⽅式中,⼀旦在浏览器端改变 url 就⼀定会向服务端发送请求。

在 SPA 中,前端路由的核⼼意义就在于:切换视图并且不会向服务端发送请求。

为了实现前端路由的这⼀功能,浏览器提供了以下两种特性的⽀持:

  • hash:即在地址栏上⾯ URL 增加 # 标志符号,实际上就是基于锚点的实现原理。通过切换锚点,来切换组件。学过 html 的朋友都知道,变更 URL 锚点是不会向服务端发送请求的,但会影响视图显示。正是基于 HTML 锚点这个特性,vue 实现了前端路由的 hash ⽅式。
  • history:⼤家可能都知道 javascript 的⽅法 window.history.back()、window.history.forward()、 window.history.go() 可以实现向前⼀个⻚⾯、后⼀个⻚⾯、指定的历史⻚⾯进⾏跳转,并且不会向服务端发送请求。正是基于 history 系列⽅法的这个特性,vue 实现了前端路由的 history ⽅式。但是 对于⼀个灵活的应⽤,这⼏个⽅法显然是不够⽤的,所以在 HTML5 中增加了 pushState() 和 replaceState() ⽅法。

history 维护栈的数据结构,栈数据结构就像⼀个桶,后进的先出。在桶顶部的组件就是视图中显示的组 件。pushState() 向栈中压⼊⼀个视图组件,replaceState() 替换⼀个视图组件。

关于这两种模式深⼊的学习,可以参考:https://juejin.im/post/5a1e89b66fb9a045196958fe

四、单⻚⾯应⽤的优缺点

  • 从⽤户体验的⻆度,SPA 应⽤显然是更优的,⼤量的使⽤ ajax 加载数据,进⾏前端渲染,降低了服务器的压⼒。举个例⼦:⽼师记住⼀个班的学⽣容易?还是每个学⽣记住⾃⼰的⽼师容易?⾃然清楚了吧。
  • 因为 SPA ⼤量的使⽤了 Ajax,所有内容都在同⼀个 html ⻚⾯上,所以对搜索引擎不够友好。所以,如果你的产品⾮常在意搜索引擎中的排名,请谨慎使⽤。当然也可以通过服务器端渲染,使 SPA 可以针对搜索引擎进⾏优化。另外,搜索引擎的爬⾍技术也在不断的精华,这个问题将越来越容易解决。
  • 从开发的⻆度,SPA 实现了前后端分离,专业的⼈做专业的事,往往效率更⾼。代码的可维护性也会增强。
  • 单⻚应⽤程序不如多⻚应⽤程序安全。因为 SPA 将更多的代码交给了前端,前端显然不如后端更安全,但这个问题有时也因⼈⽽异。
  • 因为更多的视图渲染过程在客户端设备上进⾏,单⻚应⽤程序⽐多⻚应⽤程序更容易消耗移动设备的电池电量。

2.安装与配置 VueRouter

⼀、本节⼤纲

  • 已有项⽬中⼿动安装 VueRouter (重点掌握)
  • VueCLI2 项⽬构建过程中安装 VueRouter
  • VueCLI3 项⽬构建过程中安装 VueRouter
  • 使⽤ Vue UI 界⾯安装 VueRouter

⼆、已有项⽬中⼿动安装 VueRouter(重点掌握)

在之前的章节,我们讲解了如何使⽤ VueCLI2 快速构建 Vue 前端项⽬。当时我们并没有安装 VueRouter,现在就来学习⼀下。如果⼿动安装学会了,项⽬构建过程中的⾃动安装也就更好理解了。 需要注意的是 VueRouter 并不是 Vue 框架默认集成的,⽽是以外部依赖的形式存在。 所以⾸先使⽤如下命令,安装 VueRouter。

npm install vue-router --save

安装完成后,在 package.json ⾥⾯会多出 “vue-router” 的安装信息,说明安装成功及安装版本。

在 src ⽬录下,新建⼀个⽂件夹 router,⽂件夹内放⼊⼀个 index.js ⽂件。

  • VueRouter 并不是 Vue 框架默认集成的,⽽是以外部依赖的形式存在,所以使⽤之前需要先引⼊
  • 构造 VueRouter 对象,路由规则定义数组作为对象构造参数
  • VueRouter 默认使⽤ hash 作为前端路由实现⽅式,如果希望使⽤ history ⽅式,增加 mode 配置
  • 导出 VueRouter 对象,供其他模块使⽤(实际就只是 Vue 会使⽤ VueRouter)

在 main.js 中,引⼊ VueRouter 对象作为 Vue 实例的参数,从⽽使项⽬的前端路由⽣效。

三、VueClI2 项⽬构建过程中安装 VueRouter

在前⾯的章节,我们学习使⽤ vue init webpack [project-name] 命令快速构建 Vue 项⽬脚⼿架。如果需要在项⽬中加⼊ VueRouter,在项⽬配置填写的阶段 Install vue-touter ?填写 Y(Yes)。

就这么简单,VueCLI2 构建项⽬过程中就可以加⼊ VueRouter。

四、VueCLI3+项⽬构建过程中安装 VueRouter

在 Vue CLI3+ 项⽬构建功能选择的界⾯,通过按 Up、Down 键上下移动,按空格键选择 Router 选项。选中之后,具体的功能特性前⽅会有星号标识。

在后续的构建过程中,Vue CLI3 脚⼿架还会提示你选择前端路由的实现模式:

  • Yes 代表使⽤ history 模式实现 vueRouter 前端路由
  • No 表示使⽤ hash 模式实现 VueRouter 前端路由

3.VueRouter 开发第⼀个 Demo

⼀、本节⼤纲

  • 使⽤ VueRouter 开发第⼀个 demo
  • 对 demo 进⾏样式美化
  • 处理⼀个细节问题

⼆、使⽤ VueRouter 开发第⼀个 demo

第⼀个示例很简单,创建⼀个单⻚⾯应⽤。这个单⻚⾯应⽤有两个路由及两个组件:当点击 /home 路由的标签元素展示⽤户主⻚⾯(组件 Home),当点击/about展示”关于⻚⾯”(组件 About )。这⾥我们先不管样式及布局。

要使⽤ VueRouter 通常有三部曲:

第⼀步:创建被路由的组件

创建两个单⽂件组件,⼀个是 About.vue ,⼀个是 Home.vue 。两个组件的内容很简单,模板⾥⾯分别写⼀个 h1 标题,书写不同的内容⽂本。为每⼀个组件起⼀个 name ,并将组件以模块的形式导出 export default 。

第⼆步:配置组件与路由路径的映射关系

  • ⾸先通过 import 将⼦组件 Home 和 About 引⼊到路由配置⽂件⾥⾯
  • 配置路由 path 路径与 component 组件之间的关系

第三步:在⽗组件中使⽤路由及⼦组件

  • router-link 标签可以理解为 vue 框架语法中的标签,实际上在渲染成 html 之后它就是⼀个按标签,⽤于实现路由规则。to 属性通过路由的 path 配置映射对应的组件。
  • router-view 标签作为路由组件的展示出⼝,根据路由的 path 不同,这个位置替换成不同的组件内容。

显示效果

三、对 demo 进⾏样式美化

我们做的例⼦有点太丑了,可以利⽤开源的组件,开箱即⽤,提⾼我们的开发效率,⽽且样式也⽐较好看。

在 Vue 后端管理系统中经常⽤到的 UI 组件库有 View UI(iVuew) 、Element UI 等,本节以 View UI(iVuew) 为例,讲解⼀下第三⽅组件库的使⽤。

⾸先我们需要安装 View UI ,使⽤如下的命令:

npm install view-design --save

然后在单⻚⾯应⽤的 main.js 中引⼊ ViewUI 并使⽤ ViewUI。

最后,我们登录 ViewUI 官⽹(发⽂环境所限,搜索引擎⾃⾏查找),找到⼀个喜欢的布局样式,点击 show Code 复制代码。

因为 app.vue 是我们路由布局的⽗组件,所以将 template 代码粘贴到 App.vue 的 template ⾥⾯,注意保留 id 为 app 的 div。script 脚本和 style 样式代码也要粘贴到⽂件⾥⾯。

  • 图中的 Layout 是布局组件。可以嵌套使⽤。Sider 是侧边栏组件。
  • Menu 是菜单组件,MenuItem 是菜单项组件,Icon 是图标组件。显然 router-link 标签应该放到 MenuItem ⾥⾯。
  • Content 是内容组件,显然我们的 router-view 标签应该放到内容组件⾥⾯。

最终的实现效果如下:点击对应的菜单,显示对应的内容。

4.路由重定向与组件懒加载

⼀、本节⼤纲

  • 路由重定向
  • 路由组件懒加载

⼆、路由重定向

上节我们实现的例⼦仍然存在⼀个问题,那就是当我们访问项⽬根路径时,Content 内没有显示任何的 router-view 组件内容。我们点击了左侧菜单中的“我的”,右侧内容区才会显示:“这⾥是主⻚”,如下图:

但是⼀个正常的应⽤习惯,在访问项⽬时候 Content 区域就应该默认显示 Home 主⻚组件,那么应该怎样做呢?只需要按照下⾯的⽅法在 router/index.js ⽂件的路由规则配置加上⼀个路由的重定向配置:

const routes = [
  {
    path: "/", //当访问根路径的时候
    redirect: "/home", // 重定向到redirect配置路径
  },
  {
    path: "/home",
    component: Home,
  },
  {
    path: "/about",
    component: About,
  },
];

此时当我们通过访问项⽬根路径 / 时候,就会重定向到 /home 显示 Home 主⻚内容:这⾥是主⻚。并且浏览器地址栏不再是根路径 /,⽽是 /home 路径。路由路径 path 与⻚⾯显示组件内容是统⼀的。

三、路由组件的懒加载

在前⾯的章节,我们已经给⼤家讲解了什么是单⻚⾯应⽤(SPA)。我们也使⽤组件和前端路由开发了⼀个例⼦,所有的组件代码最终都将打包到⼀个 js ⽂件发布。当浏览器第⼀次访问应⽤时这个 js 被浏览器加载,并渲染出⼀个 HTML 视图。如图中红⾊边框的内容。

你有没有想过这样⼀个问题:所有的前端组件代码⼀次性加载,如果组件特别多的话有可能因为⽹络原因导致加载缓慢,从⽽出现⻚⾯⽩屏的现象。

那么,我们能不能把组件按需加载呢?也就是说当组件被路由切换到的时候再去加载,当然是可以的, 这个实现的名字就叫做懒加载或者延迟加载。如图中⿊⾊边框中的组件,可以适当的对其中⼀些组件进⾏懒加载。

那么什么样的组件最适合使⽤懒加载?就是那些使⽤频率相对较低的组件,⽐较适合使⽤懒加载。⾸屏组件不适合懒加载,⽐如:我们例⼦中的 Home 组件。

可以看到在上图红⾊区域中,我们使⽤ ES6 语法对 About 组件实现了懒加载,代码如下:

const About = () => import("../components/About");

然后使⽤ run build server 命令对项⽬进⾏打包,最终的打包结果多出⼀组两个 js ⽂件,就是因为 About 组件被单独打包了。

打开⽂件内容查看⼀下,可以确认就是 About 组件被单独打包的结果,从⽽实现 js ⽂件组件的按需加 载。

5.嵌套路由的配置实现

⼀、本节⼤纲

  • 什么是嵌套路由
  • 嵌套路由的实现
  • 需要特别记忆理解的规则
  • 默认⼦路由

⼆、什么是嵌套路由

在前⾯章节内容⾥,我们讲了⼀个使⽤ VueRouter 的例⼦。内容很简单,当浏览器 url 路径切换到 /home 的时候显示 Home 主⻚组件,当浏览器 url 路径切换到 /about 的时候显示 About 关于组件, 如下图中左侧的图示所示。那么在实际的开发中,路由的映射关系和⽹⻚视图不只是平⾯的,组件⾥⾯仍有⼦组件,路径下⾯还有⼦路径,如下图中的右侧的图示所示。

这⼀节,就来学习⼀下嵌套路由与嵌套组件的之间的映射的⼀个例⼦。也就是说:

  • 当访问 /home/article 的时候,Home 组件中的默认 router-view 显示 Article ⽂章组件
  • 当访问 /home/news 的时候,Home 组件中的默认 router-view 显示 News 新闻组件

三、嵌套路由的实现

开发的套路仍然是三部曲:

第⼀步:创建被路由的组件

创建两个单⽂件组件,⼀个是 Article.vue, ⼀个是 News.vue。两个组件的内容仍然很简单,模板⾥⾯分别写⼀个 h1 标题,书写不同的内容⽂本。为每⼀个组件起⼀个 name,并将组件以模块的形式导出 export default。

第⼆步:配置组件与路由路径的映射关系

图中的⻩⾊代码,为本节新增的代码。

  • ⾸先需要将 Article 组件和 News 组件 import 到路由关系配置⽂件⾥⾯
  • 然后在上⼀节原 home 路由及组件的映射条⽬⾥⾯,加上 children 数组属性表示⼦路由组件之间的映射关系
  • 另外需要注意,⼦组件路由的 path 配置不要加斜杠 /

第三步:在⽗组件中加⼊ router-view

在上⾯的需求中,我们已经明确了⽂章组件 Article 和新闻组件 News,都是 Home 组件的⼦组件。 所以我们在 Home.vue ⽂件中加⼊ router-view。代码如下:

  • ⾸先需要注意,当⼀个⽗组件有多个⼦组件的时候,要在 template ⾥⾯加上⼀个唯⼀的根标签。如图中的 div#home。
  • 另外注意:路由 router-link 的 to 属性,需要写完整的路由路径,如:/home/article,不能写 /article

在上⼀节的代码的基础之上,最终实现效果如下:

  • 当点击“⽂章”的时候,显示:这⾥是⽂章组件
  • 当点击“新闻”的时候,显示:这⾥是新闻组件

四、需要特别记忆理解的规则

  • 需要特别注意的⼀点是:根据不同的需求,router-link 可以定义在任何组件内,不⼀定⾮要定义在对应的 router-view 的同⼀个⽗组件⾥⾯。只要 router-link 和 router-view 同时在浏览器内显示,在任何其他组件内触发 router-link 都会导致其对应的 router-view 发⽣组件切换。
  • 那么什么是 router-link 对应的 router-view?
    • ⽐如:to="/home" 对应的 router-view,就是 App.vue 中定义的的 router-view。
    • ⽐如:to="/home/news" 对应的 router-view,就是 Home.vue 中定义的 router-view。

五、默认⼦路由

在上⾯的实现中,有⼀个问题,那就是当访问 /home 路径时,只显示出上图中的红⾊边框部分,没有显示出绿⾊边框的部分。也就是⼦组件不会默认显示出来,⽽是在点击了“⽂章”或“新闻”之后,对应的⼦组件才显示出来。

在代码中加⼊空字符串的 path ⼦路由配置,指向 Article 组件,这样当访问到 home 路径的时候就默认 将 Article 组件显示出来了。

6.动态路由的配置与实现

⼀、本节⼤纲

  • 动态路由简介
  • 实现⼀个动态路由的例⼦
  • 动态路由的多段匹配
  • 动态路由的正则匹配

⼆、动态路由

之前所讲的路由的例⼦都是⼀对⼀的关系,也就是说:⼀个 url 匹配⼀个路由和⼀个组件。⽐如:当浏览器 url 路径切换到 /home 的时候显示 Home 主⻚组件,当浏览器 url 路径切换到 /about 的时候显示 About 关于组件。

在实际的开发过程中,我们通常需要多个 url 匹配同⼀个路由和组件,如下图所示:

如上图所示,动态路由有如下 2 层含义:

  • 动态路由可以传递参数,如上图中的 id,根据不同的参数组件可以显示不同的内容。如上图:传递参数 1 显示 id=1 的⽂章,传递 2 显示 id=2 的⽂章。
  • 动态路由可以实现路由导航,也就是匹配⼀个路由规则,从⽽显示该路由规则对应的组件。如上图中的 Article 组件的显示。

三、实现⼀个动态路由的例⼦

在上⼀节的代码基础上实现上图所示的动态路由。

⾸先,在 router/index.js ⽂件中修改组件与路由之间的映射关系配置,注意 id 是⼀个路由参数,前⾯需要加上冒号

Home 组件是 Article 的⽗组件。修改 Home.vue 中的 router-link,router-link 默认渲染为 html 的超链接 a 标签,从⽽实现路由的触发。注意在路由中传递了参数 id 分别是 1 和 2,⽤以匹配上图中的路由规则。

Article 组件是 Home 组件的⼦组件。

在 Article.vue 中接收参数 id,显示:这⾥是 id={{$route.params.id}} 的⽂章组件。

$route 对象代表当前路由,params 是代表参数,id 是路由参数的名称,通过这种⽅式获取参数 id 的值。

实现效果如下:当点击 id=1的⽂章 时,组件内容显示:这⾥是 id=1 的⽂章组件;当点击 id=2的⽂章 时,组件内容显示:这⾥是 id=2 的⽂章组件;

四、动态路由的多段匹配

路由规则 匹配 url $route.params 对象 匹配规则含义
/article/:id /article/1 {id:1} id=1 的⽂章
/article/:id/reader/: num /article/1/reader/ 2 {id:1,num:2} id=1 的⽂章的第 2 位读者信息

如上表所示:第⼀条路由匹配是⼀个单段的路由匹配,第⼆个路由匹配是⼀个多段路由匹配。多段的路由匹配可以实现更为复杂的路由匹配规则和传递更多的参数,表达更为丰富的含义。

五、动态路由的正则匹配

动态路由⽀持正则匹配,举例:

路由规则 匹配规则含义
/article/:id 参数 id 必须传,参数可以是数字也可以是字符串。即 /article/1 和 /article/abc 都匹配这个路由规则
/article/:id? 参数 id 可以传,也可以不传。即 /article/1 和 /article 都匹配这个路由规则
/article/:id(\d+) 参数 id 必须传,并且 id 只能是数字。如:/article/1
/article/* 星号表示匹配任意路径,即:/article/abc 和 /article/abc/def 都可以匹配这条路由规则

当然,你也可以根据⾃⼰的需求定义正则规则,正则规则可以千变万化。

如果⼀个 url 路径匹配了多个路由规则怎么办?答案是:哪个路由先定义的,哪个路由的匹配优先级就更⾼,所以在使⽤正则路由的时候要考虑好路由定义的先后顺序。

7.命名路由与命名视图

⼀、本节⼤纲

  • 命名路由
  • 命名路由与动态路由
  • 命名视图

⼆、命名路由

  • 在本专栏之前的章节实现的路由规则,都是根据 path 路径匹配对应的组件,如下图中⻩⾊边框定义的部分。
  • 命名路由就是为路由定义规则起⼀个名字,并在路由导航的时候根据名字进⾏导航,即:根据路由名字进⾏导航和组件的切换。如下图中的红⾊边框定义的部分。

上图中右侧部分为路由规则的定义,即 router/index.js ⾥⾯定义的路由规则。

那么有了路由规则,我们该如何使⽤路由规则呢?看下图中的代码。

  • 上图中被注释掉的代码,是之前的实现,是通过匹配 path 实现路由导航及组件的切换。
  • 上图中⻩⾊框的代码,使⽤了命名路由,根据 name 的定义与路由规则定义中那么进⾏匹配,从⽽实 现路由导航及组件的切换。
  • 注意:命名路由使⽤ v-bind 的对象语法,to 属性之前加上⼀个冒号(冒号等于 v-bind 指令),to 属性内容是⼀个对象

命名路由与 path 匹配的路由实现的效果是⼀样的

三、命名路由与动态路由

之前的章节也讲过动态路由,所谓的动态路由就是:根据传递参数的不同,在同⼀组件上显示不同的内容。

  • 上图代码中第⼀段使⽤了 path 匹配规则的路由,都导航到 Article 组件,但是根据 id 不同显示不同的⽂章
  • 上图代码中第⼆段使⽤了命名路由,也实现了与动态路由⼀样的效果,但是传递参数的⽅式有所不同

上⾯两段路由导航代码匹配到的路由定义规则如下:

四、命名视图

顾名思义,命名视图就是给视图组件起⼀个名字,但如何使⽤命名视图才是重点。

为了更好地理解命名视图的使⽤,我们先看下⾯的这张图。

之前讲过路由导航与内容展示,都是多个 router-link 对应⼀个 router-view。⽐较适合实现上⾯的视图,单击菜单⼀、⼆、三、四,都会将菜单项对应的导航内容显示到内容显示区域。

但是这样的组件实现⽅式有⼀个⾮常⼤的弊端就是:只有⼀个 router-view,也就是说只有⼀个组件的展示出⼝,这将很⼤程度上影响我们的应⽤开发布局的丰富性。为了解决这个问题我们就需要使⽤到命名视图

如上图所示,当⼀个⼤的视图组件中有三个 router-view 的时候,该如何区分哪个 router-view 显示哪⼀个组件?如下图代码使⽤命名视图(为了更清晰的显示核⼼代码,没有加布局样式):

  • 如果不为视图起名,将默认显示路由规则中的 default 组件
  • 如果为视图起名,将显示路由规则中名字对应的组件

8.编程式导航

⼀、本节⼤纲

  • 编程式导航的例⼦
  • 编程式导航 API 详解

⼆、编程式导航的例⼦

在之前的章节中,我们都是使⽤的⽅式实现路由的导航与组件的切换,这种⽅式被称为声明式导航。在实际的开发过程中,还可以使⽤编程式导航的⽅式来实现(也就是通过编写 JS 代码的⽅式 实现)。

上图是前⾯学习过的 path 路由规则和命名路由规则的示例图。

下⾯就使⽤编程式导航来实现示例图所呈现的例⼦,需求就是:当点击 /home 路由的标签元素展示⽤户主⻚⾯(组件 Home ),当点击 /about 路由的标签元素展示 “关于⻚⾯” (组件 About )。

  • 上图中⽤编程式导航替换了声明式导航(图中绿⾊被注释掉的代码)
  • 图中⻩⾊及红⾊框选代码为新增的编程式导航的代码
  • 通过 this.$router 获取当前的路由实例,push ⽅法实现路由导航
  • 实现需求就是:当点击 /home 路由对应的按钮导航⾄⽤户主⻚⾯(组件 Home ),当点击 /about 对应的按钮导航⾄”关于⻚⾯”(组件 About)

从实现的代码量上看,似乎声明式导航⽐编程式导航更易⽤。

但是在实际的应⽤开发过程中,编程式导航更容易实现复杂对象参数及变量参数的传递。如下代码:

//编程式导航:命名路由的参数传递
router.push({ name: "article", params: { articleId: "123" } }); // ->/article/123
//编程式导航:动态路由
let articleId = "123";
router.push({ path: `/article/${articleId}` }); // ->/article/123

三、编程式导航 API 详解

VueRouter 的编程式导航是通过三个⽅法来实现的:

  • router.push ⽅法:最常⽤的编程式路由导航⽅法
  • router.replace ⽅法
  • router.go ⽅法

看到这三个⽅法有没有感觉到⽐较亲切?之前单⻚⾯应⽤的时候⽤到了这样的⼀张图:

结合这张图,我们可以这样理解 Vue ⻚⾯视图组件的显示及路由导航:

  • Vue 维护着关于组件的栈的数据结构,⼀个栈类似于⼀个桶,默认情况下只有栈顶部的组件才被渲染到浏览器⻚⾯上。
  • 当使⽤ push ⽅法导航⼀个组件时,组件被压⼊栈中,处于栈的最顶层。如上图中的上半部分,组件 3 压在组件 2 的上层,所以浏览器显示组件 3。
  • 当使⽤ replace ⽅法导航⼀个组件时,该组件替换掉栈中最顶层的组件。如上图中下半部分,组件 2 换成组件 3 ,所以浏览器显示组件 3。
  • 默认情况下栈顶部的组件才被渲染到浏览器⻚⾯上,但是当使⽤ go ⽅法的时候,就是在调整栈的顶部指针,指针指到哪个组件哪个组件就被显示出来。go (正数值)是向栈的顶部移动,go (负数值)是向栈的底部移动。当正负值位移超出栈的容量时,go ⽅法调⽤失败。

附录:源码

App.vue

<template>
  <div id="app">
    <!-- <router-link to="/home"><h2>我的</h2></router-link>
    <router-link to="/about"><h2>关于</h2></router-link> -->
    <button @click="toHome"><h2>⾸⻚</h2></button>
    <button @click="toAbout"><h2>关于</h2></button>
    <router-view></router-view>
  </div>
</template>
<script>
    export default {
        name:"App",
        methods: {
            toHome () {
                this.$router.push("/home");
           },
            toAbout() {
                this.$router.push("/about");
           }
       }
   }
</script>

9.路由的参数传递与获取

⼀、本节⼤纲

  • params 与 query 参数的区别
  • params 参数传递⽅式详解
  • query 参数的传递⽅式详解
  • this.$routethis.$router 的区别
  • 使⽤ props 接收参数(推荐)

⼆、params 与 query 参数的区别

在本专栏前⾯的章节实际已经为⼤家介绍过动态路由,动态路由是路由传参中的⼀种,也就是 params 传参。还有⼀种⽅式就是 query 参数传参。

{
    name: "article",
    path: '/article/:id?',
    component: Article
}

先来看⼀下上⾯代码定义的路由规则分为两部分,⼀部分是 name 代表命名路由规则,⼀部分是 path 带 id 参数代表动态路由规则(后⾯加⼀个问号表示 id 参数可传可不传),匹配命名路由和动态路由其中⼀个规则,就会导航到 Article 组件。

针对上⾯的代码定义的路由规则,介绍两种参数传递⽅式的区别:

  • params :/article/123,/article/789 ,这⾥的 id,123,789 是参数,被称为 params 参数。
  • query:/article?id=123 ,/article?id=456 ,这⾥的 id=123,id=456 是参数,被称为 query 参数。

这两种⽅式结合动态路由、命名路由,再结合编程式导航和声明式导航,所以 2 乘 2 乘 2 ⼀共有⼋种传递参数的⽅式。

三、params 参数传递⽅式详解

声明式导航 params 传参

<!--id=2参数,动态路由的实现⽅式 -->
<router-link to="/article/2">
    <h2>id=2的⽂章</h2>
</router-link>
<!--id=2参数,命名路由的实现⽅式 -->
<router-link :to="{name:'article', params: { id: 2 }}">
    <h2>id=2的⽂章</h2>
</router-link>

编程式导航 params 传参

//id=2参数:命名路由的实现⽅式
//(name与params属性结合使⽤)
router.push({ name: "article", params: { id: "2" } });
//id=2参数:动态路由的实现⽅式
router.push({ path: "/article/2" });
// id=2参数:将id参数定义为⼀个变量,如articleId。
//这样的使⽤⽅式更加灵活
let articleId = "2";
router.push({ path: `/article/${articleId}` });
//需要注意path和params属性不能⼀起使⽤
//否则params属性将被忽略,如下写法是错误的
router.push({ path: "/article", params: { id: "2" } });

不论是声明式导航还是编程式导航,不论是使⽤命名路由还是动态路由,上⾯的代码可以实现导航到 Article 组件并传递参数 id。那么在 Article 组建中如何接收 params 参数 id 呢?就⽤下⾯的代码就可以:

<div>{{ this.$route.params.id }}</div>

四、query 参数的传递⽅式详解

声明式导航 query 传参

<!--id=1的query参数,path路径URL传参的⽅式 -->
<router-link to="/article?id=1">
    <h2>id=1的⽂章</h2>
</router-link>
<!--id=2的query参数,命名路由的实现⽅式 -->
<router-link :to="{name:'article', query: { id: 1 }}">
    <h2>id=1的⽂章</h2>
</router-link>

编程式导航 query 传参

//name命名路由传递query参数
this.$router.push({
  name: "article",
  query: { id: "1" },
});
//path路径URL传递query参数
this.$router.push({
  path: "/article",
  query: { id: "1" },
});

上⾯的代码可以实现导航到 Article 组件并传递 query 参数 id。

那么在 Article 组建中如何接收 query 参数 id 呢?就⽤下⾯的代码就可以:

<div>{{ this.$route.query.id }}</div>

五、this.$route与this.$router 的区别

在上⽂中我们使⽤到了 this.$routethis.$router ,两者容易弄混,所以我们有必要仔细看⼀下⼆者都是什么。

看下⾯的截图:

图中信息可以看出:

  • router 是⼀个 VueRouter 实例。管理项⽬整体路由的钩⼦,导航动作(如 push ),路由规则定义信息(图中蓝框)等信息。
  • route 代表当前路由信息,该路由只保管本次导航的路由信息,如:路由的 name、path、params 参数、query 参数的信息。

六、使⽤ props 接收参数(推荐)

在实际的项⽬开发中,不建议使⽤通过 this.$route 获取 query 和 params 参数,因为这限制了组件只有在路由传参的情况下才能使⽤,也就是限定了特定的 URL 下使⽤组件。

实际上我们的组件的传参⽅式还有很多,⽐如前⾯讲过使⽤ props 接收⽗组件向⼦组件传递的数据。也就是说,如果我们希望组件在这两种情况下都能很好的使⽤,就不能⽤ this.$route,只能⽤ props 统⼀接收参数,从⽽去除组件与路由之间的耦合。

使⽤ props 接收路由参数,都需要修改路由定义,有三种修改⽅式

  • 布尔模式
  • 对象模式
  • 箭头函数模式

⾸先来看看布尔模式,也就是在路由规则定义的时候增加 props 为 true。这样当我们进⾏路由导航的时候,可以在导航⽬标组件⾥⾯直接使⽤ props 接收路由定义中带冒号的变量(如 id ),看下图所示:

最终该组件的显示结果为:这⾥是 id=1 的⽂章组件。

再来看⼀下对象模式,对象模式使⽤的场景⽐较窄,当我们需要给组件传递静态参数,也就是写死的参数的时候,会⽤到这个模式。

如下图,我们在路由规则定义的时候给出⼀个静态参数 name ,然后在组件⾥⾯接收该参数。

最终该组件的显示结果为:这⾥是 id=aaa 的⽂章组件。

最后来看⼀下箭头函数模式,箭头函数是 ES6 语法,在本专栏前⾯的章节有介绍。布尔模式的缺点在于 props 只能接收 params 参数,⽆法接收 query 参数。⽽函数模式就不⼀样了,它既可以接收 params 参数,也可以接受 query 参数。下图是路由规则的定义:

最终该路由规则导航 Article 组件的显示结果为:这⾥是 id=(route.query.id) 的⽂章组件。

其中 (route.query.id) 为具体的参数值。除了可以使⽤ route.query,也可以使⽤ route.params 。


文章作者: Syhan
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 Syhan !
评论
  目录