阅读视图

发现新文章,点击刷新页面。

子组件和父组件之间优雅通信---松耦合

1、传统的写法

// 父组件
<!-- 父组件 -->
<template>
  <ChildComponent 
    @tabActiveFn="onTabActiveUpdate"
  />
  <button @click="goOrder"></button>
</template>

<script>
export default {
  methods: {
    refresh() {
      console.log('刷新数据');
    },
    onTabActiveUpdate(newValue) {
      this.tabActive = newValue;
      this.refresh();
    },
     goOrder(){
        this.$router.replace({ name: 'order' });
    },
  }
}
</script>
// 子组件
<!-- 子组件 -->
<template v-for="(item, index) in TAB">
  <span :key="index" :class="[tabActive === item.index ? 'active' : 'tab']" @click="changeTab(item)"> {{ item.name }}</span>
  <button @click="goOrder"></button>
</template>

<script>
export default {
data() {
    return {
      tabActive: 1,
      TAB: [
        { index: 1, name: 'xxx服务' },
        { index: 2, name: 'xxx服务' }
      ],
  },
  methods: {
    changeTab(item) {
      this.$emit('tabActiveFn', item.index);
      this.tabActive = item.index;
    },
     goOrder(){
        this.$router.replace({ name: 'order' });
    },
  }
}
</script>

2、使用语法糖

  • (1)父组件定义一个万能方法onInvoke(action) { this[action]?.(); },根据传入的字符串调用对应方法。
  • (2)父组件通过自定义事件@invokeFn<ChildComponent @invokeFn="onInvoke"/>暴露给子组件。
  • (3)子组件<button @click="$emit('invokeFn', 'goOrder')"></button>,点击时,触发父组件invokeFn事件并传值goOrder
  • (4)父组件收到事件,执行onInvoke('goOrder'),最终安全调用goOrder方法。
// 父组件
<!-- 父组件 -->
<template>
  <ChildComponent 
    @tabActiveFn="onTabActiveUpdate"
    @invokeFn="onInvoke"
  />
</template>

<script>
export default {
  methods: {
    refresh() {
      // 刷新数据
      console.log('刷新数据');
    },
    onTabActiveUpdate(newValue) {
      this.tabActive = newValue;
      this.refresh();
    },
    goOrder(){
        this.$router.replace({ name: 'order' });
    },
    onInvoke(action) {
      this[action]?.();
    },
  }
}
</script>
// 子组件
<!-- 子组件 -->
<template v-for="(item, index) in TAB">
  <span :key="index" :class="[tabActive === item.index ? 'active' : 'tab']" @click="changeTab(item)"> {{ item.name }}</span>
  <button @click="$emit('invokeFn', 'goOrder')"></button>
</template>

<script>
export default {
data() {
    return {
      tabActive: 1,
      TAB: [
        { index: 1, name: 'xxx服务' },
        { index: 2, name: 'xxx服务' }
      ],
  },
  methods: {
    changeTab(item) {
      this.$emit('tabActiveFn', item.index);
      this.tabActive = item.index;
    }
  }
}
</script>

其中,this.[action],使用方括号语法,动态的访问当前对象(this)上名为action变量值的属性或者方法。例如:action是‘goOrder’,它就试图获取this.goOrder

?()是可选链操作符函数的结合,如果this.[action]的值不是null或者undefined,则调用该函数;如果是,则整个表达式短路,返回undefined,而不会抛出错误。

可选链?.是ES2020中引入的一个非常实用的语法。它的核心价值是在于防止在访问嵌套属性或者方法时,因中间某个值为null或者undefined而导致运行时错误

优点:松耦合,子组件完全不需要知道父组件具体有哪些方法,它只需要知道自己需要触发什么指令。父组件则负责接收指令并执行对应的逻辑,这使得子组件的复用性非常高,可以轻松被不同父组件使用,只要这些父组件都实现了相应的指令并监听了对应的事件即可。

❌