自己总结 ,欢迎讨论指正,如果能帮到你,请点个免费的赞👍
子传父
单向数据流
- 单项数据流是发生在父子组件传值时,父组件通过
prop向子组件传递数据。—— 父级prop的更新会向下流动到子组件中,但是反过来则不行。 - 这样会防止从子组件意外改变父组件的状态。每次父级组件发生更新时,子组件中所有的
prop都将会刷新为最新的值。 - 子组件想修改时,只能通过
$emit触发一个自定义事件,父组件接收到后,由父组件修改。
1. $emit
在子组件上绑定某个事件以及事件触发的函数,在父组件中绑定触发事件,接收子组件传递的数据。
- 子组件
<template>
<div class="app">
<input @click="sendMsg" type="button" value="给父组件传递值">
</div>
</template>
<script>
export default {
data () {
return {
msg: "我是子组件的msg", //将msg传递给父组件
}
},
methods:{
sendMsg(){
//func: 是父组件指定的传数据绑定的函数,this.msg:子组件给父组件传递的数据
this.$emit('func',this.msg)
}
}
}
</script>
- 父组件
<template>
<div class="app">
<child @func="getMsgFormSon"></child>
</div>
</template>
<script>
import child from './child.vue'
export default {
data () {
return {
msgFormSon: "this is msg"
}
},
components:{
child,
},
methods:{
getMsgFormSon(data){
this.msgFormSon = data
}
}
}
</script>
2.$refs
通过$ref的能力,给子组件定义一个ID,父组件通过这个ID可以直接访问子组件里面的方法和属性
- 子组件
<template>
<div>
<Tree :data="treeData" show-checkbox ref="treeData"></Tree>
</div>
</template>
<!-- 定义方法 -->
getData(){
return this.$refs.treeData.getCheckedNodes()
},
- 父组件
<AuthTree ref="authTree"></AuthTree>
<!-- 调用方式 -->
console.log( this.$refs.authTree.getData());
父传子
1. prop
父组件使用prop把数据传给子组件
- 子组件
export default {
name: 'ExGroupSearchTree',
components,
props: {
selectedKeys: {
type: Array,
default: function() {
return []
},
},
isSearch: {
type: Boolean,
default: false,
},
},
data() {
return {},
},
methods: {},
}
- 父组件
<div>
<ex-group-search-tree
:key="key"
:selected-keys="defaultSelectedKeys"
:is-search="true"
@treeSelect="treeSelect"
></ex-group-search-tree>
</div>
2.provide、inject(依赖注入)
向下传递(可注入其子孙的属性),适用于嵌套多层组件时。
注意:当provide提供的是一个对象时,变化后会更新到子组件里,如果是个值不行
- 父组件
父组件发送需要传入的参数
data: function() {
return {}
},
provide() {
return {
isAnony: this.isAnony,
}
},
methods: {},
- 子组件
子组件接收
data() {
return {}
},
inject: {
isAnony: { default: false },
},
created() {}
3.$props、$attrs、v-on="$listeners"
多用于三层组件嵌套,祖孙组件通信时,父组件通过attrs来接收外层组件的参数,通过v-on="$listeners"来监听外层组件的方法,都需要一层一层向下传递。
$props:当前组件接收到的 props 对象。Vue 实例代理了对其 props 对象属性的访问。将属性向下传递
$attrs:包含了父作用域中不作为 prop 被识别 (且获取) 的特性绑定 (class 和 style 除外)。将属性向下传递(例如:没有在props中定义的key,但是在标签中定义)
- 中间组件
<template>
<div>
<oa-ex-unit-picker v-if="isXcex" v-bind="{ ...$props }" v-on="$listeners" />
<oa-group-unit-picker v-else v-bind="{ ...$props }" v-on="$listeners" />
</div>
</template>
<script>
import components from './_import-components/oa-unit-picker-import'
export default {
name: 'OaUnitPicker',
components,
props: {
value: {
type: [Array, String],
default: function() {
return []
},
},
rootNode: {
type: [Array],
default: function() {
return null
},
},
hierarchical: {
type: Boolean,
default: false,
},
},
data() {
return {
isXcex: true, // 是否启用 新公文交换开关
}
},
methods: {},
}
</script>
<style module lang="scss">
@use '@/common/design' as *;
</style>
兄弟组件
1. eventBus
Publisher(发布者)通过post()方法,把Event事件发布出去,Subscriber(订阅者)在onEvent()方法中接收事件
- 初始化——全局定义
全局定义,可以将eventBus绑定到vue实例的原型上,也可以直接绑定到window对象上.
//main.js
//方式一
Vue.prototype.$EventBus = new Vue();
//方式二
window.eventBus = new Vue();
- 触发事件
//使用方式一定义时
this.$EventBus.$emit('eventName', param1,param2,...)
//使用方式二定义时
EventBus.$emit('eventName', param1,param2,...)
- 监听事件
//使用方式一定义时
this.$EventBus.$on('eventName', (param1,param2,...)=>{
//需要执行的代码
})
//使用方式二定义时
EventBus.$on('eventName', (param1,param2,...)=>{
//需要执行的代码
})
- 移除监听事件
为了避免在监听时,事件被反复触发,通常需要在页面销毁时移除事件监听。或者在开发过程中,由于热更新,事件可能会被多次绑定监听,这时也需要移除事件监听。
//使用方式一定义时
this.$EventBus.$off('eventName');
//使用方式二定义时
EventBus.$off('eventName');
2.VueX
vuex 是一个专门为vue.js应用程序开发的状态管理模式。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-MrJqsMCM-1665458801322)(segmentfault.com/img/bVbfPQV…)]
可以理解为在data中的属性,需要共享给其他组件使用的部分,,使用vuex进行统一集中式的管理。
- vuex中,有默认的五种基本的对象:
- state:存储状态(变量)
- getters:对数据获取之前的再次编译,可以理解为state的计算属性。我们在组件中使用 $sotre.getters.fun()
- mutations:修改状态,并且是同步的。在组件中使用$store.commit('',params)。这个和我们组件中的自定义事件类似。
- actions:异步操作。在组件中使用是$store.dispath('')
- modules:store的子模块,为了开发大型项目,方便状态管理而使用的。这里我们就不解释了,用起来和上面的一样。
- 在store.js文件中,引入vuex并且使用vuex
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
const state = {
count: 0
}
export default new Vuex.Store({
state
})
- 在其他组件中就可以使用定义的属性了
<template>
<div class="hello">
<h3>{{$store.state.count}}</h3>
</div>
</template>
- 用mutations和actions操作VueX中的属性
- store.js
/**
* mutations 里面放置的是我们操作state对象属性的方法
*/
const mutations = {
mutationsAddCount(state, n = 0) {
return (state.count += n)
},
mutationsReduceCount(state, n = 0) {
return (state.count -= n)
}
}
export default new Vuex.Store({
state,
mutations
})
- helloWorld.vue
<template>
<div class="hello">
<h3>{{$store.state.count}}</h3>
<div>
<button @click="handleAddClick(10)">增加</button>
<button @click="handleReduceClick(10)">减少</button>
</div>
</div>
</template>
methods: {
handleAddClick(n){
this.$store.commit('mutationsAddCount',n);
},
handleReduceClick(n){
this.$store.commit('mutationsReduceCount',n);
}
}
- actions(异步操作),dispatch
- store.js
const actions = {
actionsAddCount(context, n = 0) {
console.log(context)
return context.commit('mutationsAddCount', n)
},
actionsReduceCount({ commit }, n = 0) {
return commit('mutationsReduceCount', n)
}
}
export default new Vuex.Store({
state,
mutations,
actions
})
- helloWorld.vue
<div>
<button @click="handleActionsAdd(10)">异步增加</button>
<button @click="handleActionsReduce(10)">异步减少</button>
</div>
handleActionsAdd(n){
this.$store.dispatch('actionsAddCount',n)
},
handleActionsReduce(n){
this.$store.dispatch('actionsReduceCount',n)
}
- getters
使用getters来获取我们的state,因为它算是state的一个计算属性
- store.js
const getters = {
getterCount(state, n = 0) {
return (state.count += n)
}
}
export default new Vuex.Store({
state,
mutations,
actions,
getters
})
- helloWorld.vue
<h4>{{count}}</h4>
const getters = {
getterCount(state) {
return (state.count += 10)
}
}
插槽(传值)
让父组件可以向子组件指定位置插入html结构,也是一种组件通信的方式
- 默认插槽
父组件向子组件发送title
- fath文件
<template>
<son :title="电影">
<ul>
<li v-for="(item,index) in films" :key="index">{{ item }}</li>
</ul>
</son>
</template>
<script>
export default{
name:'fath',
components:{ son },
data:{
return {
films:['猫和老鼠','哆唻A梦','樱桃小丸子']
}
}
}
</script>
- son文件
<template>
<div>
<h3>{{ title }}</h3>
<!-- 定义一个插槽(设置一个位置,等着组件的使用者进行填充) -->
<slot>这是一个插槽,当使用插槽时,此文字不展示被填充内容覆盖</slot>
</div>
</template>
<script>
export default{
name:'son',
props:['listData','title']
}
</script>
- 具名插槽
当有多个slot时,为了对应起来,可以给slot起名字,传递的时候对应起来。
-
fath
<div> <son> <div v-slot:content> <span>{{ 给内容区放点东西 }}</span> </div> <!-- vue 2.6版本后语法,在template中可以直接写v-slot:slot1 --> <template slot="footer"> <div> <span>{{ 给底部放点东西 }}</span> </div> </template> </son> </div> -
son
<template> <h2>{{ 这里是头部 }}</h2> <slot name='content'>这里是内容插槽,当使用插槽时,此文字不展示被填充内容覆盖</div> <slot name='footer'>这里是底部插槽,当使用者没有传递具体结构时,此文字会显示</div> </template> -
作用域插槽
数据在子组件中,父组件可以取到并对数据的结构进行调整
子组件的数据可以被外层父组件使用
- fath
<son>
<template scope="scopeData">
<span v-for="item in scopeData.films">{{ item }}</span>
</template>
<template slot-scope="scopeData">
<h3 v-for="item in scopeData.films">{{ item }}</h3>
</template>
<template v-slot:default="scopeData">
<ul>
<li v-for="item in scopeData.films">{{ item }}</li>
</ul>
</template>
</son>
- son
<template>
<div>
<slot :film="film">
</div>
</template>
<script>
export default{
name:'son',
data:{
return {
films:['猫和老鼠','哆唻A梦','樱桃小丸子']
}
}
}
</script>