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を置き換えていく。
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)リファレンス
developer.mozilla.org
Nuxt.jsビギナーズガイド―Vue.js ベースのフレームワークによるシングルページアプリケーション開発
- 作者:花谷 拓磨
- 出版社/メーカー: シーアンドアール研究所
- 発売日: 2018/10/17
- メディア: 単行本(ソフトカバー)
Homebrew経由でMySQLをインストールした直後のSocketエラーをなんとかする
brewコマンドで入れるだけで使える状態かと思いきや、自分の環境では色々と手を加える必要があった。他の端末に入れようとした時にまた絶対ハマるはずなのでメモ。
環境:
Mac OS X Lion 10.7.5
Homebrew 0.9.4
MySQL 5.6.10
■まずは普通にhomebrewでMySQLを入れる
以下brewコマンドでのインストール直後からの流れ。
$ mysql error: 'Can't connect to local MySQL server through socket '/tmp/mysql.sock' (2)'
コマンド打つと即エラー。
ソケット指定がおかしい時によく出るやつですね。(2)ってなんだろう。
今回のパターンだと /tmp/mysql.sock のファイル自体が存在していない。
※my.cnfは特にいじっていない状態
■mysql_install_dbコマンドを実行
このコマンド実行後にソケットファイルが自動生成される。
$ mysql_install_db --verbose --user=`root` --basedir="$(brew --prefix mysql)" --datadir=/usr/local/var/mysql --tmpdir=/tmp
■サーバー起動
$ sudo mysql.server start Starting MySQL . ERROR! The server quit without updating PID file (/usr/local/var/mysql/[ホスト名].local.pid).
まだ怒られる。
今回の場合は /usr/local/var/mysql の所有権がないことが原因の模様。
$ sudo chown -R _mysql:_mysql /usr/local/var/mysql