一、事件总线
事件总线(EventBus)用来解决兄弟组件之间通信的问题;
- 创建一个事件总线(一个空的 Vue 实例);
- 谁的数据被修改,谁监听事件;在 created 钩子中 监听事件:eventBus.$on(事件名, 事件函数)
- 谁发起修改,谁触发事件;eventBus.$emit(事件名, 数据)
<body><div id="app"><prev></prev><hr><next></next></div><script src="vue.js"></script><script type="module">// import E from './7-eventBus.js'; // 不能导,会报错let eventBus = new Vue();let prev = {template: `<div :style="{background: color}">哥哥 {{color}}</div>`,data() {return {color: 'green'}},created() {// 一般在 created 钩子中监听// eventBus.$on('change-red', this.toRed)},mounted() {// 在 mounted 中监听也可以// eventBus.$on('change-red', this.toRed)},methods: {toRed(val) {console.log(val); // val 可以收到事件触发时传递数据this.color = val}}};let next = {template: `<div>弟弟:<button @click="fn">修改</button></div>`,data() {return {}},methods: {fn() {// 弟弟发起修改的一方,所以弟弟触发事件eventBus.$emit('change-red', 'red')}}};let vm = new Vue({el: '#app',components: {prev,next}});</script></body>
eventBus.js
// 一般真实项目中用到事件总线,只有一个;用的时候从这个模块中导出即可;import Vue from './vue.js'; // 现在浏览器直接导入还不行,但是用 vue-cli 或者 webpack 就可以export default new Vue;

二、插槽
插槽:当引用组件时,我们可以向组件的自定义标签中嵌入内容,这些内容可以嵌入到子组件中,但是需要使用插槽,即 slot;
如何使用 slot?
- 在子组件中提前定义插槽,在需要的地方写一个 标签;
- 把你想要嵌入子组件的内容,写在子组件标签的里面;
匿名 slot 和具名 slot
子组件中的 slot 标签可以设置一个 name 属性,设置了 name 属性的 slot 具名 slot,没有设置 name 属性的叫做匿名 slot;
如果子组件标签中的内容要添加到指定的 slot,需要给这段模板指定 slot 属性;
<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><title>Title</title><style>* {margin: 0;padding: 0;}.modal {width: 100vw;height: 100vh;position: fixed;top: 0;display: flex;justify-content: center;align-items: center;background: rgba(0, 0, 0, .5);}.modal .content {width: 400px;height: 300px;/*padding: 15px;*/background: #00b38a;}.content .header {height: 80px;line-height: 80px;font-size: 20px;color: yellow;padding: 15px;border-bottom: 2px solid #000;}.content .body {height: 100px;padding-left: 15px;font-size: 30px;border-bottom: 2px solid #000;}</style></head><body><div id="app"><button @click="open">打开</button><modal :open.sync="flag"><div>提示:您确定要删除吗?</div><div slot="before">提示:您确定要增加吗?</div><div slot="before"><!--写了 slot = before 之后,这一段模板会添加到子组件的 name 属性为 before 的 slot上-->这一段内容是具名 slot 的</div><div>只要不写 slot 属性的模板,都会插入到匿名 slot 的位置</div></modal><button @click="open2">打开2</button><modal :open.sync="flag2"><div slot="header" class="header"><span>!!</span>警告!</div><div slot="body" class="body">不许偷看~~</div><div slot="footer" class="footer"><button @click="cancel">取消</button><button @click="confirm">确定</button></div></modal></div><script src="vue.js"></script><script type="module">import modal from './8-modal.js';let vm = new Vue({el: '#app',data() {return {flag: false,flag2: false}},methods: {open() {this.flag = true;},open2() {this.flag2 = true},cancel() {this.flag2 = false;},confirm() {this.flag2 = false;console.log('确定');}},components: {modal}})</script></body></html>
moudal.js
let template = `<div class="modal" v-show="open"><div class="content"><!--<slot name="before"></slot><button @click="closeModal">关闭</button><slot></slot> --><slot></slot> <!--匿名slot--><slot name="header"></slot> <!--具名slot--><slot name="body"></slot><slot name="footer"></slot></div></div>`;export default {template,data() {return {// open: false}},props: ['open'],methods: {closeModal() {// this.$emit('shutdown', false)this.$emit('update:open', false)}},mounted() {console.log('x');}}
三、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 用法:
- 安装并引入 vue-router.js
- 设置路由映射表;
- 创建 VueRouter 的实例,创建 VueRouter 实例时需要传入路由映射表;
- 创建 Vue 实例时配置 router 属性,router 属性的值是 VueRouter 的实例;
<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><title>Title</title></head><body><div id="app"><!--router-link 叫做编程式导航--><router-link to="/" tag="button">HOME</router-link> <!--router-link 是用来切换路由的,to 是指定点击 router-link 时要跳转到的路由; tag 指定 router-link 以什么标签渲染到页面中--><router-link to="/home" tag="button">主页</router-link><router-link to="/list" tag="button">LIST</router-link><router-view></router-view></div><script src="vue.js"></script><script src="vue-router.js"></script><script>let home = {template: `<div>HOME</div>`};let list = {template: `<div>LIST</div>`};let fourOFour = {template: '<div>NOT FOUND</div>'};// 使用 vue-router 需要配置路由映射表let routes = [{path: '/', // path 是路由component: home // 路由对应的组件},{path: '/home',component: home},{path: '/list',component: list},{path: '*', // 除了路由映射表中配置的路由以外的路由,页面渲染成 home / fourOFourcomponent: fourOFour}];// 创建一个 VueRouter 的实例let router = new VueRouter({routes: routes // 创建 VueRouter 实例时,要传入路由映射表});// 创建 Vue 实例时配置 router 属性let vm = new Vue({el: '#app',router: router // 配置 router 属性});</script></body></html>
1. 路由方法
<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><title>Title</title></head><body><div id="app"><!--router-link 叫做编程式导航--><router-link to="/" tag="button">HOME</router-link><router-link to="/home" tag="button">主页</router-link><router-link to="/list" tag="button">LIST</router-link><router-view></router-view></div><script src="vue.js"></script><script src="vue-router.js"></script><script>let home = {template: `<div>HOME <button @click="goTo">去列表</button></div>`,methods: {goTo() {// this.$router.push('/list')this.$router.push({name: 'list', query: {name: 'mabin', age: 18}});// 对象中的 query 是路由后面的问号传参// #/list?name=mabin&age=18}}};let list = {template: `<div>LIST <button @click="back">滚回去</button></div>`,methods: {back() {// this.$router.go(-1) 返回上一页this.$router.go(-1);}}};let fourOFour = {template: '<div>NOT FOUND</div>'};// 使用 vue-router 需要配置路由映射表let routes = [{path: '/', // path 是路由component: home // 路由对应的组件},{path: '/home',component: home},{name: 'list', /*路由映射表中配置了 name 属性,使用 $router.push() 时可以传入一个对象// {name: 'list'}*/path: '/list',component: list},{path: '*', // 除了路由映射表中配置的路由以外的路由,页面渲染成 homecomponent: fourOFour}];// 创建一个 VueRouter 的实例let router = new VueRouter({routes: routes // 创建 VueRouter 实例时,要传入路由映射表});// 创建 Vue 实例时配置 router 属性let vm = new Vue({el: '#app',router: router // 配置 router 属性});// vue-router 给我们提供了方法用于切换路由:// 配置 router 以后,才能使用 $router 对象// 1. this.$router.go(-1) 返回上一页(其实是组件)// 2. this.$router.push('/list') 切换到指定的路由</script></body></html>
2. 嵌套路由
<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><title>Title</title></head><body><div id="app"><router-link to="/detail">详情</router-link><router-view></router-view></div><template id="detail"><div>DETAIL<router-link to="/detail/profile">个人中心</router-link><router-link to="/detail/about">关于我们</router-link><router-view></router-view> <!--嵌套路由需要在子组件模板中添加 router-view--></div></template><script src="vue.js"></script><script src="vue-router.js"></script><script>// 从当前页面可以进入到当前页面的子页面中,例如// 当前页面的路由:/detail// profile 页面路由:/detail/profile// about 页面路由:/detail/about// profile 和 about 是 /detail 的子路由;// 如何实现子路由(路由嵌套),在 /detail 下面配置 children 属性;// 在 /detail 对应的组件模板中添加 router-viewlet home = {template: `<div>HOME </div>`,};let detail = {template: `#detail`};let profile = {template: `<div>profile</div>`};let about = {template: `<div>about</div>`};// 使用 vue-router 需要配置路由映射表let routes = [{path: '/', // path 是路由component: home // 路由对应的组件},{path: '/home',component: home},{name: 'detail',path: '/detail', // 带 / 的都是一级路由component: detail,children: [{path: 'profile', // 二级路由前面不需要写 /component: profile},{path: 'about',component: about}]}];// 创建一个 VueRouter 的实例let router = new VueRouter({routes: routes // 创建 VueRouter 实例时,要传入路由映射表});// 创建 Vue 实例时配置 router 属性let vm = new Vue({el: '#app',router: router // 配置 router 属性});</script></body></html>
3. 路由参数
<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><title>Title</title></head><body><div id="app"><router-link to="/detail/3/z">文章3</router-link><!--<router-link to="/detail/4/a?name=mabin&age=18">文章4</router-link>--><router-link :to="{name: 'detail', params: {id: 4, text: 'a'}}">文章4</router-link><!--/detail/:id/:text--><router-view></router-view></div><template id="home"></template><script src="vue.js"></script><script src="vue-router.js"></script><script>// 动态路由:/order/detail/:orderId// 路由中的某一部分不是写死的,可以变的一部分要写一个:// vue-router 也支持动态路由:// /detail/:id/:text 此时 id 和 text 是可以变的;现在我们要研究的问题是如何获取 id 和 text 的值;let home = {template: '#home'};let detail = {// 获取动态路由的参数 this.$route.paramstemplate: `<div>id:{{$route.params.id}};text:{{$route.params.text}}</div>`,mounted() {// this.$route.params 是动态路由的参数// this.$route.query 是问号传参的参数console.log(this.$route);}};let routes = [{name: 'detail',path: '/detail/:id/:text',component: detail}];let router = new VueRouter({routes});let vm = new Vue({el: '#app',router});</script></body></html>
