【Vue】getterとsetter(ゲッターとセッター)とは何か?computedの中のget()とset()の必要性や意味を実例で解説

vue-js-prograshi(プロぐらし)-kv Vue.js
記事内に広告が含まれていることがあります。

Vueでは、computedオブジェクトの中でget()やset()といったメソッドが定義してあることがあります。

ここではget()やset()とは何か?getterとsetter(ゲッターとセッター)とは何か?について実例を踏まえて解説しています。


▼こういうやつの処理内容についてです。

  computed:{
    message:{
      get(){
        return this.msg
      },
      set(newVal){
        this.$emit("changeMsg", newVal)
      }
    }
  }


getterとsetterとは何か?

gertter(ゲッター)とsetter(セッター)とは、あるデータを外部から直接編集できないようにするために使うものです。

gertter(ゲッター)で対象となるデータの値を取得して、別のプロパティに代入します。

代入したプロパティの値が変更されたら、setter(セッター)を使って間接的に元となっているデータの値を書き換えます。


get()やset()とは何か?

Vue.jsにおいては、computedの中に記載されている

  • get()の処理がgertter(ゲッター)
  • set()の処理がsetter(セッター)

となります。


なぜgetterとsetterとが必要なのか?

Vue.jsではcomputedの中にget()とset()が常に必要なわけではありません。

主に必要になるのは、親テンプレートのプロパティを、子テンプレートで呼び出し、そのプロパティの値を子テンプレートの中で変更するときです。

なぜなら、子テンプレートの中では、親テンプレートのデータを直接編集することができないためです。

直接編集すると以下のような警告が発生します。

[Vue warn]: Avoid mutating a prop directly since the value will be overwritten whenever the parent component re-renders. Instead, use a data or computed property based on the prop's value. Prop being mutated: "thPosition"

訳すと以下のようになります。

[警告]:親コンポーネントが再レンダリング(読み込み)されるたびに値が上書きされるため、propを直接変更することは避けてください。

代わりに、propの値に基づいてdataまたはcomputedプロパティを使用してください。: “対象のpropプロパティ名”

この警告を防ぐために、computedの中でgetterとsetterを使って間接的に、親コンポーネントから受け取ったデータを変更するというわけです。


getterとsetterの使い方

computedオブジェクトでget()とset()を使う手順は以下のようになります。

getterとsetterを使うための手順
  1. 親コンポーネントから子コンポーネントにデータを渡す。
  2. 子コンポーネントで親コンポーネントから渡されたデータを受け取る。
  3. getterの設定する。
  4. setterの設定する。
  5. 親コンポーネントにイベントを設置する。
  6. 親コンポーネントにメソッドを設置する。


親コンポーネントから子コンポーネントにデータを渡す

まずは親コンポーネントの中で、子コンポーネントにデータを渡します。

<template>
 <div>
    <子コンポーネント名
     :渡すプロパティ名="渡したいプロパティ名" />
   省略
 <div>
</template>
合わせて読みたい

ここでは、コンポーネントの読み込みなどの説明は割愛します。親コンポーネントの中に、子コンポーネントを登録する方法については下記をご参考ください。

【Vue.js】拡張子.vueの単一ファイルコンポーネントの作成と読み込み方


子コンポーネントで親コンポーネントから渡されたデータを受け取る

続いて親コンポーネントからデータを受け取るために、子コンポーネントでpropsの中に渡されたプロパティを定義します。

<script>
export default {
  props:{
    プロパティ名: 型などの指定
  }
}
</script>
合わせて読みたい

propsで定義したプロパティには、型などを指定する必要があります。

他にも、デフォルトの値や親子間での受け渡し必須にするといったバリデーションを加えることができます。詳細は下記をご参考ください。

【Vue】propsのtypeとは何か?プロパティ設定時の注意点と使い方を実例で解説|type, default, required, validation


getterの設定

続いて、子コンポーネントのcomputedの中で算出プロパティ定義し、get()を使って受け取ったデータを間接的に定義した算出プロパティの値として代入します。

computed: {
    算出プロパティ名: {
      get() {
        return this.親から受け取ったデータのプロパティ名
      }
    }
  }


setterの設定

上記で定義した算出プロパティの値が変更になったときに、そのことを親テンプレートに伝える処理を記述します。

computed: {
    変数名: {
      get() {
        return this.親から受け取ったデータのプロパティ名
      },
      set(newVal) {
        this.$emit('親側で発動させるイベント名', newVal)
      }
    }
  }

set()の中の引数は、computedで指定した変数の値が変更されたときに、渡される変更内容です。

ここではnewValとしていますが、引数名に特に指定はありません。

      set(newVal) {
        this.$emit('親側で発動させるイベント名', newVal);
      }

this.$emit('親側で発動させるイベント名', newVal)は、親コンポーネントのイベントを発動させる処理です。

第1引数で親コンポーネントで発動させるイベント名を指定します。第2引数がイベントに渡すデータです。


親コンポーネントにイベントを設置する

this.$emit('親側で発動させるイベント名', newVal)は、親コンポーネントのイベントを発動させる処理です。

このため、親コンポーネントに対象となるイベントを設定します。

子コンポーネントを呼び出しているタグの中で、v-onディレクティブ(または@)を使ってイベントを設定します。

このときイベント名は子コンポーネントの$emitで設定した内容と合わせます。

<子コンポーネントタグ
  @イベント名="メソッド名"
>
合わせて読みたい

v-onや@とは何?使い方や違いは?という方は、下記をご参考ください。

【Vue.js】v-onや@アットマークの使い方を実例で解説


親コンポーネントにメソッドを設置する

続いて、イベントが発火したときに実行するメソッドを定義します。

  methods:{
    メソッド名(引数){
      this.プロパティ名 = 引数
    }
  }

引数の部分には、$emitイベントの第2引数で渡したデータが入ります。

以上で設定は完了です。


実例

言葉で説明されても難しい部分もあると思うので、実例で解説します。

親コンポーネント「HelloWorld.vue」の中で子コンポーネントとして定義した「UserDetail.vue」に対して、親コンポーネントのdata「xxx」を渡して、子コンポーネントの中で「yyy」として受け取り、子コンポーネントの中でその値を変更できるようにします。

getterとsetterを使うための手順
  1. 親コンポーネントから子コンポーネントにデータを渡す。
  2. 子コンポーネントで親コンポーネントから渡されたデータを受け取る。
  3. getterの設定する。
  4. setterの設定する。
  5. 親コンポーネントにイベントを設置する。
  6. 親コンポーネントにメソッドを設置する。


親コンポーネントから子コンポーネントにデータを渡す

まずは、親コンポーネントから子コンポーネントにデータを渡します。

    <UserDetail 
     :xxx="xxx"
    />

上記の記述により、コンポーネント「UserDetail」にプロパティ名「xxx」の値が、「xxx」というプロパティ名で渡されます。

ここでは渡すデータのプロパティ名とデータの値を同じにしていますが、異なっていても問題ありません。

受け取るときに、「:プロパティ名」の方の名前を使ってください。


親コンポーネントのコード全体像

<template>
  <div class="hello">
    <UserDetail 
     :xxx="xxx"
    />
    <div>xxxの値:{{xxx}}</div>
  </div>
</template>

<script>
import UserDetail from './UserDetail'

export default {
  name: 'HelloWorld',
  components: {
    UserDetail
  },
  data(){
    return{ 
      xxx: "secret"
    }
  }
</script>

<style scoped>
省略
</style>


子コンポーネントで親コンポーネントから渡されたデータを受け取る

次に、子コンポーネントで親コンポーネントから渡されたデータを受け取ります。

子コンポーネントのpropsに親コンポーネントから渡されたデータのプロパティ名「xxx」を記載します。

型は文字列(String)を指定しておきます。

<script>
export default {
  props:{
    xxx: {type: String}
  }
}
</script>
合わせて読みたい


子コンポーネントのコード全体像

<template>
  <div id="user-detail">
    <h1>ユーザーの詳細ページです</h1>
    <ul class="profile-list">

    </ul>
    <p></p>
  </div>
</template>

<script>
export default {
  props:{
    xxx: {type: String}
  }
}
</script>

<style scoped>
省略
</style>


getterの設定する

続いて、子コンポーネントの中のcomputedに算出プロパティ「yyy」を定義して、getterを設定します。

return this.xxxでpropsで定義したプロパティの値を戻すだけの処理です。

<script>
export default {
  props:{
    xxx: {type: String}
  },
  computed:{
    yyy:{
      get(){
        return this.xxx
      }
    }
  }
}
</script>

この状態であれば、子コンポーネントの中で「yyy」というプロパティで「xxx」の値を表示することができます。

ですが、データの変更ができません。


setterの設定する

算出プロパティ「yyy」の値の変更に合わせて、親コンポーネントのプロパティ「xxx」を変更できるようにsetterを定義します。

this.$emit("changeXxx", newVal)で、変更があった場合に、親の「chnageXxx」イベントを発火させ、そのときに変更された値が入った変数「newVal」を渡します。

<script>
export default {
  props:{
    xxx: {type: String}
  },
  computed:{
    yyy:{
      get(){
        return this.xxx
      },
      set(newVal){
        this.$emit("changeXxx", newVal)
      }
    }
  }
}
</script>


親コンポーネントにイベントを設置する

子コンポーネントから送られたイベントが発火するように、親コンポーネントにイベントを設置します。

子コンポーネントを呼び出しているタグに「@changeXxx=”changeXxx”」を記述します。

    <UserDetail 
     :xxx="xxx"
     @changeXxx="changeXxx"
    />

「@changeXxx=”changeXxx”」は「@イベント名=”メソッド名”」の形になっています。

イベント名とメソッド名を一致させる必要はありませんが、getterとsetterを使った処理では、わかりやすいように同じものを使うのが一般的です。


親コンポーネントにメソッドを設置する

子コンポーネントから送られたイベントを受け取れるようになったので、イベントが発火したときに動くメソッドとその処理を、親コンポーネントにメソッドを設置します。

  methods:{
    changeXxx(newVal){
      this.xxx = newVal
    }
  }

送られてきた新しい値を、親コンポーネントのプロパティ「xxx」に代入します。

以上で処理は完了です。


全体のコード

処理を記述したあとの、親コンポーネントと子コンポーネントの記述は以下のようになります。

親コンポーネントの中身

<template>
  <div class="hello">
    <UserDetail 
     :xxx="xxx"
     @changeXxx="changeXxx"
    />
    <div>xxxの値:{{xxx}}</div>
  </div>
</template>

<script>
import UserDetail from './UserDetail'

export default {
  name: 'HelloWorld',
  components: {
    UserDetail
  },
  data(){
    return{ 
      xxx: "secret"
    }
  },
  methods:{
    changeXxx(newVal){
      this.xxx = newVal
    }
  }
}
</script>

<style scoped>
省略
</style>

子コンポーネントを読み込んでいる場所に@changeXxx="changeXxx"というイベントとメソッドを定義しています。

子コンポーネントの$emitでchangeXxxイベントが発火すると指定したメソッドが実行されます。(※ここではイベント名とメソッド名を一致させていますが、異なってても問題ありません)

メソッドは次のようになってます。

  methods:{
    changeXxx(newVal){
      this.xxx = newVal
    }
  }

$eventで送られてきた第2引数のデータを引数として受け取り、プロパティに代入します。



子コンポーネントの中身

<template>
  <div id="user-detail">
    <h1>ユーザーの詳細ページです</h1>
    <ul class="profile-list">
      <li>xxx: <input type="text" v-model="yyy"></li>
    </ul>
    <p></p>
  </div>
</template>

<script>
export default {
  props:{
    xxx: {type: String}
  },
  computed:{
    yyy:{
      get(){
        return this.xxx
      },
      set(newVal){
        this.$emit("changeXxx", newVal)
      }
    }
  }
}
</script>

<style scoped>
省略
</style>

computedでyyyという新しいプロパティを作成しています。

  computed:{
    yyy:{
      get(){
        return this.xxx
      },
      set(newVal){
        this.$emit("changeXxx", newVal)
      }
    }
  }

inputタグのv-modelで双方向バインディングするプロパティもこのcomputedで定義したyyyに変更しています。

<input type="text" v-model="yyy">


ブラウザの表示

デフォルトでは以下のように表示されます。

子コンポーネントの中で表示している「secret」の文字を、「changed」に変えると、親コンポーネントの要素として下部に表示している「xxxの値」の部分も変更され、正しく動作します。



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