Webpack4 と Typescript で Vuejs の環境を作る

vue-cli を利用して作った環境を webpack4 と Typescript で動くようにしてから Vue.js の勉強をしてみようと思い立ってから 2時間くらい Vue.js と関係ないとこで費やして疲れたメモです。

# Versions
webpack: 4.5.0
typescript: 2.8.1
vue-cli: 2.9.3

# vue-cli を使って環境を構築する

```bash
vue init webpack myapp
cd myapp
```
色々聞かれますが推奨を選びました。
確認のため移動して動かしてみます。

```bash
npm run dev
```
localhost:8080 でサーバが立ち上がり、確認できました。
正常な状態が確認できたので停止して Typescript の環境を用意します。

# Typescriptのインストール

```bash
npm i -D typescript ts-loader vue-class-component vue-property-decorator
```
ローダーと型定義ファイルも合わせて入れます。

```bash
./node_modules/typescript/bin/tsc --init
```
tsconfig.json を作り、[公式サイト](https://jp.vuejs.org/v2/guide/typescript.html)の設定をもとに少し手を加えて以下のようにしました。

```json

{
  "compilerOptions": {
    // これは Vue のブラウザサポートに合わせます
    "target": "es5",
    "module": "es2015",
    // これは `this` におけるデータプロパティに対して厳密な推論を可能にします
    "strict": true,
    // this を型推論するため有効にする
    "noImplicitThis": true,
    "moduleResolution": "node",
    // import Vue from 'vue' を有効にする
    "allowSyntheticDefaultImports": true,
    // import/export を使うため有効にする
    "esModuleInterop": true,
    // デコレーターを有効にする
    "experimentalDecorators": true
  }
}
```
コメント入れたら読みにくいですね。

## .vueファイルを import できるようにする

Typescriptで*.vueファイルを読めるようにするため型定義ファイルを設置します。
index.d.ts としました。

```ts

declare module '*.vue' {
  import Vue from 'vue'
  export default Vue
}
```

公式の[型定義ファイル](https://github.com/vuejs/vue/tree/dev/types)

# webpack4で動くようにする
CLIで構築時にインストールされたwebpack@3からwebpack@4に（その他の webpack のモジュール含めて）上げておきます。 `webpack-cli` がいるので入れます。

```bash
npm i -D webpack-cli 
```

webpack4にした際に次のモジュールを更新する必要がありました。
最初は、extract-text-webpack-plugin@nextに更新しましたが、公式ドキュメントにあるmini-css-extract-pluginに変更しました。

* extract-text-webpack-plugin
* html-webpack-plugin
* webpack-dev-server

vue用のモジュールと一緒にnpm outdatedで確認して適宜最新のものに入れ直しました。

```bash
npm outdated
npm i -D extract-text-webpack-plugin@next
.
.
.
```

## ./build/webpack.base.conf.jsを編集

entryのmain.jsを.tsに拡張子を変えます。

```js
entry: {
  app: './src/main.ts'
}
```

.tsを追加します。

```js
resolve: {
  extensions: ['.js', '.ts', '.vue', '.json']
}
```

vue init時にeslintを入れた状態だったので、module.rulesからeslint-loaderを外してts-loaderを追加します。

```js

  module: {
    rules: [
      // ...(config.dev.useEslint ? [createLintingRule()] : []),
      {
        test: /\.ts$/,
        loader: 'ts-loader',
        exclude: '/node_modules/',
        options: {
          appendTsSuffixTo: [/\.vue$/]
        }
      },
      {
        test: /\.vue$/,
        loader: 'vue-loader',
        options: vueLoaderConfig
      },
```

あとでtslintを使う予定。

## ./build/webpack.dev.conf.jsを編集

[mode](https://webpack.js.org/concepts/mode/)を追加します。

```js
const devWebpackConfig = merge(baseWebpackConfig, {
  mode: 'development',
```

## ./build/webpack.prod.conf.jsを編集

[mode](https://webpack.js.org/concepts/mode/)を追加する

```js
const webpackConfig = merge(baseWebpackConfig, {
  mode: 'production',
```

### optimization.splitChunksを設定する
設定ファイルを編集せずに `npm run build` したら以下のエラーが出ました。

```bash
Error: webpack.optimize.CommonsChunkPlugin has been removed, please use config.optimization.splitChunks instead.
```
webpack4から `optimize.CommonsChunkPlugin` が削除されたので、
`optimization.splitChunks` を使えということのようです。

plugins に書かれている `new webpack.optimize.CommonsChunkPlugin({...` をコメントアウトしてoptimization.splitChunksを設定します。

```js
  output: {
    path: config.build.assetsRoot,
    filename: utils.assetsPath('js/[name].[chunkhash].js'),
    chunkFilename: utils.assetsPath('js/[id].[chunkhash].js')
  },
  optimization: {
    splitChunks: {
      cacheGroups: {
        default: false,
        vendor: {
          minChunks: 2
        },
        manifest: {
          minChunks: 2
        },
        app: {
          minChunks: 3
        },
        commons: {
          test: /[\\/]node_modules[\\/]/,
          name: "vendor",
          chunks: "all"
        }
      }
    }
  },
```

チューニングはおいおいやることにしてひとまず設定。
webpack公式ドキュメントにある[splitChunks](https://webpack.js.org/plugins/split-chunks-plugin/#conditions)を読んで適切に設定してください.

### ExtractTextPluginを編集
build時にExtractTextPluginで怒られたのでhashType(md5)を追加して `[md5:contenthash].css` としました。

```js
  plugins: [
    // extract css into its own file
    new ExtractTextPlugin({
      filename: utils.assetsPath('css/[name].[md5:contenthash].css'),
```

[MiniCssExtractPlugin](https://webpack.js.org/plugins/mini-css-extract-plugin/#minimizing-for-production)に変更するほうが良いようです。

あとはvuejs公式の[基本的な使い方](https://jp.vuejs.org/v2/guide/typescript.html#%E5%9F%BA%E6%9C%AC%E7%9A%84%E3%81%AA%E4%BD%BF%E3%81%84%E6%96%B9)にあるように vue を import して書き換えます。

```vue
<script lang="ts">
import Vue from 'vue'
export default Vue.extend({
  name: 'App'
})
</script>
```

上記のように `*.vue` ファイルを書き換えたら `*.js` ファイルを `*.ts` に書き換えます。
これでひとまず動くはずですが、webpack3からwebpack4へのマイグレーションを適宜してください。

```bash
npm run dev
```

dev/build共に無事に動いたらあとはTypescriptでVueに入門するだけです。あー長かった。