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()を使う手順は以下のようになります。
親コンポーネントから子コンポーネントにデータを渡す
まずは親コンポーネントの中で、子コンポーネントにデータを渡します。
<template>
<div>
<子コンポーネント名
:渡すプロパティ名="渡したいプロパティ名" />
省略
<div>
</template>
子コンポーネントで親コンポーネントから渡されたデータを受け取る
続いて親コンポーネントからデータを受け取るために、子コンポーネントでpropsの中に渡されたプロパティを定義します。
<script>
export default {
props:{
プロパティ名: 型などの指定
}
}
</script>
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で設定した内容と合わせます。
<子コンポーネントタグ
@イベント名="メソッド名"
>
親コンポーネントにメソッドを設置する
続いて、イベントが発火したときに実行するメソッドを定義します。
methods:{
メソッド名(引数){
this.プロパティ名 = 引数
}
}
引数の部分には、$emitイベントの第2引数で渡したデータが入ります。
以上で設定は完了です。
実例
言葉で説明されても難しい部分もあると思うので、実例で解説します。
親コンポーネント「HelloWorld.vue」の中で子コンポーネントとして定義した「UserDetail.vue」に対して、親コンポーネントのdata「xxx」を渡して、子コンポーネントの中で「yyy」として受け取り、子コンポーネントの中でその値を変更できるようにします。
親コンポーネントから子コンポーネントにデータを渡す
まずは、親コンポーネントから子コンポーネントにデータを渡します。
<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の値」の部分も変更され、正しく動作します。