玩转vue组件,各种高阶使用方法,再也不用担心组件封装与通信了。

267 阅读1分钟

组件传参和通讯的几种方式

props & 自定义事件

在组件上定义属性传递给子组件,子组件使用props进行数据获取并使用 使用 @ 或者 v-on 指令,传入事件,子组件可通过$emit向父组件通讯

  <HelloWorld msg="hello world" @onClick="handClick"/>
  methods: {
    // 自定义事件
    handClick(params) {
      console.log(params)
    }
  }
  // HelloWorld
  <template>
    <div class="hello" @click="childClick">
      {{ msg }}
    </div>
  </template>

  <script>
  export default {
    name: "HelloWorld",
    props: {
      msg: String
    },
    methods: {
      childClick() {
        this.$emit("onClick", { desc: "子组件向父组件传值" })
      }
    }
  }
  </script>

全局自定义事件$bus & new Vue 实例

实现独立的事件通讯机制,可以自己手动实现,也可重新new一个Vue实例,使用它本身的事件通讯或者直接在全局上绑定vue实例进行通讯。

  // Bus:事件派发、监听和回调管理
  class Bus {
    constructor() {
      this.callbacks = {};
    }
    $on(name, fn) {
      this.callbacks[name] = this.callbacks[name] || [];
      this.callbacks[name].push(fn);
    }
    $emit(name, args) {
      if (this.callbacks[name]) {
        this.callbacks[name].forEach((cb) => cb(args));
      }
    }
  }
  // main.js
  Vue.prototype.$bus = new Bus();

  // child1
  this.$bus.$on("customEvent", (params) => {
    console.log(params)
  })
  // child2
  this.$bus.$emit("customEvent", { desc: "手动实现一个自定义事件" })
  
  window.vm = new Vue({
    render: (h) => h(App),
  }).$mount("#app");
  
  vm.$emit vm.$on // 全局通信 跨组件层级,自己重新new Vue 进行导出导入使用也是同样效果

兄弟组件通讯之祖辈搭桥 $parent || $root

在组件实例中,可以通过$parent访问父组件或者通过$root访问根组件,也可递归查找当前父组件的父组件,直至根组件,在查找过程中可以根据每个组件的唯一性标识找到自己想要的组件进行操作

  // brother1
  this.$parent.$on("parentEvent", (params) => {
    console.log(params)
    // 在此可以对当前组件下的某个子组件进行操作,或者直接操作当前组件的data响应式数据
  })
  // brother2
  this.$parent.$emit("parentEvent", { desc: "通过父组件进行兄弟组件之间的通讯" })
  
  // 递归查找指定组件
  let $parent = this.$parent
  
  while ($parent) {
    if ($parent.xx === xx) break; // 找到指定组件跳出循环
    $parent = $parent.$parent // 父父组件向上查找
  }
  console.log($parent); // 你找到的正确父组件

父组件访问子组件 $refs $children

获取子组件可通过refs 和 children,children也可递归遍历找到指定子组件,但是children是一个数组,且不保证排序顺序,所以需要知道children的唯一性,如果有多个子组件时需要自己判断

  // refs 获取⼦节点引⽤
  <HelloWorld ref="hwRef"/>
  this.$refs.hwRef; // 访问子组件实例,可以操作子组件中的函数、数据、等等在实例上有的任何属性。
  
  <div>
    <HelloWorld/>
  </div>
  
  this.$children[0]; // 当前组件中只有一个子组件,所以使用下标0即可获取 HelloWorld组件实例,从而进行一系列操作
  

组件属性&事件集合传递和获取 $attrs $listeners

通常在高阶组件中,可能会有多个属性传递,此时要传递多个属性或自定义事件到子组件中,一个个写十分不方便。且组件中可能有属性未接受,需要留待接口给外部(这种情况通常发生在多个组件封装成一个集合类组件中)

  <p>{{$attrs.foo}}</p> // 子组件中使用 $attrs 可以访问未被prop声明的任意属性
  
  <HelloWorld v-bind="$attrs"/> // 将父组件使用当前组件时传入的所有属性传递给子组件,与react中 { ...props } 相同
  
  <HelloWorld v-on="$listeners"/> // 将父组件使用当前组件时传入的所有事件传递给子组件 => prop has @event1 @event2 => v-on="$listeners" 会全部展开传递给子组件

组件跨层级通讯 provide inject

祖先和后代之间传参,祖先元素使用provide暴露给后代需要的值,后代使用inject去捕捉该值,然后就可以直接使用 ,类比react中的context。

  // 祖先
  export default {
    name: 'App',
    components: {
      HelloWorld
    },
    provide() {
      return {
        baseAddress: "四川",
        props: this.$data; // 如果传递的值需要响应式更新的话,那么需要传递的就是当前组件的响应式数据
      }
    },
    data() {
      return {
        name: "chen",
        age: 25
      }
    },
    mounted() {
      setTimeout(() => {
        this.age = 30
      }, 2000)
    }
  }
  
  // 后代
  <template>
    <div class="hello">
      {{ baseAddress }}
      {{ props.age }}
    </div>
  </template>

  <script>
  export default {
    name: "HelloWorld",
    inject:["props", "baseAddress"]
  }
  </script>

组件封装最常用之 slot

slot 插槽 vue 2.6 +,插槽是组件封装中常见的,其中有具名和匿名,有传值和不传值。

  <div>
    <slot name="header" v-bind:params="{userName:'chen'}"></slot> // 具名插槽 传值
    <slot></slot> // 匿名插槽 如果省略name name会自动赋值为default 在使用时可以 v-slot="default"
  </div> 
  
  // 组件调用 使用时根元素尽量为template 进行v-slot绑定
  <DivComponent>
    <template v-slot:header="slotProps">
      <h1>这是具名插槽传递的内容 {{ slotProps.userName }}</h1>
    </template>
    <template v-slot:default>
      <h1>这是匿名插槽</h1>
    </template>
  </DivComponent>

结语

以上篇幅简述了vue中组件的各种应用场景以及操作,可以帮助我们更好的完成功能实现,组件封装,组件扩展。如需更细节的配置请前往官网找到你感兴趣的某个配置进行查看。