天天看點

用這招監聽 Vue 的插槽變化

作者:Dmitri Pavlutin

譯者:前端小智

來源:Dmitri Pavlutin

本文 GitHub https://github.com/qq449245884/xiaozhi 已收錄,有一線大廠面試完整考點、資料以及我的系列文章。

最近,每當元件的内容(插槽、子元件等)發生變化時,我需要更新它的狀态。對于上下文,它是一個表單元件,用于跟蹤其輸入的有效性狀态。

下面的代碼片段是以

Options

API格式編寫的,但除了指定的地方外可以在Vue2 和 Vue2中使用。

開始

先從控制表單狀态開始,根據狀态修改一個類,孩子内容使用

<slot/>

填充:

<template>
  <form :class="{ '--invalid': isInvalid }">
    <slot />
  </form>
</template>

<script>
export default {
  data: () => ({
    isInvalid: false,
  }),
};
</script>           

複制

為了更新

isInvalid

屬性,我們需要添加一個觸發的事件,可以使用

sumit

事件 ,但我更喜用

input

事件。

表單事件7個: focus, blur, input, select, change, reset, submit 等,具體詳解看這篇文章:https://blog.csdn.net/qq_4379...

表單不會觸發

input

事件,但我們可以使用 "事件委托"。我們将監聽器附加到父元素(

<form>

)上,當事件發生在它的子元素(

<input>

<select>

<textarea>

等)上時就會被觸發。

任何時候在這個元件的

<slot>

中觸發

input

事件,表單将捕獲該事件。

<template>
  <form :class="{ '--invalid': isInvalid }" @input="validate">
    <slot />
  </form>
</template>

<script>
export default {
  data: () => ({
    isInvalid: false,
  }),
  methods: {
    validate() {
      // 驗證邏輯
    }
  }
};
</script>           

複制

驗證邏輯可以是簡單或複雜的。本文為了示範,用簡單的方法,使用

form.checkValidity()

API 來檢視表單是否基于HTML驗證屬性而有效。

為了通路<form>元素。可以用refs或

<template>
  <form :class="{ '--invalid': isInvalid }" @input="validate">
    <slot />
  </form>
</template>

<script>
export default {
  data: () => ({
    isInvalid: false,
  }),
  methods: {
    validate() {
      this.isInvalid = !this.$el.checkValidity()
    }
  }
};
</script>           

複制

問題

這裡有一點問題。如果表單的内容改變了,會發生什麼?如果一個

<input>

在表單加載被添加到DOM中,會發生什麼?

舉個例子,我們把這個表單元件稱為

"MyForm"

,在 App 中,内容如下:

// App.vue
<template>
  <MyForm>
    <input
      v-model="showInput"
      id="toggle-name"
      name="toggle-name"
      type="checkbox"
    />
    <label for="toggle-name">顯示其它 input</label> 

    <template v-if="showInput">
      <label for="name">Name:</label>
      <input id="name" name="name" required />
    </template>

    <button type="submit">送出</button>
  </MyForm>
</template>

<script>
import Form from "./components/form.vue";
export default {
  name: "App",
  components: {
    MyForm: Form,
  },
  data: () => ({
    showInput: false,
  }),
};
</script>           

複制

App.vue

通過條件來隐藏顯示某些

input

,我們的表單需要知道。在這種情況下,我們會想到在表單内容發生變化時跟蹤其有效性,而不僅僅是在

input

事件或

mounted

生命周期鈎子上。否則,可能會顯示不正确的資訊。

熟悉 Vue的生命周期鈎子小夥伴,這裡可能會想到使用

update

來跟蹤變化。理論上,這聽起來不錯。在實踐中,它會創造一個無限的循環,然後浏覽器挂了。

解決方法

經過一番研究和測試,最佳解決方案是使用

MutationObserver

API。它是浏覽器内置的方法,提供了監視對DOM樹所做更改的能力,如果節點的增減、屬性的變動、文本内容的變動,這個 API 都可以得到通知。

它是原生的方法,是以不受限于架構。

使用時,首先使用

MutationObserver

構造函數,建立一個觀察器執行個體,同時指定這個執行個體的回調函數。在每次 DOM 變動後調用,這個回調都被調用。該回調函數接受兩個參數,第一個是變動數組,第二個是觀察器執行個體,将我們的

form

元件改寫成如下:

<template>
  <form :class="{ '--invalid': isInvalid }" @input="validate">
    <slot />
  </form>
</template>

<script>
export default {
  data: () => ({
    isInvalid: false,
  }),
  mounted() {
    const observer = new MutationObserver(this.validate);
    observer.observe(this.$el, {
      childList: true,
      subtree: true,
    });
    this.observer = observer;
  },
  methods: {
    validate() {
      this.isInvalid = !this.$el.checkValidity();
    },
  },
  beforeUnmount() {
    this.observer.disconnect();
  },
};
</script>


<style scoped>
</style>           

複制

這裡還需要使用

beforeUnmount

生命周期事件來斷開

observer

的連接配接,這會清除它所配置設定的任何記憶體。

最後,我們将

isInvalid

狀态傳遞給要通路的内容的插件槽,這也稱作用域的槽,它非常有用。

<template>
  <form :class="{ '--invalid': isInvalid }" @input="validate">
    <slot v-bind="{ isInvalid }" />
  </form>
</template>

<script>
export default {
  data: () => ({
    isInvalid: false,
  }),
  mounted() {
    const observer = new MutationObserver(this.validate);
    observer.observe(this.$el, {
      childList: true,
      subtree: true,
    });
    this.observer = observer;
  },
  methods: {
    validate() {
      this.isInvalid = !this.$el.checkValidity();
    },
  },
  beforeUnmount() {
    this.observer.disconnect();
  },
};
</script>           

複制

通過這樣的設定,可以在我們的表單元件中添加任意數量的

input

,并添加任何它需要的條件渲染邏輯。隻要

input

使用HTML驗證屬性,表單就會跟蹤它是否處于有效狀态。

此外,由于使用的是作用域槽,我們将表單的狀态提供給父級,是以父級可以對有效性的變化做出反應。

例如,在

App.vue

,我們想在表單無效時 "禁用" 送出按鈕,可以這麼來寫

<template>
  <MyForm>
    <template slot:default="form">
      <label for="name">Name:</label>
      <input id="name" name="name" required>

      <button
        type="submit"
        :class="{ disabled: form.isInvalid }"
      >
        Submit
      </button>
    </template>
  </MyForm>
</template>           

複制

nice~.

希望本文能對你未來的開必有所幫助。

~完,我是小智,準備來場按摩,秀起來!

原文:https://austingil.com/watchin...

代碼部署後可能存在的BUG沒法實時知道,事後為了解決這些BUG,花了大量的時間進行log 調試,這邊順便給大家推薦一個好用的BUG監控工具 Fundebug。

交流

文章每周持續更新,本文 GitHub https://github.com/qq449245884/xiaozhi 已經收錄,整理了很多我的檔案,歡迎Star和完善,大家面試可以參照考點複習