【Vue.js】ライフサイクルフックの基本を利用方法と共に解説!

Web

ライフサイクルフックとは?

Vueには、インスタンスの生成から破棄までの間に実行される関数がいくつかあります。この関数をライフサイクルフックといいます。ライフサイクルフックはコンポーネントごとに存在します。

ライフサイクルフックの種類

ライフサイクルフックには次のようなものがあります。

ライフサイクルフック説明
beforeCreateインスタンスの初期化時に実行されます。
プロパティ(dataなど)は生成前のためアクセスできません。
createdインスタンスの初期化後に実行されます。
この時点でプロパティへのアクセスが可能になります。
beforeMountマウント前、つまりMustacheやディレクティブが実行される前に実行されます。
この時点ではDOM要素にはアクセスできません。
mountedマウント後に実行されます。
この時点でDOM要素へのアクセスが可能になります。
beforeUpdateDOM更新前に実行されます。
例えばv-ifで要素を消した場合などに発生します。
updateDOM更新後に実行されます。
beforeDestroyインスタンスの破棄の前に実行されます。
この時点ではプロパティへのアクセスは可能です。
destroyedインスタンスが破棄された後に実行されます。
この時点からプロパティへのアクセスはできません。

この中でよく使われるのはcreated、mounted、beforeDestroyの3つです。

この3つについて後に具体的な利用例を用いて補足していきます。

ライフサイクルフックの定義

ライフサイクルフックは次のように関数の形で定義します。

export default {
  beforeCreate() {

  },
  created() {

  },
  beforeMount() {

  },
  mounted() {

  },
  beforeUpdate() {

  },
  updated() {

  },
  beforeDestroy() {

  },
  destroyed() {

  }
}

ライフサイクルフックの利用例

created

createdの主な役割は、マウントに必要なデータを取得・設定することです。例えば初期表示の段階で外部APIから取得したデータを表示したいときに使用します。

export default {
  data() {
    return {
      value: ''
    }
  },
  created() {
    this.$axios.get("https://hogehoge.com").then(res => {
      this.value = res.data[0].value
    })
  },
}

mountedで取得するケースもありますが、マウント後に実行されるので当然その分遅延します。可能であればcreatedで取得するのがベターです。

mounted

mountedでは、DOM要素へのアクセスが可能になります。初期表示時点でDOM要素を直接操作するような処理はmountedを使用します。

例えば、コンポーネント表示時に特定の入力欄にフォーカスを当てる場合には次のようにします。

<template>
  <div>
    <input type="text" ref="input1">
    <input type="text" ref="input2">
  </div>
</template>

<script>
export default {
  mounted() {
    this.$refs.input2.focus()
  }
}
</script>

beforeDestroy

beforeDestroyは、インスタンス破棄の前にクリアしなければいけない処理がある場合に使用します。

例えば、インスタンス生成から1秒ごとにカウントアップするようなコンポーネントがあったとします。

<template>
  <div>
    <p>{{count}}</p>
  </div>
</template>
<script>
export default {
  data() {
    return {
      count: 0,
      intervalId: undefined
    }
  }
  created() {
    this.intervalId = setInterval(() => {
      console.log(++this.count)
    }, 1000)
  }
}
</script>

実はこの実装では不十分です。仮にこのコンポーネントがv-ifなどで破棄されたとしても、setIntervalで設定した処理は動作し続けます。次の親コンポーネントを作ってコンソールを確認してみてください。

<template>
  <child-component v-if="show" />
  <button @click="show=!show">切替</button>
</template>
<script>
import ChildComponent from 'ChildComponent'

export default {
  components: {
    ChildComponent
  },
  data() {
    return {
      show: true
    }
  }
}
</script>

コンポーネントが破棄されたとしても、再度生成されたとしてもsetIntervalが動作していることがわかるはずです。

このような場合にはbeforeDestroyでクリア処理を行う必要があります。

beforeDestroy() {
  clearInterval(this.intervalId)
}

setInterval以外にも、イベントリスナーを設定する場合にも同じことが言えます。

<template>
  <div>
    <p>{{count}}</p>
  </div>
</template>
<script>
export default {
  data() {
    return {
      count: 0
    }
  },
  methods: {
    countup() {
      console.log(++count)
    }
  },
  created() {
    window.addEventListener("keydown", this.countup)
  },
  beforeDestroy() {
    window.removeEventListener("keydown", this.countup)
  }
}
</script>

nextTick

ライフサイクルフックとは話が離れてしまうのですが、関連するところもあるので説明しておきます。

例えば、次のようにv-if内の要素が表示されたときに入力欄にフォーカスを当てたいとします。

<template>
  <div>
    <div v-if="show">
      <input type="text" ref="input1">
      <input type-"text" ref="input2">
    </div>
    <button @click="swichShow">切替</button>
  </div>
</template>
<script>
export default {
  data() {
    return {
      show: true
    }
  },
  methods: {
    switchShow() {
      this.show = !this.show
      if (this.show) {
        this.$refs.input2.focus()
      }
    }
  }
}
</script>

しかし、これはうまく動作しません。エラーを確認すると、Cannot read property ‘focus’ of undefined(フォーカスなんてプロパティはないよ)と出ています。

なぜこのようなエラーになるのかというと、input2がundefinedだからです。つまり、focusが実行されるタイミングでは、v-if内のDOMがまだ展開されておらず、input2自体参照できないからです。

このような場合に使用するのがnextTickです。

nextTickに設定した処理は、DOM展開後に実行されます。今回の場合だと次のようにすれば、うまくフォーカスされます。

switchShow() {
  this.show = !this.show
  this.$nextTick(() => {
    if (this.show) {
      this.$refs.input2.focus()
    }
  })
}

まとめ

ライフサイクルフックで大事なのは、何がどこまで生成されているかです。これが理解できていなければ、よからぬエラーを生み出してしまうためしっかりと覚えましょう。

 

- Vue.jsのおすすめ書籍はコチラ -

コメント

タイトルとURLをコピーしました