一、事件总线
事件总线(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 / fourOFour
component: 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: '*', // 除了路由映射表中配置的路由以外的路由,页面渲染成 home
component: 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-view
let 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.params
template: `<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>