Vue组件通信

本文详细介绍了Vue中10种组件通信的方法,包括props、自定义事件、全局事件总线$bus、pubsub-js、VuX、插槽、$attrs与$listeners、$children与$parent、v-model以及sync修饰符的使用场景、注意事项和实现原理。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

第一种: props

适用于的场景:父子组件通信

注意事项:

如果父组件给子组件传递数据(函数),本质其实是子组件给父组件传递数据

如果父组件给子组件传递数据(非函数),本质就是父组件给子组件传递数据

书写方式: 3种

  1. ['todos'],
  2. {type:Array},
  3. {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>

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值