1.父子组件之间的通信

(1)父给子传参

props

父组件内,在子组件标签里边使用v-bind(简写为 : )绑定属性,在子组件内使用props接收数据.

  <Child :count="count" :userInfo="userInfo" :changeCount="changeCount"></Child>

props接收数据有三种形式

数组形式

props: ['count', 'userInfo', 'changeCount'],

对象形式

props: { ​ count: Number, ​ userInfo: Object, ​ changeCount: Function },

配置对象形式

props: 
{ ​ 
count: { ​ type: Number, 
​          required: true, // 必填 ​
          default: 4 // 默认值 
          ​// required 和 default 是互斥的,如果必填还设置默认值没有意义 ​ }, 
​          userInfo: { ​ type: Object, ​ default() { ​ return { ​ name: '老六', ​ age: 22 ​ } ​ } ​ 
       }, ​       
          changeCount: { ​ type: Function, ​ required: true ​ } 
  },

props传参分为函数类型和非函数类型

非函数类型

  • 基本数据类型(数组形式): 子组件不能修改父组件传过来的数据

  • 引用数据类型(对象形式): 子组件不能修改父组件传过来的引用数据类型地址,可以改里面的属性(但是不推荐,违反了单项数据流)

函数数据类型

  • 传函数的目的是为了子组件调用修改父组件的数据

(2)子给父传参

自定义事件

( @change----->$emit )

父组件 ( 使用v-on( 简写为@ ) 绑定事件 )

<Event1 :count="count" @changeCount="changeCount"></Event1>
        
<Event1 :count="count" @changeCount="changeCount($event)"></Event1>

子组件 ( 使用$emit触发父组件绑定的事件 )

<button @click="$emit('changeCount')">修改父组件的count</button>
​
<button @click="$emit('changeCount', 9)">修改父组件的count为9</button>

  1. 事件类型 - changeCount - 自定义事件是给组件绑定的,自己定义的,名字自己取,有无限个数

  2. 触发机制 - 自己触发的,参数需要自己传递,没有事件对象,使用$emit触发

$emit('changeCount', 9)

参数一:触发的事件类型 参数二: 传递的参数

自定义事件关于$event

函数不加小括号 - 触发的函数中的参数直接是 $emit 传过来的数据

函数加小括号 - 自定义事件是没有事件对象的,$event变成了子组件传过来的参数

原生事件

        <button @click="changeCount">修改count</button>
        <button @click="changeCount(8)">修改count为8</button>
        <button @click="changeCount(16, $event)">修改count为16</button>
原生事件
1. 事件类型 - click - 在原生事件中,事件类型一定是有限个数
2. 触发机制 - 浏览器触发的,触发的时候会给函数传一个事件对象,事件对象是浏览器生成的
原生事件关于$event
函数不加小括号 - 默认系统触发原生事件的时候,第一个参数是 事件对象
函数加小括号 - 加小括号的时候,如果需要事件对象必须使用 $event (底层上是当前这个函数外部套了一层函数,套的这个函数形参是$event,我们用的就是这个形参)

2.跨组件通信

(1)全局事件总线

  1. 安装总线

new Vue({

beforeCreate() {

Vue.prototype.$bus = this; // 安装总线

},

}
  1. 接收数据

在接收数据的组件中,绑定事件,留下回调,接收参数

Child2组件 - 接数据

mounted() {

this.$bus.$on('receiveParams', this.receiveParams)

},

methods: {

receiveParams(params) {

console.log('接收到的参数', params);

}

}
  1. 发送数据

在发送数据的组件中,触发事件,传递参数

<button @click="$bus.$emit('receiveParams', 25)">传给Child2参数</button>

3.父子组件间的数据同步

(1) v-model

父组件的子组件标签中绑定

<CustomInput v-model="keyword">   </CustomInput>
-----------------------------------------------------------------------------------------------
 data() {
        return {
            keyword: "abc",
        };
    },

在子组件中绑定value和input事件,留下回调触发回调

     <input type="text" :value="value" @input="changeValue">
----------------------------------------------------------------------------------------------
     props: ['value'],
     methods: {
        changeValue(e) {
            this.$emit('input', e.target.value);
        }
     }

条件: 必须实现

1. 绑定:value值

2.绑定@input事件

数据的流向:

\1. 通过:value把数据绑定给子组件

\2. 子组件中使用props接收父组件传过来的数据

\3. 修改数据,使用$emit触发自定义事件,把参数传递给父组件

\4. 父组件接收到数据之后,更新keyword的值

\5. keyword值更新之后,又重新通过:value传递给子组件

什么情况下会使用v-model?

封装组件的时候,组件中有表单元素的时候一般会使用v-model

多数用在表单元素上,双向数据绑定,用来收集表达数据

在哪见过?

  • 在element ui 中的 el-input组件使用的就是 v-model

(2).sync

父组件中的子组件标签里使用( :msg(属性名).sync='' )绑定数据

   <Child1 :msg.sync="string"></Child1>
----------------------------------------------------------------------------------------------------------
    data() {
        return {
            string: "我爱你",
        };
    },

子组件使用原生事件绑定一个方法,方法内使用( this.$emit('update:msg', "666") )来触发 从而实现父子组件数据同步

  <button @click="changeParentMsg">修改父组件传过来的数据</button>
-----------------------------------------------------------------------------------------------------------
      props: ['msg'],
              methods: {
      changeParentMsg() {
            this.$emit('update:msg', "666")
        }
    }

条件: 必须实现 1. :msg(属性名) 2. @update:msg

  • :msg="msg" 给子组件绑定数据,用于展示

  • @update:msg="changeMsg" 绑定自定义事件

  • 这里 必须使用 @update:msg 冒号后面跟绑定的数据名称

  • 这里的msg是v-bind绑定的属性

在哪见过?

  • 在element UI中的对话框dialog中见过

使用场景(主要是却别v-model)

  • v-model 在封装表单元素的时候实现父子组件数据同步,经常使用v-model

  • .sync 在封装非表单元素的组件时候实现父子组件数据同步,经常使用.sync

数据的流向:

  1. 通过:msg把数据传给子组件

  2. 子组件修改数据,$emit 触发了事件把参数传给父组件,父组件修改数据

  3. 父组件更改msg,再通过 :msg 传给子组件

4.$attrs与$listeners

  • $attrs 可以接收到绑定在组件上的所有属性,除去props接收过的属性、style、class类名

  • $listeners 可以接收到所有绑定在组件上的事件

  
  <el-tooltip :effect="effect" :content="content" placement="top">
        <el-button v-bind="$attrs" v-on="$listeners">{{ content }}</el-button>
    </el-tooltip>

对element ui 组件进行二次封装的时候,把element里面的每个属性往外一个一个暴露,很累

$attrs

$attrs 可以接收到绑定在组件上的所有属性,除去props接收过的属性、style、class类名

在使用的时候使用 v-bind="$attrs" 直接将这个对象绑定到组件上即可

注意: v-bind不能写成 :

$listeners

$listeners 可以接收到所有绑定在组件上的事件

在使用的时候使用 v-on="$listeners" 直接将这个对象绑定到组件上即可

注意: v-on 不能简写成 @

5.$refs & $children & $parent

$refs 可以获取到组件实例,拿到组件实例可以拿到组件中的数据,在标签上使用的时候获取到的是标签

$children 当前组件的所有子组件,得到的是一个数组

$parent 获取当前组件的父组件

只有可以获取到当前的组件实例,就可以获取到数据,修改数据

注意: 这里不推荐使用 $parent, 为什么?

因为父组件不确定是谁

6.mixin

mixin 是混入

组件中有公共提取的时候就需要用到mixin

使用:

首先创建一个mixin.js的文件,

暴露一个对象,这个对象就是vue的配置对象( Vue组件中能配置什么,这里就能配置什么 )

mixin.js 文件
--------------------------------------------------------------------------------------------------------
export default {
    data() {
        return {
        };
    },
​
    computed: {  
    },
​
    mounted() {
        console.log("mixin的挂载");
    },
​
    methods: {
    },
};

然后在需要混入的组件当中 引入并配置 就OK了

     import Mixin from './mixin'
     export default {
         mixins: [Mixin], // 混入的配置项
     }

  • 在mixin中配置的内容(内容包括但不限于数据,方法等),都可以混入到组件当中

  • 如果在组件中有重复的内容,会发生覆盖,组件中的内容会覆盖混入的内容

注意:

钩子函数不会被覆盖,先执行mixin中的钩子,再执行组件中的钩子

7.用户祖先组件和后代组件之间进行通信

(1)Provide、Inject

  • 使用方式类似于props

provide 理解成广播

inject 就是用来接数据的

注意: 广播只广播一次,当数据发生改变的时候,后代组件当中接收不到更新的信息(包含基本数据类型和引用数据类型的地址)

在使用引用数据类型的时候,对象中的属性发生改变,后代组件是可以接收到的

祖先组件-----------------------------------------------------------------------------------------------------------------------------------------------------------------------

<template>
    <div class="box">
        <h1>祖先组件</h1>
        <div>content1: {{ content1 }}</div>
        <div>content2.name: {{ content2.name }}</div>
        <button @click="changeContent1">修改content1</button>
        <button @click="changeContent2">修改content2</button>
        <Child></Child>
    </div>
</template>
<script>
import Child from "./Child";
export default {
    name: "ProvideInjectTest",
    components: {
        Child,
    },
    provide() {
        return {
            content1: this.content1,
            content2: this.content2,
            changeContent1: this.changeContent1,
            changeContent2: this.changeContent2
        }
    },
    data() {
        return {
            content1: "jack",
            content2: {
                name: "tom",
            },
        };
    },
    methods: {
        changeContent1() {
            this.content1 = 'jerry';
        },
        changeContent2() {
            // this.content2 = { // 无法修改
            //     name: 'tony'
            // }
            this.content2.name = 'tony'; // 可以修改
        }
    },
};
</script>
<style>
</style>

子组件

<template>
    <div class="box">
        <h2>子组件</h2>
        <br>
        <GrandChild />
    </div>
</template>
<script>
import GrandChild from './GrandChild'
export default {
    name: 'Child',
    components: {
        GrandChild
    }
}
</script>
<style>
</style>

孙组件---------------------------------------------------------------------------------------------------------------------------------------------------------------------------

<template>
    <div class="box">
        <h4>孙组件</h4>
        <div>祖先传过来的content1: {{ content1 }}</div>
        <div>祖先传过来的content2.name {{ content2.name }}</div>
        <button @click="changeContent1">调用祖先传过来的方法修改content1</button>
        <button @click="changeContent2">调用祖先传过来的方法修改content2</button>
    </div>
</template>
​
<script>
export default {
    name: "GrandChild",
    inject: ['content1', 'content2', 'changeContent1', 'changeContent2']
};
</script>

8.Vuex

使用场景: 多个组件依赖同一数据的时候使用 vuex,

例如说: 一般情况下用户信息会放在store中 (因为权限问题)

vuex使用步骤

  1. 安装 npm i vuex@3 -S

  2. 引入暴露

创建 store.js 文件

import Vue from "vue";
import Vuex from 'vuex';
Vue.use(Vuex);
​
export default new Vuex.Store({ 
    state,
    mutations,
    actions,
    getters,
    modules //模块化
})
  1. 创建vm关联store

    在 main.js 文件中

    //暴露
    import store from '@/store'
    ​
    new Vue({
        render: h => h(App),
        store  //关联
    }).$mount('#app')

模块化

配置 modules 只要开启模块化,state中的数据就是加了一层,而mutations、actions、getters和之前的使用方式一样

state
     $store.state.test.count  //普通写法  
​
    
     ...mapState({     // 辅助函数 - 往computed中映射
         count: state => state.test.count
     })
​
actions
     $store.dispath('increment')   //普通写法   
         //actions怎么写,mutaions就怎么写,只改一个地方dispath改成commit即可  
    
     ...mapActions(['increment'])   // 辅助函数 - 往methods中映射
getters
     $store.getters.dblCount  //普通写法    
     //辅助函数 - 往compouted中映射
     ...mapGetters(['dblCount'])
​
//命名空间
在store当中配置 namespaced: true   //加了命名空间,每一个模块都是独立的
state和开启模块化的时候一样,mutations、actions、getters都多加了一层
state - 还是开启模块化的写法
actions
     $store.dispatch('test/increment')  //普通写法    
 
     ...mapActions('test', ['increment'])       //辅助函数
getters
     $store.getters['test/dblCount']   //普通写法    
​
     ...mapGetters('test', ['dblCount'])  // 辅助函数
 

9.slot

作用: 父子组件之间的通信,父给子传 html 和 css

插槽有三种

普通插槽、具名插槽、作用域插槽

父组件--------------------------------------------------------------------------------------------------------------------------------------------------------------------------

<template>
    <div class="box">
        <h1>组件间通信10: 插槽==> 作用域插槽</h1>
​
        <h2>需求: 自定义TODO列表组件能实现下面需求</h2>
​
        <h2>效果一: 显示TODO列表时, 已完成的TODO为绿色</h2>
        <!--
            要求有Header Footer 中间列表内容
        -->
        <List :todos="todos">
            <!--  v-slot:default 可以省略,是默认的普通插槽 -->
            <template v-slot:default>
                父组件传的Header
            </template>
            <!-- # 是v-slot指令的简写 -->
            <template #footer>
                父组件传的Footer
            </template>
            <!--
                data是一个对象,这个对象中放的数据是子组件绑定在slot标签上的属性组成的对象
            -->
            <template v-slot:content="data">
                <div :style="{ background: data.row.isComplete ? 'green' : '' }">
                    内容: {{ data.row.text }} - 
                    是否完成: {{ data.row.isComplete ? '完成' : '未完成' }} - 
                    msg: {{ data.msg }}
                </div>
            </template>
        </List>
​
        <h2>效果二: 显示TODO列表时, 带序号, TODO的颜色为蓝绿搭配</h2>
        <List :todos="todos">
            <template v-slot:content="{ row, $index }">
                <div
                    :style="{
                        background: $index % 2 == 0 ? 'skyblue' : 'yellowgreen'
                    }"
                >序号: {{ $index }} - 内容: {{ row.text }}</div>
            </template>
        </List>
    </div>
</template>
​
<script>
​
import List from './List'
export default {
    name: 'SlotTest',
    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
    }
}
</script>

子组件---------------------------------------------------------------------------------------------------------------------------------------------------------------------------

<template>
    <div class="box">
        <slot>header</slot>
        <div>--------------------------------</div>
        <ul>
            <li v-for="(todo, index) in todos" :key="todo.id">
                <slot name="content" :row="todo" :$index="index" msg="hello">内容</slot>
            </li>
        </ul>
        <div>--------------------------------</div>
        <slot name="footer">footer</slot>
    </div>
</template>
​
<script>
export default {
    name: "List",
    props: ['todos']
};
</script>

10.Pubsub

  1. 安装

    npm i pubsub-js

  2. 接收数据,接收数据的组件,绑定事件(订阅消息),留下回调,接收参数

    Pubsub.subscribe('changeMessage', this.changeMessage)

  3. 传递出局,传递数据的组件,触发事件(发布消息),传递参数

    Pubsub.publish('changeMessage')

这里和总线唯一的区别在接收数据的函数中,参数一是消息类型

父组件------------------------------------------------------------------------------------------------------------------------------------------------------------------------

<template>
    <div class="box">
        <h1>pubsub - 父组件</h1>
        <br>
        
        <Child1></Child1>
        <br>
​
        <Child2></Child2>
    </div>
</template>
​
<script>
import Child1 from './Child1.vue'
import Child2 from './Child2.vue'
export default {
    name: "pubsub",
    components: { Child1, Child2 }
}
</script>
​
<style scoped>
​
</style>

子组件1------------------------------------------------------------------------------------------------------------------------------------------------------------------------

<template>
    <div class="box">
        <h1>Child1 - 发数据</h1>
        <button @click="clickHandler">发送数据</button>
    </div>
</template>
​
<script>
import Pubsub from 'pubsub-js'
export default {
    name: "Child1",
    methods: {
        clickHandler() {
            Pubsub.publish('changeMessage', '我爱你')
        }
    }
}
</script>
​
<style scoped>
​
</style>

子组件2------------------------------------------------------------------------------------------------------------------------------------------------------------------------

<template>
    <div class="box">
        <h1>Child2 - 接数据</h1>
​
    </div>
</template>
​
<script>
import Pubsub from 'pubsub-js'
export default {
    name: "Child2",
    mounted() {
        Pubsub.subscribe('changeMessage', this.changeMessage)
    },
    methods: {
        changeMessage(type, text) {
            console.log(type, text)
        }
    }
}
</script>
​
<style scoped>
​
</style>

Logo

前往低代码交流专区

更多推荐