第一种: props
适用于的场景:父子组件通信
注意事项:
如果父组件给子组件传递数据(函数),本质其实是子组件给父组件传递数据
如果父组件给子组件传递数据(非函数),本质就是父组件给子组件传递数据
书写方式: 3种
- ['todos'],
- {type:Array},
- {type:Array,default:() =>{ return [] }}
小提示:路由的props
书写形式:布尔值,对象、函数形式
第二种:自定义事件
适用于场景:子组件给父组件传递数据
$on与$emit
子组件使用$emit向父组件传递('变量名',传递的数据)
父组件使用$on 接收变量名
子组件:
<button @click="$emit('update:money',money - 100)">花钱</button>
父组件:
<child :money="money" @update:money="money = $event" />
第三种:全局事件总线$bus
适用于场景:万能
需要在main.js中配置
//统一接口api文件夹里面全部请求函数
import * as API from '@/api'
new Vue({
render: (h) => h(App),
//全局事件总线$bus配置
beforeCreate() {
Vue.prototype.$bus = this;
Vue.prototype.$API = API
},
//需要把router进行注册
//可以让全部的组件(非路由|路由组件)都可以获取到$route|$router属性
//$route(路由):可以获取到路由信息(path、query、params)
//$router:进行编程式导航路由跳转push||replace
router,
//在入口文件这里注册store,在每一个组件的身上都拥有一个$store这个属性
store,
}).$mount('#app')
通知$bus事件
// 修改响应式数据
changeCurrentIndex (index) {
this.currentIndex = index
// 通知兄弟组件,当前的索引值是多少
this.$bus.$emit('getIndex', this.currentIndex)
}
全局事件总线获取兄弟组件传递过来的参数
mounted () {
// 全局事件总线获取兄弟组件传递过来的参数
this.$bus.$on('getIndex', (index) => {
this.currentIndex = index
})
},
第四种:pubsub-js,在react框架中使用比较多。(发布与订阅)
适用于场景:万能
工具库: PubSubJS
下载:
npm install pubsub-js --save
使用:
import PubSub from 'pubsub-js' //引入
PubSub.subscribe('delete', (msg,data)=>{ }); //订阅
PubSub.publish('delete', data) //发布消息
谁接数据就在componentDidMount里面接收订阅消息,然后再componentWillUNmount中取消订阅,发布消息是全局都可以发布
第五种:VueX
适用于场景:万能
- vuex是什么?
- vuex是官方提供一个插件,状态管理库,集中式管理项目中组件共用的数据.
- 切记:并不是全部项目都需要Vuex,如果项目很小,完全不需要Vuex,如果项目很大,组件很多,数据很多,数据维护很费劲,Vuex存储数据
- state (状态)
- mutations (修改state的唯一手段)
- actions (处理action,可以书写自己的业务逻辑,也可以处理异步)
- getters (理解为计算属性,用于简化仓库数据,让组件获取仓库的数据更加方便)
- modules (实现Vuex仓库模块式开发存储数据)
- Vuex存储数据不是持久化的,只要刷新,数据就会清空
- vuex基本使用
- 如果需要使用vuex中的储存数据,需要在相应组件中引入一个辅助函数(import {mapState} from 'vuex')接着就需要一个计算属性computed
第六种:插槽
插槽:可以实现父子组件通信(通信的结构)
分为:默认插槽、具名插槽、作用域插槽:子组件的数据来源于父组件,子组件是决定不了自身结构与外观
// 我是父组件
<template>
<div>
<h2>效果一: 显示TODO列表时, 已完成的TODO为绿色</h2>
<!-- 子组件:数据来源于父组件 -->
<List :todos="todos">
<!-- 子组件决定不了结构与外观 -->
<template slot-scope="todo">
<!-- 父组件再给子组件传结构 -->
<span :style="{color:todo.todo.isComplete ? 'green' : 'red'}">{{todo.todo.text}}</span>
</template>
</List>
<hr>
<h2>效果二: 显示TODO列表时, 带序号, TODO的颜色为蓝绿搭配</h2>
<list1 :todos="todos">
<template slot-scope="{todo,$index}">
<span :style="{color:todo.isComplete ? 'blue' : 'green'}">{{$index}}-------{{todo.text}}</span>
</template>
</list1>
</div>
</template>
<script type="text/ecmascript-6">
//子组件
import List from './List'
import List1 from './List1'
export default {
name: 'ScopeSlotTest',
data () {
return {
todos: [
{ id: 1, text: 'AAA', isComplete: false },
{ id: 2, text: 'BBB', isComplete: true },
{ id: 3, text: 'CCC', isComplete: false },
{ id: 4, text: 'DDD', isComplete: false },
]
}
},
components: {
List,
List1
}
}
</script>
//我是List子组件
<template>
<ul>
<li v-for="(item,index) in todos" :key="index">
<!-- 数据来源于父亲:但是子组件决定不了结构与外观-->
<!-- 作用域插槽 下面这个可不是props,而是接收到数据之后,数据的回传,回传给父组件-->
<slot :todo="item"></slot>
</li>
</ul>
</template>
// 我是List1子组件
<template>
<ul>
<li v-for="(item,index) in todos" :key="index">
<!-- 数据来源于父亲:但是子组件决定不了结构与外观-->
<!-- 作用域插槽 下面这个可不是props,而是接收到数据之后,数据的回传,回传给父组件-->
<slot :todo="item" :$index="index"></slot>
</li>
</ul>
</template>
第七种: $attrs与$listeners
$attrs与$listeners ----vue-helper 父子组件通信
$attrs:组件实例的属性,可以获取到父亲传递的props数据(前提子组件没有通过props接受)
$listeners:组件实例的属性,可以获取到父亲传递自定义事件(对象形式呈现)
//我是父组件
<div>
<h2>自定义带Hover提示的按钮</h2>
<!-- 当用户在使用我们封装的按钮的时候,需要向HintButton组件传递相应的参数 (el-button二次封装)-->
<HintButton type="success" icon="el-icon-delete" size="mini" title="提示按钮" @click="handler"></HintButton>
</div>
// 我是子组件
<template>
<div>
<!-- 可以巧妙利用a标签实现按钮带有提示功能 -->
<a :title="title">
<!-- 下面这种写法 不能使用:符号进行替换 v-on不能用@符号进行替换 -->
<el-button v-bind="$attrs" v-on="$listeners"></el-button>
</a>
</div>
</template>
<script>
export default {
name: "",
props: ['title'],
mounted () {
// $attrs属于组件的一个属性,可以获取到父组件传递过来的props数据
// 对于子组件而言,父组件给的数据可以利用props接收,但是需要注意,如果子组件通过props接收的属性,在$attr属性当中是获取不到的
console.log(this.$attrs);
// $listeners,它也是组件实例自身的一个属性,他可以获取到父组件给子组件传递自定义事件
console.log(this.$listeners);
}
};
</script>
第八种: $children与$parent
ref可以获取到某一个组件,子组件
$children组件实例的属性,可以获取到当前组件的全部子组件,返回的是一个【数组】
$parent组件实例的属性: 可以获取当当前子组件的父组件,进而可以操作父组件的数据与方法
//我是父组件
<template>
<div>
<h2>BABA有存款: {{ money }}</h2>
<button @click="jieqianFromXM(100)">找小明借钱100</button><br />
<button @click="jieqianFromXH(150)">找小红借钱150</button><br />
<button @click="jieqianFromAll(200)">找所有孩子借钱200</button><br />
<button>我是baba</button>
<br />
<!-- 小明 ref: 获取节点(组件标签) -->
<!-- 在Vue组件当中可以通过ref获取子组件,就可以操作子组件数据与方法 -->
<Son ref="xm" />
<br />
<!-- 小红 -->
<Daughter ref="xh" />
</div>
</template>
<script>
import Son from "./Son";
import Daughter from "./Daughter";
export default {
name: "ChildrenParentTest",
data () {
return {
money: 1000,
};
},
methods: {
// 向儿子小明借100
jieqianFromXM (money) {
// 父组件的钱数进行累加100元
this.money += money
//ref可以获取到真实DOM节点,当然也可以获取子组件标签(操作子组件的数据与方法)
this.$refs.xm.money -= money
},
// 向女儿小红借了150
jieqianFromXH (money) {
this.money += money
this.$refs.xh.money -= money
},
// 向所有孩子借200
jieqianFromAll (money) {
this.money += 2 * money
//组件实例自身拥有一个属性$children,可以获取到组建当中,全部子组件
// console.log(this.$children);
this.$children.forEach(item => {
item.money -= 200
})
// 切记别这样书写:如果子组件过多,第零项可能不是小明
// this.$children[0].money -= 200
// this.$children[1].money -= 200
}
},
components: {
Son,
Daughter,
},
};
</script>
// 我是子组件
<template>
<div style="background: #ccc; height: 50px;">
<h3>儿子小明: 有存款: {{money}}</h3>
<button @click="geiqianFrombaba(50)">给BABA钱: 50</button>
</div>
</template>
<script>
export default {
name: 'Son',
data () {
return {
money: 30000
}
},
methods: {
// 小明给爸爸50
geiqianFrombaba (money) {
// 小明的钱减少50
this.money -= money
// 需要在子组件内部,获取到父组件,让父组件的数据加上50
// 可以通过$parent属性获取某一个组件父组件,可以操作父组件的数据与方法
// console.log(this.$parent);
this.$parent.money += money
}
}
}
</script>
第九种: v-model【组件通信方式一种】
v-model它是一个Vue框架中指令,它主要结合表单元素一起使用(文本框,复选,单选等等)
它主要的作用是收集表单数据,切记:v-model收集checkbox需要用数组收集
v-model:实现原理 :value 与 @input事件实现的,而且还需要注意可以通过v-model实现父子组件数据同步。
//父组件
<div>
<h2>深入v-model</h2>
<input type="text" v-model="msg">
<span>{{msg}}</span>
<br>
<hr>
<!-- 原生DOM当中是有oninput事件,它经常结合表单元素一起使用,当表单元素文本内容发生变化的时候,就会发出一次回调
Vue2:可以通过value与input事件实现v-model功能
-->
<h2>v-model实现原理(vue2)</h2>
<input type="text" :value="msg" @input="msg = $event.target.value">
<span>{{msg}}</span>
<!-- 深入学习v-model:实现父子组件数据同步(实现父子组件通信) -->
<hr>
<!-- :value到底是什么? 这可是props,父子组件通信
@input到底是什么? 并非原生DOM的input事件,属于自定义事件
-->
<CustomInput :value="msg" @input="msg = $event"></CustomInput>
<!-- 简写写法可以 -->
<CustomInput v-model="msg" />
</div>
//子组件
<template>
<div style="background: #ccc; height: 50px;">
<h2>input包装组件----{{value}}</h2>
<!-- :value是动态属性
@input 给原生DOM绑定原生DOM事件
-->
<input type="text" :value="value" @input="$emit('input',$event.target.value)">
</div>
</template>
<script type="text/ecmascript-6">
export default {
name: 'CustomInput',
props: ['value']
}
</script>
第十种:属性修饰符 sync 【组件通信方式一种】
可以实现父子组件数据同步。
:money.sync,代表父组件给子组件传递props['money'],给当前子组件绑定了一个自定义事件(update:money)
以后在elementUI组件中出现,实现父子数据同步。
// 父组件
<div>
小明的爸爸现在有{{ money }}元
<h2>不使用sync修改符</h2>
<!-- :money 是父组件给子组件传递的props
@update:moeny给子组件绑定的自定义事件只不过名字叫做update:money
目前这种操作,其实和v-model很相似,可以实现父子组件数据同步
-->
<child :money="money" @update:money="money = $event" />
<h2>使用sync修改符</h2>
<!--
:money.sync 第一,父组件给子组件传递了props money
第二: 给当前子组件绑定了一个自定义事件,而且事件名称即为update:money
-->
<child2 :money.sync="money" />
<hr />
</div>
// 这个是child子组件
<template>
<div style="background: #ccc; height: 50px;" class="child">
<span>小明每次花100元</span>
<button @click="$emit('update:money',money - 100)">花钱</button>
爸爸还剩 {{money}} 元
</div>
</template>
// 这个是child2子组件
<template>
<div style="background: #ccc; height: 50px;" class="child2">
<span>小明每次花1000元</span>
<button @click="$emit('update:money',money - 1000)">花钱</button>
爸爸还剩{{money}}元
</div>
</template>