Vue 通过一个案例来复习组件间通信

959 阅读2分钟

这是我参与8月更文挑战的第3天,活动详情查看:8月更文挑战

背景

之前学习过Vue组件间的通信机制,有父子组件间通信props/$emit),中央事件总线 $emit/$on,Vuex状态管理等等。没怎么实践过,可能理解不是特别深。刚好最近做需求的时候用到了 所以通过这个案例来复习,这个用到的是props/$emit的通信机制。

需求描述

表格组件展示数据,操作弹窗用采用抽屉组件,要求把抽屉组件再封装成一个子组件。 本来在同一个组件中,控制抽屉组件打开和关闭的变量的变化可以直接作用,封装成子组件后,就需要把这个状态变量在父子组件之间同步。

实现思路

1. 父组件


<template>
     <div>
      <el-table :data="tableData" row-key="C0" border>
        <el-table-column prop="C0" label="C0" align="center"></el-table-column>
        <el-table-column prop="C1" label="C1" align="center"></el-table-column>
        <el-table-column prop="C2" label="C2" align="center"></el-table-column>
        <el-table-column prop="" label="操作" align="center">
          <template slot-scope="scope">
            <el-button type="primary" @click="handleEdit(scope.row)">
              编辑
            </el-button>
          </template>
        </el-table-column>
      </el-table>
      <editComponent
        v-model="drawerShow"
        :state="activeRow"
        @close="loadData"
      ></editComponent>
     </div>
</template>

父组件通过 state 将当前行的数据传入子组件。

处理 close 事件,执行相应的回调。

这里有一个小技巧,通过v-model双向绑定 drawerShow 属性,用来控制子组件的行为。

可以自定义组件的 v-model

2.2.0+ 新增

一个组件上的 v-model 默认会利用名为 value 的 prop 和名为 input 的事件,但是像单选框、复选框等类型的输入控件可能会将 value attribute 用于不同的目的model 选项可以用来避免这样的冲突:

2. 子组件


<template>
    <div>
      <el-drawer
      :visible.sync="drawerShow"
      custom-class="demo-drawer"
      destroy-on-close
    >
      <el-row slot="title">
        <h1>编辑</h1>
      </el-row>
      <div class="demo-drawer__content">
        <div class="demo-drawer__footer">
          <el-button @click="cancelForm('lecturerEditForm')">取 消</el-button>
          <el-button type="primary" @click="">
            确 定
          </el-button>
        </div>
      </div>
    </el-drawer>
  </div>
</template>

<script>
  export default {
    components: {},
    model: {
      prop: 'show',
      event: 'change',
    },
    props: {
      show: {
        type: Boolean,
        default: false,
      },
      state: {
        type: Object,
        default: null,
      },
    },
    data() {
      return {
      };
    },
    computed: {
      drawerShow: {
        get() {
          return this.show;
        },
        set(value) {
          this.$emit('close', value);
        },
      },
    },
    watch: {
      show(value) {
        if (value) {
          //抽屉打开
        } else {
          //抽屉关闭
        }
      },
    },
    created() {},
    methods: {
      cancelForm() {
        this.drawerShow = false;
      },
    },
  };
</script>

<style lang="scss" scoped>
</style>

如上所述,我们自定义了组件的 v-model,并利用了名为 show 的 prop 和 名为 change 的事件。 从外部传入的控制状态的 prop 是 show , 子组件中用来控制状态的变量 handleClose 是一个基于 show 值计算属性,并且在子组件中设置了监听器来监听show值的变化。

有一点要注意的是,父组件中对于子组件中触发的事件监听器应该要注册在子组件标签上,一开始注册在父组件的外部标签上,没有捕捉到对应的事件。

在子组件中,是通过计算属性 handleClose 来控制 组件的打开和关闭,但是这个计算属性是基于 prop show 的,这样就获取到了外部传进来的信息。

为什么不直接用prop,而要加一层计算属性? 因为prop不是响应式的,我们需要在状态发生变化的时候做出一定操作,所以采用了计算属性和侦听器结合的方法来实现响应式。

这样,在抽屉打开和关闭时,我们可以在侦听器中设置需要进行的操作, 而在子组件中,只要抽屉被关闭,drawerShow被赋予新值,可以主动触发 close 事件。 当父组件接收到 close 事件时(父组件中设置了 close 事件的监听器),执行相应的回调。 这样就可以同步父子组件中的状态。