$attrs 與 $listeners

本文轉(zhuǎn)自

學會使用attrs 與listeners,二次包裝組件就靠它了

前幾天產(chǎn)品經(jīng)理給我甩過來一份管理系統(tǒng)的設計原型,我打開看了看,雖然內(nèi)心是拒絕的,但是為了活著,還是要做的。小編看了看原型,發(fā)現(xiàn)系統(tǒng)中的大部分彈框右下角都是確定和取消兩個按鈕。如果使用element-ui提供的Dialog,那么每一個彈框都要手動加按鈕,不但代碼量增多,而且后面如果按鈕UI,需求發(fā)生變化,改動量也比較大。

image.png

如果可以將Dialog進行二次封裝,將按鈕封裝到組件內(nèi)部,就可以不用重復去寫了。說干就干。

定義基本彈框代碼

<template>
  <el-dialog :visible.sync="visibleDialog">
    <!--內(nèi)容區(qū)域的默認插槽-->
    <slot></slot>
    <!--使用彈框的footer插槽添加按鈕-->
    <template #footer>
      <!--對外繼續(xù)暴露footer插槽,有個別彈框按鈕需要自定義-->
      <slot name="footer">
        <!--將取消與確定按鈕集成到內(nèi)部-->
        <span>
          <el-button @click="$_handleCancel">取 消</el-button>
          <el-button type="primary" @click="$_handleConfirm">
            確 定
          </el-button>
        </span>
      </slot>
    </template>
  </el-dialog>
</template>
<script>
export default {
  props: {
    // 對外暴露visible屬性,用于顯示隱藏彈框
    visible: {
      type: Boolean,
      default: false
    }
  },
  computed: {
    // 通過計算屬性,對.sync進行轉(zhuǎn)換,外部也可以直接使用visible.sync
    visibleDialog: {
      get() {
        return this.visible;
      },
      set(val) {
        this.$emit("update:visible",val);
      }
    }
  },
  methods: {
    // 對外拋出cancel事件
    $_handleCancel() {
      this.$emit("cancel");
    },
    // 對外拋出 confirm事件
    $_handleConfirm() {
      this.$emit("confirm");
    }
  }
};
</script>

通過上面的代碼,我們已經(jīng)將按鈕封裝到組件內(nèi)部了,效果如下圖所示:

<!--外部使用方式 confirm cancel 是自定義的事件 opened是包裝el-dialog的事件,通過$listeners傳入到el-dialog里面-->
<custom-dialog :visible.sync="visibleDialog" @opened="$_handleOpened" @confirm="$_handleConfirm" @cancel="$_handleCancel">這是一段內(nèi)容</custom-dialog>
image.png

但上面的代碼存在一個問題,無法將Dialog自身的屬性和事件暴露到外部(雖然可以通過props及emit一個一個添加,但是很麻煩),這時候就可以使用attrs與$listeners


使用attrs與listeners

attrs: 當組件在調(diào)用時傳入的屬性沒有在props里面定義時,傳入的屬性將被綁定到attrs屬性內(nèi)(class與style除外,他們會掛載到組件最外層元素上)。并可通過v-bind="$attrs"傳入到內(nèi)部組件中

listeners: 當組件被調(diào)用時,外部監(jiān)聽的這個組件的所有事件都可以通過listeners獲取到。并可通過v-on="$listeners"傳入到內(nèi)部組件中。

修改彈框代碼

<!---使用了v-bind與v-on監(jiān)聽屬性與事件-->
<template>
    <el-dialog :visible.sync="visibleDialog" v-bind="$attrs" v-on="$listeners">
    <!--其他代碼不變-->
    </el-dialog>
</template>
<script>
  export default {
    //默認情況下父作用域的不被認作 props 的 attribute 綁定 (attribute bindings) 
    //將會“回退”且作為普通的 HTML attribute 應用在子組件的根元素上。
    //通過設置 inheritAttrs 到 false,這些默認行為將會被去掉
    inheritAttrs: false
 }
</script>

<!---外部使用方式-->
<custom-dialog
  :visible.sync="visibleDialog"
  title="測試彈框"
  @opened="$_handleOpened"
>
  這是一段內(nèi)容
</custom-dialog>

對于attrs,我們也可以使用props來代替,實現(xiàn)代碼如下

<template>
  <el-dialog :visible.sync="visibleDialog" v-bind="$props" v-on="$listeners">
    <!--其他代碼不變-->
  </el-dialog>
</template>
<script>
import { Dialog } from 'element-ui'
export default {
  props: {
    // 將Dialog的props通過擴展運算符展開到props屬性里面
    ...Dialog.props
  }
}
</script>

但上面的代碼存在一定的缺陷,有些組件存在非props的屬性,比如對于一些封裝的表單組件,我們可能需要給組件傳入原生屬性,但實際原生屬性并沒有在組件的props上面定義,這時候,如果通過上面的方式去包裝組件,那么這些原生組件將無法傳遞到內(nèi)部組件里面(感謝@陌上兮月的提醒)

?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容