babel-plugin-transform-runtimeだとIE11でVuexが使えない

Vueでアプリを開発する際、規模にかかわらず~~イベントバスがめんどい~~拡張性を担保するため、
基本的にVuexをインストールしています。
状態管理で幸せになれる便利なVuexライブラリですが、「IE11でVuex使えない！」というケースがあるので、原因と対策をまとめてみました。

>**webpackを使用していることを前提**としています。
また、検証用に使用した**vue-cli(2.9.6)**でインストールされるbabelが6系のものだったので、
**babel関連のモジュール名も6系のもの**に合わせています。
7系を使っている場合は**babel-XXX**を**@babel/xxx**に読み替えてください。

##IE11のエラー
IE11でVuexが動いていない場合、ブラウザ表示が真っ白になって（何も表示されず）、コンソールで以下のエラーが吐かれるかと思います。

```console
SCRIPT5022: [vuex] vuex requires a Promise polyfill in this browser.
```

どうやら、IE11でvuexを使うにはPromiseのポリフィルが必要なようです。

##結論
いきなり結論ですが、上記のエラーの通り、Promiseのポリフィルを入れることでIE11にVuexを対応させることができます。
が、タイトルの通り```babel-plugin-transform-runtime```だと**Vuexライブラリ内のPromiseが変換されません。**

ちなみに、[**vue-cli(3.x)**ではBabel7およびpreset-envによるポリフィル注入がサポートされている](https://jp.vuejs.org/2018/08/11/vue-cli-3-0-is-here/)ため、デフォルトでIE11でもVuexが動作していることが確認できました。

言い換えると、

- ```vue-cli 3.x```ではなく```vue-cli 2.x```でプロジェクトを作成した
- ES2015+の関数を```babel-polyfill```ではなく```babel-plugin-transform-runtime```でトランスパイルしている
- そもそもES2015+（Promise）のpolyfillをどこにも置いていない

といった場合にIE11でVuexが使えないケースが起こりうるのではないかと思います。

##原因
Vuexライブラリでは**内部的にPromiseが使用**されています。
（actionsがPromiseを返す、とかそのあたりだと思う）

[IE11はPromiseをサポートしていない](https://caniuse.com/#search=Promise)ため、Promiseって知らないからポリフィル入れてね、とエラーが出るわけですね。

###```transform-runtime```だとダメな理由

```transform-runtime ```はグローバルにpolyfillを置くものではありません（グローバル汚染しないなどの利点があります）。
ES2015+の関数があれば、JSファイルごとに必要な箇所を書き換えるよ、という静的解析するものなので、
```node_modules```であるVuexライブラリ内部の```Promise```に関しては変換の対象外となってしまいます。

babelの設定に関しては以下の記事が参考になります。

- [Babelの設定を見直すための逆引きガイド](https://aloerina01.github.io/blog/2018-03-19-1)

##対処と検証
まずは実際にIE11でVuexが使えない環境を再現します。

### 検証用プロジェクトの作成
今回検証のために**vue-cli（2.9.6）**を使ってプロジェクトを作りました。

「[Vue.js を vue-cli を使ってシンプルにはじめてみる](https://qiita.com/567000/items/dde495d6a8ad1c25fa43)」を参考にさせていただきました。

生成された``` .babelrc ```を見ると、pluginsに``` "transform-runtime" ```が使用されているようです。

```js
{
  "presets": [
    ["env", {
      "modules": false,
      "targets": {
        "browsers": ["> 1%", "last 2 versions", "not ie <= 8"]
      }
    }],
    "stage-2"
  ],
  "plugins": ["transform-vue-jsx", "transform-runtime"],
  "env": {
    "test": {
      "presets": ["env", "stage-2"],
      "plugins": ["transform-vue-jsx", "transform-es2015-modules-commonjs", "dynamic-import-node"]
    }
  }
}
```

プロジェクトを作成したら、Vuexをインストールします。

```
npm i -S vuex
```

つぎに、ストアのインスタンスを新規作成します。

```/src/store/index.js
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)

const store = new Vuex.Store({
  state: {
    hoge: 'Storeのhogeです'
  }
})

export default store
```

作成したストアを```/src/main.js```で読み込みます。

```/src/main.js.diff
// The Vue build version to load with the `import` command
// (runtime-only or standalone) has been set in webpack.base.conf with an alias.
import Vue from 'vue'
import App from './App'

+ import store from './store'

Vue.config.productionTip = false

/* eslint-disable no-new */
new Vue({
  el: '#app',
+ store,
  components: { App },
  template: '<App/>'
})

```

プロジェクト作成時に```HelloWorld.vue```が作成されていると思うので、ここにStoreの情報を表示するよう変更します。

```/src/components/HelloWorld.vue.diff
<script>
export default {
  name: 'HelloWorld',
  data () {
    return {
-     msg: 'Welcome to Your Vue.js App'
+     msg: this.$store.state.hoge
    }
  }
}
</script>
```

Vuexが使えていれば、以下のh1タグ内にStoreのhogeが表示されすはずです。

```/src/components/HelloWorld.vue
<template>
  <div class="hello">
    <h1>{{ msg }}</h1> //ここに表示されるはず
```

下記コマンドを叩いて```localhost```で確認します。

```
npm run dev
```

ChromeなどPromiseをサポートしているブラウザでは、問題なく下記のように表示されるかと思います。
![vue-cli-test - http___localhost_8080_.png](https://qiita-image-store.s3.amazonaws.com/0/315402/9d6b41f1-7dbd-2968-4c40-195dbe297b02.png)

IE11は何も表示されず、例のエラーが表示されます。
![vue-cli-test - http___localhost_8080_IE.png](https://qiita-image-store.s3.amazonaws.com/0/315402/1ab781ac-9225-bca5-a0e6-a9f736d69efc.png)

###IE11でVuexを使えるようにする
前置きが長くなりましたが、いよいよ本題です。
以下の2つのやり方のいずれかで対処することができました。

#### 1. es6-promiseをimportする方法

``` es6-promise ```をインストールします。

```
npm i -D es6-promise
```

``` main.js ```にes6-promiseをimportします。
注意点として、storeのimport文より前に記述してください。

```/src/main.js.diff
// The Vue build version to load with the `import` command
// (runtime-only or standalone) has been set in webpack.base.conf with an alias.
import Vue from 'vue'
import App from './App'

+ import 'es6-promise/auto'
import store from './store'

Vue.config.productionTip = false

/* eslint-disable no-new */
new Vue({
  el: '#app',
  store,
  components: { App },
  template: '<App/>'
})
```

#### 2. babel-polyfillをバンドル時に結合する方法

``` babel-polyfill ```をインストールします。

```
npm i -D babel-polyfill
```

``` webpack.base.conf.js ```を修正して、```main.js```をバンドルする際に```babel-polyfill```を結合します。

```/build/webpack.base.conf.js.diff
module.exports = {
  context: path.resolve(__dirname, '../'),
  entry: {
-   app: './src/main.js'
+   app: ['babel-polyfill','./src/main.js']
  },
```

同時に、``` .babelrc ``` の``` "transform-runtime" ```は不要になるはずなので、こちらを削除します。

```diff
{
  "presets": [
    ["env", {
      "modules": false,
      "targets": {
        "browsers": ["> 1%", "last 2 versions", "not ie <= 8"]
      }
    }],
    "stage-2"
  ],
- "plugins": ["transform-vue-jsx", "transform-runtime"],
+ "plugins": ["transform-vue-jsx"],
  "env": {
    "test": {
      "presets": ["env", "stage-2"],
      "plugins": ["transform-vue-jsx", "transform-es2015-modules-commonjs", "dynamic-import-node"]
    }
  }
}
```

### 結果
![vue-cli-test - http___localhost_8080_IEOK.png](https://qiita-image-store.s3.amazonaws.com/0/315402/64bd3f6d-fae2-3395-46a5-f417f74136bb.png)

無事、IE11でもVuexが使用できるようになりました。

## 参考
下記を参考にしました。

- [Babelの設定を見直すための逆引きガイド](https://aloerina01.github.io/blog/2018-03-19-1)
- [Vue.js を vue-cli を使ってシンプルにはじめてみる](https://qiita.com/567000/items/dde495d6a8ad1c25fa43)

[:christmas_tree: FORK Advent Calendar 2018](/advent-calendar/2018/fork)
:arrow_left: 20日目 [git cloneできない時の調べ方と対策 〜AWS CodeCommit/Mac OSの場合〜](https://qiita.com/izanari/items/8094612d7de7b9b172ef) @izanari 
:arrow_right: 22日目 [Apache（php-fpm + Apache 2.4）で何を設定すればいいのかわからなかったので調べてみた](https://qiita.com/Kodak_tmo/items/27505cd42df32012c066) @Kodak_tmo