はじめに
Vueでは関数定義をES6のアロー関数(Arrow Function)を使わずにES5以前からの書き方のfunction式(function expression)を使う方法が推奨されています。
メソッド(例 plus: () => this.a++) を定義するためにアロー関数を使用すべきではないことに注意してください。
アロー関数は、this が期待する Vue インスタンスではなく、this.a が undefined になるため、親コンテキストに束縛できないことが理由です。
var vm = new Vue({
data: { a: 1 },
methods: {
plus: function () {
this.a++ // this = VueComponent
}
}
})
vm.plus()
vm.a // 2
例えばmethods内部でfunctionを定義した場合、thisに呼び出し元のVueインスタンスが格納されます。
var vm = new Vue({
data: { a: 1 },
methods: {
plus: () => {
this.a++ // this = undefined
}
}
})
vm.plus()
vm.a
一方でアロー関数で定義した場合、thisの内容はundefinedとなります。
そして上記の例でのthis.aの呼び出しはundefinedのプロパティを参照しようとするのでエラーとなってしまうのです。
これは従来のfunction expressionが呼び出し元のコンテキストをthisに格納する挙動に対して、アロー関数ではthisが呼び出し元ではなくレキシカルスコープのthisにバインドされるという違いによるものです。
私も当初アロー関数に書き直そうとして、このthis参照問題に当たってしまいました。
GoogleやQiitaで検索してみるとアロー関数を諦めてfunctionで関数定義しなさいと解説している記事がいくつかヒットするので同じ悩みの人が一定数いるようです。
しかし別の記法でES6スタイルで記述されている方のgistを参考に、functionを撤廃することができたので具体的な方法を紹介します。
要約
ES6からObjectのpropertyにfunctionを定義する場合の短縮構文である、メソッド定義構文(Method Definition)が実装されました。
この記法を使うことでVueのcomputed, methodsなどの内部で定義したfunctionを置き換えていきます。
メソッド定義 - JavaScript | MDN
var obj = {
foo: function() {
/* コード */
},
bar: function() {
/* コード */
}
};
var obj = {
foo() {
/* コード */
},
bar() {
/* コード */
}
};
なぜアロー関数ではなくこの記法にするのかは後述します。
それでは個別の事例ごとにNGパターンを踏まえつつES6スタイルに書き直してみましょう。
props
propsなどの内部のプロパティとして値を返すfunctionが定義されている場合
props: {
colorCodes: {
type: Array,
default: function() {
return ['#FFFFFF', '#F0F0F0'];
}
}
}
これは単純にアロー関数にできそうですね?
props: {
colorCodes: {
type: Array,
default: () => {
return ['#FFFFFF', '#F0F0F0'];
},
},
},
できました!しかし…
functionの内部でthisが参照されている時はどうでしょうか?
props: {
colorCodes: {
type: Array,
default: function() {
console.log(this); // VueComponentが参照できる
return this.colorArray;
},
},
}
####NGな書き方
前述のように単純にアロー関数に変換すると内部にthisが記述された時に参照できなくなってしまいます
props: {
colorCodes: {
type: Array,
default: () => {
console.log(this); // undefined
return this.colorArray; // undefinedのプロパティを参照しようとしてエラーになる
},
},
}
####OKな書き方
メソッド定義形式で書けば内部でthisが参照できます
props: {
colorCodes: {
type: Array,
default() {
console.log(this); // VueComponentが参照できる
return this.colorArray;
},
},
}
computed, methodsなど
computedの内部でfunctionで定義されている場合
computed: {
mySomething: function() {
const something = this.getSomething();
...
},
},
####NGな書き方
単純にアロー関数に書き換えると、内部にthisが記述された時に参照できなくなります
computed: {
mySomething: () => {
const something = this.getSomething(); // undefinedのプロパティを参照しようとしてエラーになる
...
},
},
####OKな書き方
メソッド定義形式で書けば内部でthisが参照できます
computed: {
mySomething() {
const something = this.getSomething(); // thisでVueComponentが参照できる
...
},
},
computedに限らずmethodsなどの中身も同様にメソッド定義形式に書き換えられます
methods: {
getColorCodes: function() {
return {
startColor: this.colorCodes[0],
endColor: this.colorCodes[1],
};
}
},
####OKな書き方
methods: {
getColorCodes() {
return {
startColor: this.colorCodes[0],
endColor: this.colorCodes[1],
};
}
},
実例
Nuxtのサイトのサンプルがメソッド定義形式で書かれていました
Vuex ストア - NuxtJS
export default {
fetch ({ store }) {
store.commit('increment')
},
computed: mapState([
'counter'
]),
methods: {
increment () {
this.$store.commit('increment')
}
}
}
実例2
element-uiのサンプルもメソッド定義形式で書かれていました
Element - The world's most popular Vue UI framework
<el-collapse v-model="activeNames" @change="handleChange">
... 省略 ...
</el-collapse>
<script>
export default {
data() {
return {
activeNames: ['1']
};
},
methods: {
handleChange(val) {
console.log(val);
}
}
}
</script>
ESLint Rule(追記)
ESLintのObject-Shorthandルールでこの記法を強制することができます
object-shorthand - Rules - ESLint - Pluggable JavaScript linter
{
"object-shorthand": ["error", "always"]
}
ルールのページに書かれてますが、オブジェクトのプロパティとしてアロー関数を記述した場合はこのルールを入れても警告が出ないのでそこは注意しましょう
This rule does not flag arrow functions inside of object literals. The following will not warn:
/*eslint object-shorthand: "error"*/
/*eslint-env es6*/
var foo = {
x: (y) => y
};
参考サイト
超素晴らしい解説のgistです。ほぼこれを参考にしています。
Clean up your Vue modules with ES6 Arrow Functions · GitHub
MDN公式のメソッド定義(Method definitions)リファレンス
メソッド定義 - JavaScript | MDN
本記事は個人ブログに投稿したものをベースにリライトしました
https://pisolino.hatenablog.com/entry/2020/01/24/020059