props
1、适用于的场景
- 父子组件之间的通信
2、注意事项
- 如果父组件给子组件传递数据(函数):本质上是子组件给父组件传递数据
- 如果父组件给子组件传递数据(非函数):本质就是父组件给子组件传递数据
3、书写方式(3种)
['todos']
{type:Array}
{type:Array, default:[]}
小提示
- 路由的props书写形式也有三种:布尔值、对象、函数形式
自定义事件
1、适用场景
- 子组件给父组件传递数据
- 使用$emit和 $on【简写@】
2、事件分类
- 原生DOM事件【click|mouseenter…】
- 自定义事件【子给父传递数据】,自定义事件也可以使用click等,不过要加上一些修饰关键字 native等
举例:
<Event1 @click="handler1"></Event1>
- 组件绑定原生DOM事件,并非原生DOM事件,而是所谓的自定义事
- 如果你想把自定义事件变为原生DOM事件,需要加上修饰符.native修饰
- 这个修饰符,可以把自定义事件【名字:原生DOM类型的】变为原生DOM事件,
**而且在父节点设置了单击事件,父节点里面的每个子节点都可以使用点击该单击事件 ** - 因为用到了事件的委派
**注意:**给原生的DOM绑定自定义事件是没有任何意义的,因为没有办法触发$emit函数
全局事件总线 $bus
1、适用场景
- 万能的
2、使用方式
- 组件实例的原型的原型指向的Vue.prototype
Vue.prototype.$bus = this
pubsub-js【发布消息与订阅】
- 在vue中很少使用,在react中使用较多
Vuex【仓库】
1、适用场景
- 万能的【里面存储的数据是非持久化的】
5个核心概念
核心概念:5
state
mutations
actions
getters
modules
插槽 (slot)
1、适用场景
- 适用于父子组件之间的通信
- 【父子通信的结构】
2、三大插槽
默认插槽
具名插槽
作用域插槽:子组件的数据来源于父组件,但是子组件的自己的结构有父亲决定。
v-model
- 原生DOM当中是有oninput事件的,它经常结合表单元素一起使用,当表单元素文本内容发生变化的时候,就会发出一次回调
- 区别于@change事件,@change需要在外部在进行一次点击事件的操作
Vue2中 可以通过value与input事件来实现 v-model功能
<input type='text' :value='msg' @input="msg = $event.target.value" />
<span>{{msg}}</span>
**深入学习 v-model ** 实现父子组件数据同步(即实现父子原件之间通信)
// 父组件 ModelTest.vue
// :value 到底是什么?这可是props,父子组件通信
// @input是什么?并非原生DOM的input事件 属于自定义事件
<CustomInput :value='msg' @input=' msg= $event' />
// 上面写法可以简化为
<CustomInput v-model='msg' />
// 子组件 CustomInput
// :value 到底是什么?动态属性
// @input是什么?给原生DOM绑定原生DOM事件
<input type='text' :value='value' @input="$emit('input',$event.target.value)">
v-model实现原理
- value与input事件实现的,而且还需要注意的是可以通过v-model实现父子组件数据同步的操作
属性修饰符 sync【组件通信方式的一种】
- 可以实现父子组件数据同步
- :money.sync 代表父组件给字符串传递props[money] 给当前子组件绑定一个自定义事件(update:money)
父组件
<template>
<div>
<h1>小明的爸爸现在有 {{ money }}元</h1>
<!--
:money 是父组件给子组件传递的props
@update:money 是子组件绑定的自定义事件
目前这种操作其实和v-model和相似
可以实现父子组件数据同步操作
-->
<Child :money="money" @updateMoney="updateMoney" />
<hr />
<!--
:money.sync
- ① 父组件给字符串传递props参数 money
- ② 给当前子组件绑定了一个自定义事件,并且事件名称为update:money
- 在子组件触发的时候,一定是触发的函数名称要是update:money
-->
<Child2 :money.sync="money" />
</div>
</template>
<script>
import Child from "./components/Child.vue";
import Child2 from "./components/Child2.vue";
export default {
name: "App",
data() {
return {
money: 10000,
};
},
components: { Child, Child2 },
methods: {
updateMoney($event) {
this.money = $event;
return money;
},
},
};
</script>
<style>
#app {
font-family: Avenir, Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
margin-top: 60px;
}
</style>
子组件child,一般形式下
<template>
<div>
<span>小明每次花100元</span>
<button @click="$emit('updateMoney', money-100)">花钱</button>
爸爸还剩下{{money}}元
</div>
</template>
<script>
export default {
name: "Child",
props: ["money"],
};
</script>
<style>
</style>
子组件Child2 使用了sync修饰符
<template>
<div>
<span>小明每次花100元</span>
<button @click="$emit('update:money', money-100)">花钱</button>
爸爸还剩下{{money}}元
</div>
</template>
<script>
export default {
name: "Child",
props: ["money"],
};
</script>
<style>
</style>
Element-Ui(2点要求)
在使用element-ui局部引入的操作的时候,应该现在控制台安装这个
npm i babel-plugin-component -D
在babel.config.js中配置如下内容
"plugins": [
[
"component",
{
"libraryName": "element-ui",
"styleLibraryName": "theme-chalk"
}
]
]
$attrs 与 $listeners (父子组件通信)
- $attrs:组件实例的属性,可以获取到父亲传递的props数据(前提子组件没有通过props接受)
- $listeners:组件实例的属性,可以获取到父亲传递自定义事件(对象形式呈现)
例子(对el-button进行二次封装)
父组件
<template>
<div>
<h2>自定义带Hover提示的按钮</h2>
<!--
当用户在使用我们封装的按钮的时候
需要向HintButton组件传递相应的参数(el-button进行二次封装)
-->
<HintButton
type="success"
icon="el-icon-delete"
size="mini"
title="提示按钮"
@click="handler"
></HintButton>
</div>
</template>
<script>
import HintButton from "./components/HintButton.vue";
export default {
name: "App",
data() {
return {};
},
components: { HintButton },
methods: {
// 点击事件的回调
handler(){
alert(666)
}
},
};
</script>
<style>
#app {
font-family: Avenir, Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
margin-top: 60px;
}
</style>
子组件
<template>
<div>
<!-- 可以巧妙的利用a标签实现按钮带有提示功能 -->
<a :title="title">
<!-- <el-button :type="$attrs.type">添加</el-button> -->
<!-- 下面这种写法必须使用v-bind,不能使用其简写形式 v-on也不能用@符进行替换 -->
<el-button v-bind="$attrs" v-on="$listeners">添加</el-button>
</a>
</div>
</template>
<script>
export default {
name: "HintButton",
props: ["title"],
mounted() {
/*
$attrs 属于组件的一个属性
可以获取父组件传递过来的props数据 是一个对象
- 对于子组件而言,父组件给的数据可以利用props接收
- 但是需要注意:如果子组件通过props接收的属性,在$attrs属性当中是获取不到的
$listeners
- 也是组件实例自身的一个属性,它可以获取到父组件给子组件传递自定义事件
*/
console.log(this.$attrs);
console.log(this.$listeners)
},
};
</script>
<style>
</style>
相应的解释,写在了上述代码当中
$children 与 $parent (可以实现父子组件通信)
- ref:可以在父组件内部获取子组件—实现父子通信
ref:获取节点(组件标签)
注意:ref可以获取真实DOM节点,当然也可以获取子组件标签(操作子组件的数据与方法)
- $children:可以在父组件内部获取全部的子组件【返回数组】
如果子组件过多,第0项可能不是小明,尽可能不要使用 中括号+下标的形式来书写
this.$children[0].money -= 200 // 不用
// 推荐使用forEach
- $parent:可以在子组件内部获取唯一的父组件【返回组件实例】
混入mixin
- 如果项目当中出现很多结构类似功能,想到组件复用
- 如果项目当中很多组件JS业务逻辑相似,想到mixin【可以把多个组件JS部分重复、相似地方】
插槽
- 插槽:可以实现父子组件通信(通信的结构)
默认插槽
具名插槽
作用域插槽:子组件的数据来源于父组件,但是子组件是决定不了自身结构与外观
解构对象的时候,我们必须要想到的就是kv必须要一致才行,这一点很关键 - 使用插槽的时候,子组件决定不了它的结构,它的结构是由父组件决定的
作用域插槽的实例
父组件
<template>
<div>
<h2>效果一:显示TODO列表时,已完成的TODO为绿色</h2>
<!-- 首先子组件的数据来源于父组件 -->
<List :todos="todos">
<!-- 子组件决定不了自己的结构与样式,需要通过父组件决定 -->
<!-- todo是子组件返回回来的对象 -->
<template slot-scope="todo">
<span :style="{ color: todo.todo.icComplete ? 'green' : 'red' }">{{
todo.todo.text
}}</span>
</template>
</List>
<hr />
<List1 :todos="todos">
<!-- 子给父传的是两个,可以用一个对象来接收进行解构 -->
<template slot-scope="{todo, $index}">
<span>{{$index}} --- {{todo.text}}</span>
</template>
</List1>
</div>
</template>
<script>
import List from "./components/List.vue";
import List1 from "./components/List1.vue";
export default {
name: "App",
data() {
return {
todos: [
{ id: 1, text: "AAA", icComplete: false },
{ id: 2, text: "BBB", icComplete: true },
{ id: 3, text: "CCC", icComplete: false },
{ id: 4, text: "DDD", icComplete: false },
],
};
},
components: { List, List1 },
};
</script>
<style>
#app {
font-family: Avenir, Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
margin-top: 60px;
}
</style>
子组件
<template>
<div>
<ul>
<li v-for="(item, index) in todos" :key="index">
<!-- 作用域插槽 :不是props,其将每一个数据回传 -->
<slot :todo="item" :$index="index"></slot>
</li>
</ul>
</div>
</template>
<script>
export default {
name: "List",
props: {
todos: Array,
},
};
</script>
<style>
</style>
子组件占了一个坑,父组件来填坑的感觉