Vue系列:vue组件之间的通讯与插槽

本文介绍了Vue.js中多种组件间通信的方式,包括props的数组和对象形式传递,自定义eventBus实现发布订阅模式,Vuex的状态管理,以及$parent,$children,$root,$refs,provide/inject等特性。此外,还提到了$attrs和$listeners在事件和属性传递中的作用,以及插槽的使用,如具名插槽和作用域插槽。

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

1.props

  • vue 基础父子组件通讯
  • 数组形式 子组件定义props:['title', 'likes'] ,父组件 :title="title" :likes="likes"
  • 对象形式
props:{
 title:{
   type:String, // 类型 String,Number,Boolean,Array,Object,Date,Function,Symbol
   default:"",  // 默认值 为引用类型时,需要为构造函数
   requred:true  // 是否必填
 }
}

注意props 属性忽略大小写,可以使用‘-’来替换驼峰命名


2.eventBus

  • 构造
    • const bus = new Vue()
  • 发送事件
    • bus.$emint("testEvent","hello")
  • 监听事件
    • bus.$on("testEvent",handle)
  • 移除事件
    • bus.$off("testEvent",handle)
  • 实现一个eventBus (其实原理就是发布者订阅者模式)
class EventBus {
  private busMap: Map<string, Array<Function>>;
  constructor() {
    this.busMap = new Map()
  }

  $emit(name: string, arg: any) {
    const list = this.busMap.get(name)
    if (!Array.isArray(list)) {
      return;
    }

    for (const fn of list) {
      try {
        if (typeof fn != "function") {
          throw new Error("")
        }

        fn(arg);
      } catch (error) {
        console.error(error)
      }
    }

  }

  $on(name: string, cb: Function) {
    if (typeof name !== "string" || typeof cb !== "function") {
      throw new Error("参数类型错误")
    }

    let list = this.busMap.get(name)
    if (!list) {
      list = []
      this.busMap.set(name, list)
    }

    list.push(cb)
  }

  $off(name: string, cb: Function) {
    const list = this.busMap.get(name)
    if (!Array.isArray(list)) {
      return
    }

    const index = list.findIndex(fn => cb === fn)
    if (index !== -1) {
      list.splice(index, 1)
    }
  }
}

const bus = new EventBus()

3.Vuex

  • 3.1 state 数据仓库
  const state = {
    data:""
  }
  • 3.2 mutations 同步修改state的值
  const mutations = {
    changeData(state,v){
      state.data = v
    }
  }
  • 3.2 actions 异步修改state的值
  const actions = {
      fetchData({commit},arg){
          /**...**/
          /**异步代码 的回调 start**/
          commit("changeData",arg)
         /**异步代码 的回调 end**/
      }
  }
  • 3.3 getters 计算数据 (可以用来对数据进行过滤和拼接)
  const getters = {
      getData(state){
          return "pre"+state.data 
      }
  }
  • 3.4 modules 模块化
  export default new Vuex.Store({
  modules: {
     dataStore:{
        state ,
        mutations,
        actions,
        getters
     }
  }
})
  • 3.5使用
  import { mapGetters, mapActions, mapMutations } from "vuex";
  export default {
    computed:{
      ...mapGetters({
        data:getters
      })
    },
    methods:{
      ...mapActions([
        "fetchData"
      ]),
      ...mapMutations([
        "changeData"
      ])
    }
  }

4.$parent (可以用来给两个子组件进行通讯,发布订阅模式)

  /**子组件1**/
  this.$parent.$emit("event-from-children1",value)

/**子组件2**/
  this.$parent.$on("event-from-children1",v=>{
    console.log(v)
  })

5.$children (父组件可以直接访问子组件的方法和数据)

this.$children[0].xx() // 访问子组件0的方法 注意存在异步组件时,组件顺序不可知

6.$root (特殊的parent,使用一样 )


7.$refs (可以拿到指定的children,可以访问子组件的实例)

/**标签**/
<from ref="from"/>

/**调用**/
 this.$refs["from"].xx()

8.provide/inject (依赖注入)

/**父组件**/

  provide() {
    return {
      from: this, // 注入自己,子组件通过from 拿自己想拿的数据
    };
  },
/**任意子组件**/

inject: ["from"],
/**or**/
inject:{
  par:{from:"from"}
}

9.$attrs (用于父组件直接给子组件传值,class 和 style 的其他数据会默认转移到子组件的最外层标签之上)

inheritAttrs: false 可以禁止转移到子组件的最外层标签

  /**父组件**/
<Item label="用户名" prop='username'>
/**item 组件**/
console.log(this.$attrs.label)
console.log(this.$attrs.prop)

10.$listeners (子组件不处理的事件,传递给父组件处理,或者说父组件传递给子组件处理函数)

    <div id="app">
      <child1
        :p-child1="child1"
        :p-child2="child2"
        :p-child-attrs="1231"
        v-on:test1="onTest1"
        v-on:test2="onTest2">
      </child1>
    </div>
   <script>
      Vue.component("Child1", {
        inheritAttrs: true,
        props: ["pChild1"],
        template: `
        <div class="child-1">
        <p>in child1:</p>
        <p>props: {{pChild1}}</p>
        <p>$attrs: {{this.$attrs}}</p>
        <hr>
        <child2 v-bind="$attrs" v-on="$listeners"></child2></div>`,
        mounted: function() {
          this.$emit("test1");
        }
      });
      Vue.component("Child2", {
        inheritAttrs: true,
        props: ["pChild2"],
        template: `
        <div class="child-2">
        <p>in child->child2:</p>
        <p>props: {{pChild2}}</p>
        <p>$attrs: {{this.$attrs}}</p>
          <button @click="$emit('test2','按钮点击')">触发事件</button>
        <hr> </div>`,
        mounted: function() {
          this.$emit("test2");
        }
      });
      const app = new Vue({
        el: "#app",
        data: {
          child1: "pChild1的值",
          child2: "pChild2的值"
        },
        methods: {
          onTest1() {
            console.log("test1 running...");
          },
         onTest2(value) {
            console.log("test2 running..." + value);
          }
        }
      });
    </script>

插槽

/**子组件定义插槽**/

/*具名插槽*/
<slot name="header"></slot>

/*匿名插槽*/
<slot></slot>

/*作用域插槽*/
 <slot name="cell" :item="item" :index="index"></slot>
/**父组件使用插槽**/
/*具名插槽*/
<template v-slot:header>...</template>

/*匿名插槽*/
<template>...</template>

作用域插槽
 <template v-slot:cell="{ item, index }">...</template>
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值