一、事件总线

事件总线(EventBus)用来解决兄弟组件之间通信的问题;

  1. 创建一个事件总线(一个空的 Vue 实例);
  2. 谁的数据被修改,谁监听事件;在 created 钩子中 监听事件:eventBus.$on(事件名, 事件函数)
  3. 谁发起修改,谁触发事件;eventBus.$emit(事件名, 数据)
  1. <body>
  2. <div id="app">
  3. <prev></prev>
  4. <hr>
  5. <next></next>
  6. </div>
  7. <script src="vue.js"></script>
  8. <script type="module">
  9. // import E from './7-eventBus.js'; // 不能导,会报错
  10. let eventBus = new Vue();
  11. let prev = {
  12. template: `<div :style="{background: color}">哥哥 {{color}}</div>`,
  13. data() {
  14. return {
  15. color: 'green'
  16. }
  17. },
  18. created() {
  19. // 一般在 created 钩子中监听
  20. // eventBus.$on('change-red', this.toRed)
  21. },
  22. mounted() {
  23. // 在 mounted 中监听也可以
  24. // eventBus.$on('change-red', this.toRed)
  25. },
  26. methods: {
  27. toRed(val) {
  28. console.log(val); // val 可以收到事件触发时传递数据
  29. this.color = val
  30. }
  31. }
  32. };
  33. let next = {
  34. template: `<div>弟弟:<button @click="fn">修改</button></div>`,
  35. data() {
  36. return {}
  37. },
  38. methods: {
  39. fn() {
  40. // 弟弟发起修改的一方,所以弟弟触发事件
  41. eventBus.$emit('change-red', 'red')
  42. }
  43. }
  44. };
  45. let vm = new Vue({
  46. el: '#app',
  47. components: {
  48. prev,
  49. next
  50. }
  51. });
  52. </script>
  53. </body>

eventBus.js

  1. // 一般真实项目中用到事件总线,只有一个;用的时候从这个模块中导出即可;
  2. import Vue from './vue.js'; // 现在浏览器直接导入还不行,但是用 vue-cli 或者 webpack 就可以
  3. export default new Vue;

event-bus应用.png

二、插槽

插槽:当引用组件时,我们可以向组件的自定义标签中嵌入内容,这些内容可以嵌入到子组件中,但是需要使用插槽,即 slot;

如何使用 slot?

  1. 在子组件中提前定义插槽,在需要的地方写一个 标签;
  2. 把你想要嵌入子组件的内容,写在子组件标签的里面;

匿名 slot 和具名 slot
子组件中的 slot 标签可以设置一个 name 属性,设置了 name 属性的 slot 具名 slot,没有设置 name 属性的叫做匿名 slot;
如果子组件标签中的内容要添加到指定的 slot,需要给这段模板指定 slot 属性;

  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="UTF-8">
  5. <title>Title</title>
  6. <style>
  7. * {
  8. margin: 0;
  9. padding: 0;
  10. }
  11. .modal {
  12. width: 100vw;
  13. height: 100vh;
  14. position: fixed;
  15. top: 0;
  16. display: flex;
  17. justify-content: center;
  18. align-items: center;
  19. background: rgba(0, 0, 0, .5);
  20. }
  21. .modal .content {
  22. width: 400px;
  23. height: 300px;
  24. /*padding: 15px;*/
  25. background: #00b38a;
  26. }
  27. .content .header {
  28. height: 80px;
  29. line-height: 80px;
  30. font-size: 20px;
  31. color: yellow;
  32. padding: 15px;
  33. border-bottom: 2px solid #000;
  34. }
  35. .content .body {
  36. height: 100px;
  37. padding-left: 15px;
  38. font-size: 30px;
  39. border-bottom: 2px solid #000;
  40. }
  41. </style>
  42. </head>
  43. <body>
  44. <div id="app">
  45. <button @click="open">打开</button>
  46. <modal :open.sync="flag">
  47. <div>提示:您确定要删除吗?</div>
  48. <div slot="before">提示:您确定要增加吗?</div>
  49. <div slot="before">
  50. <!--写了 slot = before 之后,这一段模板会添加到子组件的 name 属性为 before 的 slot上-->
  51. 这一段内容是具名 slot 的
  52. </div>
  53. <div>只要不写 slot 属性的模板,都会插入到匿名 slot 的位置</div>
  54. </modal>
  55. <button @click="open2">打开2</button>
  56. <modal :open.sync="flag2">
  57. <div slot="header" class="header">
  58. <span>!!</span>
  59. 警告!
  60. </div>
  61. <div slot="body" class="body">
  62. 不许偷看~~
  63. </div>
  64. <div slot="footer" class="footer">
  65. <button @click="cancel">取消</button>
  66. <button @click="confirm">确定</button>
  67. </div>
  68. </modal>
  69. </div>
  70. <script src="vue.js"></script>
  71. <script type="module">
  72. import modal from './8-modal.js';
  73. let vm = new Vue({
  74. el: '#app',
  75. data() {
  76. return {
  77. flag: false,
  78. flag2: false
  79. }
  80. },
  81. methods: {
  82. open() {
  83. this.flag = true;
  84. },
  85. open2() {
  86. this.flag2 = true
  87. },
  88. cancel() {
  89. this.flag2 = false;
  90. },
  91. confirm() {
  92. this.flag2 = false;
  93. console.log('确定');
  94. }
  95. },
  96. components: {
  97. modal
  98. }
  99. })
  100. </script>
  101. </body>
  102. </html>

moudal.js

  1. let template = `<div class="modal" v-show="open">
  2. <div class="content">
  3. <!--<slot name="before"></slot>
  4. <button @click="closeModal">关闭</button>
  5. <slot></slot> -->
  6. <slot></slot> <!--匿名slot-->
  7. <slot name="header"></slot> <!--具名slot-->
  8. <slot name="body"></slot>
  9. <slot name="footer"></slot>
  10. </div>
  11. </div>`;
  12. export default {
  13. template,
  14. data() {
  15. return {
  16. // open: false
  17. }
  18. },
  19. props: ['open'],
  20. methods: {
  21. closeModal() {
  22. // this.$emit('shutdown', false)
  23. this.$emit('update:open', false)
  24. }
  25. },
  26. mounted() {
  27. console.log('x');
  28. }
  29. }

三、Vue-router

路由:根据不同的请求 pathname,做不同的操作;
前端路由:单页面应用中由前端控制路由,根据不同的路由显示不同的页面(其实是不同的组件);单页面应用(SPA)只有一个 html 文件,切换路由时是切换组件,而不会请求其他的 html 文件;

vue-router
vue 的单页面应用路由需要使用 vue-router;vue-router 监听页面的路由发生变化(页面的 url 就是前端路由),渲染对应的组件;

使用 vue-router 需要使用以下组件:
是一个 vue-router 定义的标签,通过它可以切换到 to 属性指向的路由,如有这个路由对应的组件,就会把这个组件展示到 router-view
用来展示路由对应的组件

VueRouter 默认使用的 hash 模式,hash 改变不会引起页面的刷新,但是会生成历史记录;

vue-router 用法:

  1. 安装并引入 vue-router.js
  2. 设置路由映射表;
  3. 创建 VueRouter 的实例,创建 VueRouter 实例时需要传入路由映射表;
  4. 创建 Vue 实例时配置 router 属性,router 属性的值是 VueRouter 的实例;
  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="UTF-8">
  5. <title>Title</title>
  6. </head>
  7. <body>
  8. <div id="app">
  9. <!--router-link 叫做编程式导航-->
  10. <router-link to="/" tag="button">HOME</router-link> <!--router-link 是用来切换路由的,to 是指定点击 router-link 时要跳转到的路由; tag 指定 router-link 以什么标签渲染到页面中-->
  11. <router-link to="/home" tag="button">主页</router-link>
  12. <router-link to="/list" tag="button">LIST</router-link>
  13. <router-view></router-view>
  14. </div>
  15. <script src="vue.js"></script>
  16. <script src="vue-router.js"></script>
  17. <script>
  18. let home = {
  19. template: `<div>HOME</div>`
  20. };
  21. let list = {
  22. template: `<div>LIST</div>`
  23. };
  24. let fourOFour = {
  25. template: '<div>NOT FOUND</div>'
  26. };
  27. // 使用 vue-router 需要配置路由映射表
  28. let routes = [
  29. {
  30. path: '/', // path 是路由
  31. component: home // 路由对应的组件
  32. },
  33. {
  34. path: '/home',
  35. component: home
  36. },
  37. {
  38. path: '/list',
  39. component: list
  40. },
  41. {
  42. path: '*', // 除了路由映射表中配置的路由以外的路由,页面渲染成 home / fourOFour
  43. component: fourOFour
  44. }
  45. ];
  46. // 创建一个 VueRouter 的实例
  47. let router = new VueRouter({
  48. routes: routes // 创建 VueRouter 实例时,要传入路由映射表
  49. });
  50. // 创建 Vue 实例时配置 router 属性
  51. let vm = new Vue({
  52. el: '#app',
  53. router: router // 配置 router 属性
  54. });
  55. </script>
  56. </body>
  57. </html>

1. 路由方法

  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="UTF-8">
  5. <title>Title</title>
  6. </head>
  7. <body>
  8. <div id="app">
  9. <!--router-link 叫做编程式导航-->
  10. <router-link to="/" tag="button">HOME</router-link>
  11. <router-link to="/home" tag="button">主页</router-link>
  12. <router-link to="/list" tag="button">LIST</router-link>
  13. <router-view></router-view>
  14. </div>
  15. <script src="vue.js"></script>
  16. <script src="vue-router.js"></script>
  17. <script>
  18. let home = {
  19. template: `<div>HOME <button @click="goTo">去列表</button></div>`,
  20. methods: {
  21. goTo() {
  22. // this.$router.push('/list')
  23. this.$router.push({name: 'list', query: {name: 'mabin', age: 18}});
  24. // 对象中的 query 是路由后面的问号传参
  25. // #/list?name=mabin&age=18
  26. }
  27. }
  28. };
  29. let list = {
  30. template: `<div>LIST <button @click="back">滚回去</button></div>`,
  31. methods: {
  32. back() {
  33. // this.$router.go(-1) 返回上一页
  34. this.$router.go(-1);
  35. }
  36. }
  37. };
  38. let fourOFour = {
  39. template: '<div>NOT FOUND</div>'
  40. };
  41. // 使用 vue-router 需要配置路由映射表
  42. let routes = [
  43. {
  44. path: '/', // path 是路由
  45. component: home // 路由对应的组件
  46. },
  47. {
  48. path: '/home',
  49. component: home
  50. },
  51. {
  52. name: 'list', /*路由映射表中配置了 name 属性,使用 $router.push() 时可以传入一个对象
  53. // {name: 'list'}*/
  54. path: '/list',
  55. component: list
  56. },
  57. {
  58. path: '*', // 除了路由映射表中配置的路由以外的路由,页面渲染成 home
  59. component: fourOFour
  60. }
  61. ];
  62. // 创建一个 VueRouter 的实例
  63. let router = new VueRouter({
  64. routes: routes // 创建 VueRouter 实例时,要传入路由映射表
  65. });
  66. // 创建 Vue 实例时配置 router 属性
  67. let vm = new Vue({
  68. el: '#app',
  69. router: router // 配置 router 属性
  70. });
  71. // vue-router 给我们提供了方法用于切换路由:
  72. // 配置 router 以后,才能使用 $router 对象
  73. // 1. this.$router.go(-1) 返回上一页(其实是组件)
  74. // 2. this.$router.push('/list') 切换到指定的路由
  75. </script>
  76. </body>
  77. </html>

2. 嵌套路由

  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="UTF-8">
  5. <title>Title</title>
  6. </head>
  7. <body>
  8. <div id="app">
  9. <router-link to="/detail">详情</router-link>
  10. <router-view></router-view>
  11. </div>
  12. <template id="detail">
  13. <div>DETAIL
  14. <router-link to="/detail/profile">个人中心</router-link>
  15. <router-link to="/detail/about">关于我们</router-link>
  16. <router-view></router-view> <!--嵌套路由需要在子组件模板中添加 router-view-->
  17. </div>
  18. </template>
  19. <script src="vue.js"></script>
  20. <script src="vue-router.js"></script>
  21. <script>
  22. // 从当前页面可以进入到当前页面的子页面中,例如
  23. // 当前页面的路由:/detail
  24. // profile 页面路由:/detail/profile
  25. // about 页面路由:/detail/about
  26. // profile 和 about 是 /detail 的子路由;
  27. // 如何实现子路由(路由嵌套),在 /detail 下面配置 children 属性;
  28. // 在 /detail 对应的组件模板中添加 router-view
  29. let home = {
  30. template: `<div>HOME </div>`,
  31. };
  32. let detail = {
  33. template: `#detail`
  34. };
  35. let profile = {
  36. template: `<div>profile</div>`
  37. };
  38. let about = {
  39. template: `<div>about</div>`
  40. };
  41. // 使用 vue-router 需要配置路由映射表
  42. let routes = [
  43. {
  44. path: '/', // path 是路由
  45. component: home // 路由对应的组件
  46. },
  47. {
  48. path: '/home',
  49. component: home
  50. },
  51. {
  52. name: 'detail',
  53. path: '/detail', // 带 / 的都是一级路由
  54. component: detail,
  55. children: [
  56. {
  57. path: 'profile', // 二级路由前面不需要写 /
  58. component: profile
  59. },
  60. {
  61. path: 'about',
  62. component: about
  63. }
  64. ]
  65. }
  66. ];
  67. // 创建一个 VueRouter 的实例
  68. let router = new VueRouter({
  69. routes: routes // 创建 VueRouter 实例时,要传入路由映射表
  70. });
  71. // 创建 Vue 实例时配置 router 属性
  72. let vm = new Vue({
  73. el: '#app',
  74. router: router // 配置 router 属性
  75. });
  76. </script>
  77. </body>
  78. </html>

3. 路由参数

  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="UTF-8">
  5. <title>Title</title>
  6. </head>
  7. <body>
  8. <div id="app">
  9. <router-link to="/detail/3/z">文章3</router-link>
  10. <!--<router-link to="/detail/4/a?name=mabin&age=18">文章4</router-link>-->
  11. <router-link :to="{name: 'detail', params: {id: 4, text: 'a'}}">文章4</router-link>
  12. <!--/detail/:id/:text-->
  13. <router-view></router-view>
  14. </div>
  15. <template id="home">
  16. </template>
  17. <script src="vue.js"></script>
  18. <script src="vue-router.js"></script>
  19. <script>
  20. // 动态路由:/order/detail/:orderId
  21. // 路由中的某一部分不是写死的,可以变的一部分要写一个:
  22. // vue-router 也支持动态路由:
  23. // /detail/:id/:text 此时 id 和 text 是可以变的;现在我们要研究的问题是如何获取 id 和 text 的值;
  24. let home = {
  25. template: '#home'
  26. };
  27. let detail = {
  28. // 获取动态路由的参数 this.$route.params
  29. template: `<div>id:{{$route.params.id}};text:{{$route.params.text}}</div>`,
  30. mounted() {
  31. // this.$route.params 是动态路由的参数
  32. // this.$route.query 是问号传参的参数
  33. console.log(this.$route);
  34. }
  35. };
  36. let routes = [
  37. {
  38. name: 'detail',
  39. path: '/detail/:id/:text',
  40. component: detail
  41. }
  42. ];
  43. let router = new VueRouter({
  44. routes
  45. });
  46. let vm = new Vue({
  47. el: '#app',
  48. router
  49. });
  50. </script>
  51. </body>
  52. </html>