Vue基础-Day05

前端路由

SPA介绍

spa 是 single page application 简写,意思是单页面应用程序。vue适合开发spa类型的项目。 单页面应用体验

优点:

  • 业务场景的切换,性能很好。
  • 集中维护一个网站的功能。
  • 完全的前后端分离(前后端可以并行开发,提供系统开发效率)

缺点:

  • 所有的功能集中的一个页面,依赖的资源是非常多的,加载第一次的时候很慢(首屏加载)。
  • 业务复杂度很高(解决方案:vue组件,前端路由)

前端路由介绍

目标:熟悉前端路由的概念

前端路由:根据不同的url地址,页面上展示不同的内容(根据url地址的不同分发到不同的组件。)

image.png

注意:浏览的历史支持浏览器历史回退和前进按钮操作(Ajax本身的局部更新是不支持) 前端路由模式解决了SPA应用开发的复杂性

前端路由原理

目标:基于哈希hash的路由原理(通过地址栏hash字符串的改变,去更新页面的内容。)

  1. <!-- 菜单 -->
  2. <div>
  3. <a href="#/">发现音乐</a>
  4. <a href="#/my">我的音乐</a>
  5. <a href="#/friend">朋友</a>
  6. </div>
  7. <div id="content">
  8. <!-- 内容 -->
  9. </div>
  10. <!-- 前端路由:根据hash值得改变,去更新页面内容。-->
  11. <!--
  12. 1. 监听hash值得改变
  13. - window.onhashchange = function() { console.log('ok') }
  14. 2. 改变后渲染对应内容
  15. - 获取hash location.hash 然后去判断 渲染不同内容
  16. -->
  17. <script>
  18. // 内容容器
  19. const content = document.querySelector('#content')
  20. // 监听hash改变
  21. window.onhashchange = function () {
  22. // 改变后的hash
  23. const hash = location.hash
  24. // path 值可能是:/ /my /friend
  25. const path = hash.replace('#', '')
  26. // 判断路径
  27. switch (path) {
  28. case '/':
  29. content.innerHTML = '发现音乐的网页内容'
  30. break;
  31. case '/my':
  32. content.innerHTML = '我的音乐的网页内容'
  33. break;
  34. case '/friend':
  35. content.innerHTML = '朋友F的网页内容'
  36. break;
  37. default:
  38. break;
  39. }
  40. }
  41. </script>

总结:

  1. url地址的hash的变化不会导致浏览器发送请求
  2. hash的变化会触发事件函数onhashchange

vue-router

基本介绍

目标:熟悉vue-router基本信息

vue-router是基于vue的js插件,实现了前端路由功能。

下载地址:https://unpkg.com/vue-router/dist/vue-router.js

基本使用

目标:基于url地址的变化实现组件的切换

  • 准备组件配置对象
  • 定义路由映射规则(什么路径对应什么组件)
  • 初始化 vue-router 实例
  • 需要把路由实例挂载到vue中
  • 路由跳转链接 <router-link />
  • 路由显示组件的位置 <router-view />
  1. <div id="app">
  2. <!-- 菜单 -->
  3. <div>
  4. <!-- 5. 路由连接组件 -->
  5. <router-link to="/">发现音乐</router-link>
  6. <router-link to="/my">我的音乐</router-link>
  7. <router-link to="/friend">朋友</router-link>
  8. </div>
  9. <div>
  10. <!-- 内容 -->
  11. <!-- 6. 显示当前路由规则对应的组件 -->
  12. <router-view></router-view>
  13. </div>
  14. </div>
  15. <script src="./vue.js"></script>
  16. <script src="./vue-router.js"></script>
  17. <script>
  18. // 1. 准备组件配置对象
  19. const Home = { template: `<div>发现音乐COMPONENT</div>` }
  20. const My = { template: `<div>我的音乐COMPONENT</div>` }
  21. const Friend = { template: `<div>朋友COMPONENT</div>` }
  22. // 2. 定义路由规则(vue-router提供)
  23. const routes = [
  24. // path 是路径 component 对应的组件配置对象
  25. { path: '/', component: Home },
  26. { path: '/my', component: My },
  27. { path: '/friend', component: Friend }
  28. ]
  29. // 3. 路由初始化
  30. // const router = new VueRouter({
  31. // // 传入路由规则
  32. // routes: routes
  33. // })
  34. const router = new VueRouter({ routes })
  35. // 根实例
  36. new Vue({
  37. el: '#app',
  38. // 4. 在vue的根实例下进行 路由实例 挂载
  39. router
  40. })
  41. </script>

总结:点击不同的按钮显示不同的组件

  1. 按钮由vue-router提供
  2. 组件显示到哪里由控制
  3. 路由按钮的跳转路径和组件的关系由谁维护?路由映射
  4. 要想这些配置生效,需要把router对象挂载到Vue实例中

动态路由基本使用

目标:熟悉动态路由的用法

动态路由:根据不同的URL地址,映射到同一个组件,这样做主要是为了节省组件(简化代码)

  1. <div id="app">
  2. <router-view></router-view>
  3. </div>
  4. <script src="./vue.js"></script>
  5. <script src="./vue-router.js"></script>
  6. <script>
  7. // 用户列表组件配置对象
  8. const UserList = {
  9. template: `
  10. <ul>
  11. <li><router-link to="/user/1001">tom</router-link></li>
  12. <li><router-link to="/user/1002">tony</router-link></li>
  13. <li><router-link to="/user/1003">lucy</router-link></li>
  14. </ul>
  15. `
  16. }
  17. // 用户详情组件配置对象
  18. const UserDetail = {
  19. template: `<div> 用户详情组件 用户的ID是:{{$route.params.id}}</div>`
  20. // 模板获取路径传参:$route.params.id
  21. // 组件的函数中获取:this.$route.params.id
  22. }
  23. const routes = [
  24. // 动态路由
  25. { path: '/user/:id', component: UserDetail }
  26. ]
  27. const router = new VueRouter({ routes })
  28. // 根实例
  29. new Vue({
  30. el: '#app',
  31. router
  32. })
  33. </script>

总结:

  1. 路由映射规则 { path: '/user/:id', component: UserDetail }
  2. 获取路径传参:{{$route.params.id}}
  3. <li><router-link to="/user/1001">tom</router-link></li>

动态路由的问题和解决方案

问题:动态路由的组件默认会进行复用,从而导致生命周期不会重新触发,所以需要通过watch侦听$route的变化。从而得到动态路由参数的最新值

  1. <body>
  2. <div id="app">
  3. <!-- 2、准备点击按钮 -->
  4. <!-- to表示要跳转到哪个路径 -->
  5. <router-link to='/users/123'>张三</router-link>
  6. <router-link to='/users/456'>李四</router-link>
  7. <router-link to='/users/789'>王五</router-link>
  8. <!-- 3、需要告诉前端路由组件显示在哪里 -->
  9. <hr>
  10. <!-- 路由组件填充位置 -->
  11. <router-view></router-view>
  12. </div>
  13. <script src="lib/vue.js"></script>
  14. <script src="lib/vue-router.js"></script>
  15. <script>
  16. // vue-router基本使用
  17. // 前端路由要解决的问题:不同的url地址(点击不同按钮)显示不同的组件
  18. // 1、定义基本的组件结果
  19. const UserScore = {
  20. data() {
  21. return {
  22. // 当前用户的id
  23. currentId: -1,
  24. list: [{
  25. id: 123,
  26. uname: '张三',
  27. score: 100
  28. }, {
  29. id: 456,
  30. uname: '李四',
  31. score: 99
  32. }, {
  33. id: 789,
  34. uname: '王五',
  35. score: 98
  36. }]
  37. }
  38. },
  39. template: `
  40. <div>
  41. <!-- 获取动态路由参数的id -->
  42. <!-- <div>学生的成绩{{$route.params.id}}</div> -->
  43. <div>学生的成绩{{currentId}}</div>
  44. <div>学生姓名:{{userDetail.uname}}</div>
  45. <div>学生成绩:{{userDetail.score}}</div>
  46. </div>
  47. `,
  48. computed: {
  49. // 根据list和currentId计算当前用户的信息
  50. userDetail() {
  51. const info = this.list.find((item) => {
  52. // console.log(item.id, this.currentId)
  53. return item.id === parseInt(this.currentId)
  54. })
  55. return info ? info : {}
  56. // if (info) {
  57. // return info
  58. // } else {
  59. // return {}
  60. // }
  61. }
  62. },
  63. watch: {
  64. $route(to, from) {
  65. // to 要跳转到哪里去
  66. // from 从哪里跳过来
  67. this.currentId = to.params.id
  68. }
  69. }
  70. // created() {
  71. // console.log(this.$route.params.id)
  72. // }
  73. }
  74. // 4、点击按钮时,如何根据跳转的路径找到对应的组件呢?需要配置路由映射(路由的路径和组件的关联关系)
  75. const routes = [
  76. // path表示路由的路径
  77. // component表示对应的组件
  78. { path: '/users/:id', component: UserScore },
  79. ]
  80. // 使上述路由映射生效
  81. const router = new VueRouter({
  82. routes: routes
  83. })
  84. // 5、把路由实例对象挂载到Vue实例中(把vue-router和Vue关联到一块)
  85. new Vue({
  86. router: router,
  87. el: '#app',
  88. data: {}
  89. })
  90. </script>
  91. </body>

总结:

  1. 动态路由参数的获取
  2. 动态路由参数的更新
  3. 侦听器用法
  4. 计算属性用法

注意:动态路由可以把多个路由链接映射到同一个组件,这样可以节省很多组件代码。

  • 如何保证侦听器第一次就触发?
  1. watch: {
  2. // $route(to) {
  3. // console.log('--------------')
  4. // // to.params.id 和 this.$route.params.id 类似
  5. // this.userId = to.params.id
  6. // }
  7. // -------------------------------------------
  8. // 默认情况下,侦听器首次加载时不会触发
  9. // 如果希望首次就触发,那么需要按照如下的方式进行配置
  10. $route: {
  11. // 触发的函数
  12. handler: 'handleUserId',
  13. // 控制首次触发
  14. immediate: true
  15. }
  16. }

总结:immediate: true用于控制侦听器的首次触发函数。

路由参数传递方式

目标:熟悉路由的参数传递的props方式

  1. props值设置为true
  1. const routes = [
  2. { path: '/users/:id', component: UserInfo, props: true }
  3. ]
  1. // 如下的组对路由的API有依赖
  2. const UserInfo = {
  3. // 获取路由参数
  4. props: ['id'],
  5. template: `
  6. <div>
  7. <div>用户信息{{id}}</div>
  8. <user-msg></user-msg>
  9. </div>
  10. `,
  11. components: {
  12. UserMsg
  13. }
  14. }

如果配置路由映射时,props值设置为true,那么在路由组件中可以直接通过props:['参数名称']方式获取参数。

  1. props值设置为对象
  1. const routes = [
  2. {
  3. path: '/users/:id',
  4. component: UserInfo,
  5. // props的值设置为对象形式,那么组件中可以直接获取对象里面的信息
  6. // 这种方式传递参数一般用于向组件传入固定的数据。
  7. props: {
  8. info: 'hello',
  9. msg: 'nihao',
  10. pi: 3.14
  11. }
  12. }
  13. ]
  1. // 如下的组对路由的API有依赖const UserInfo = { // 获取路由参数 props: ['info', 'msg', 'pi'], template: ` <div> <div>用户信息{{info}}{{msg}}{{pi}}</div> <user-msg></user-msg> </div> `, components: { UserMsg }}

总结:

  1. props值设置为对象后,组件中可以通过props直接获取对象中所有的属性值
  2. 应用场景:向组件传入固定的数据
  1. props值设置为函数
  1. const routes = [ { path: '/users/:id', component: UserInfo, // props的值设置为对象形式,那么组件中可以直接获取对象里面的信息 // 这种方式传递参数一般用于向组件传入固定的数据。 props: (route) => { // 这里的形参route其实就是之前所使用的$route // 函数中return的对象中的属性会直接提供路由组件使用 // 基于函数的方式既可以处理动态参数也可以同时处理静态数据 return { uid: route.params.id, pi: 3.14 } } }]
  1. // 如下的组对路由的API有依赖const UserInfo = { // 获取路由参数 props: ['uid', 'pi'], template: ` <div> <div>用户信息{{uid}}{{pi}}</div> <user-msg></user-msg> </div> `, components: { UserMsg }}

总结:

  1. props值为true表示可以通过props处理路由的动态参数
  2. props值为对象表示可以通过props处理路由静态的数据
  3. props值为函数表示既可以处理路由的动态参数也可以处理静态参数
  • 动态路由参数可以有多个
  1. const routes = [ // 动态路由的路径格式是自定义的 // 并且动态参数可以有多个(由实际的需求决定) { path: '/users/:id/articles/:aid', component: UserInfo, props: true }]
  1. <router-link to='/users/789/articles/222'>张三</router-link>

总结:动态路由的路径自定义,可以有多个动态路由参数,具体由实际需求决定。

路由重定向

目标:熟悉路由重定向的配置方式

路由重定向:访问某一个路径,自动跳转到另外一个路径。

  1. <div id="app"> <!-- 打开页面:默认访问的路由地址 `/` --> <!-- 我们的 '/home' 才是首页路由的路径 --> <!-- 当默认打开页面的时候 重定向到 首页 --> <router-view></router-view></div><script src="./vue.js"></script><script src="./vue-router.js"></script><script> const router = new VueRouter({ // 指定路由规则 routes: [ // redirect 重定向的路由地址 { path: '/', redirect: '/home' }, { path: '/home', component: { template: `<h1>首页</h1>` } } ] }) new Vue({ el: '#app', router })</script>

总结:访问A路径自动跳转到B路径(我们希望两个路径显示同一个组件)

编程式导航

目标:熟悉编程式导航实现路由跳转的方式

编程式导航:通过js的方式触发路由的跳转。

  1. <body> <div id="app"> <!-- 只有一个路由填充位 --> <router-view></router-view> </div> <script src="lib/vue.js"></script> <script src="lib/vue-router.js"></script> <script> // vue-router 编程式导航:使用js方法实现路由跳转 const Login = { template: ` <div> <div>登录功能</div> <hr> <div> 用户名:<input type="text"> </div> <div> 密码:<input type="password"> </div> <div> <button @click='handleLogin'>登录</button> </div> </div> `, methods: { handleLogin() { // 实现登录功能 // 获取表单数据,调用接口传递参数验证用户信息是否正确 // 假设如下的ret是登录接口返回的结果,status值为1表示登录成功 const ret = { status: 1 } if (ret.status === 1) { // 登录成功,跳转到主页面(切换路由组件) // 如下的this.$router由vue-router提供 // push方法用于实现路由跳转,参数表示路由的路径 this.$router.push('/home') } } } } const Home = { template: ` <div> 主页 </div> ` } const routes = [ { path: '/', redirect: '/login' }, { path: '/login', component: Login }, { path: '/home', component: Home } ] const router = new VueRouter({ routes }) new Vue({ router, el: '#app', data: {} }) </script></body>

总结:编程式导航就是通过js方式控制路由的跳转

  • $route 获取路由相关信息(路由传参 this.$route.params)
  • $router 提供路由相关函数 ( 跳转方法 this.$router.push() )

嵌套路由

目标:熟悉嵌套路由的用法

要进行路由的嵌套,只需要在一级路由规则下,加上一个属性 children,即可定义二级路由规则。

  1. <body>
  2. <div id="app">
  3. <!-- 只有一个路由填充位 -->
  4. <router-view></router-view>
  5. </div>
  6. <script src="lib/vue.js"></script>
  7. <script src="lib/vue-router.js"></script>
  8. <script>
  9. // vue-router 嵌套路由:在路由组件中再次配置路由填充位
  10. const Login = {
  11. template: `
  12. <div>
  13. <div>登录功能</div>
  14. <hr>
  15. <div>
  16. 用户名:<input type="text">
  17. </div>
  18. <div>
  19. 密码:<input type="password">
  20. </div>
  21. <div>
  22. <button @click='handleLogin'>登录</button>
  23. </div>
  24. </div>
  25. `,
  26. methods: {
  27. handleLogin() {
  28. // 实现登录功能
  29. // 获取表单数据,调用接口传递参数验证用户信息是否正确
  30. // 假设如下的ret是登录接口返回的结果,status值为1表示登录成功
  31. const ret = {
  32. status: 1
  33. }
  34. if (ret.status === 1) {
  35. // 登录成功,跳转到主页面(切换路由组件)
  36. // 如下的this.$router由vue-router提供
  37. // push方法用于实现路由跳转,参数表示路由的路径
  38. this.$router.push('/home')
  39. }
  40. }
  41. }
  42. }
  43. const Home = {
  44. template: `
  45. <div>
  46. <div>主页内容</div>
  47. <hr>
  48. <router-link to='/home/tech'>科技</router-link>
  49. <router-link to='/home/edu'>教育</router-link>
  50. <router-link to='/home/jinrong'>金融</router-link>
  51. <hr>
  52. <router-view></router-view>
  53. </div>
  54. `
  55. }
  56. const Tech = {
  57. template: '<div>Tech</div>'
  58. }
  59. const Edu = {
  60. template: '<div>Edu</div>'
  61. }
  62. const Jinrong = {
  63. template: '<div>Jinrong</div>'
  64. }
  65. const routes = [
  66. { path: '/', redirect: '/login' },
  67. { path: '/login', component: Login },
  68. {
  69. path: '/home',
  70. component: Home,
  71. // 这里可以通过重定向方式控制二级路由组件的显示
  72. redirect: '/home/tech',
  73. // 通过children属性配置嵌套路由
  74. children: [
  75. { path: 'tech', component: Tech },
  76. { path: 'edu', component: Edu },
  77. { path: 'jinrong', component: Jinrong }
  78. ]
  79. }
  80. ]
  81. const router = new VueRouter({
  82. routes
  83. })
  84. new Vue({
  85. router,
  86. el: '#app',
  87. data: {}
  88. })
  89. </script>
  90. </body>

总结:嵌套路由,在路由组件中再次配置路由填充位,支持多级嵌套

  • 二级路由的路径不要添加斜杠
  • 一级路由的路径需要添加斜杠
  • 二级路由的映射也支持重定向