Help us understand the problem. What is going on with this article?

VueコンポーネントのfunctionをES6スタイルで書く

はじめに

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

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした