GoogleAppsScriptのWebAppsでVue.jsするためのvue.config.js

# はじめに

この記事は以前投稿した[Vue.js製のSPAをGoogleAppsScriptにデプロイするためのWebpack設定](https://qiita.com/clomie/items/2361e5922f7ea5d5388d)に対する、Vue CLI v3向けのアップデートです。

# Vue.js側のビルド設定方針

GASのWebAppsで公開されるWebページは、`doGet`関数で返すHTMLがエスケープされて配信され、クライアント側でevalするような仕組みになっていて、このHTMLにJS、CSS含めすべてのリソースをインライン化して配信する必要があります。

ただし、HTMLのサイズが大きくなってくると画面の表示にかなり多くの時間がかかるようになってきます。このため、npmモジュールやwebフォントなど外部から取得できるものはなるべくそちらを使う方針が良いと思います。

設定は主にvue.config.jsの[`chainWebpack`プロパティ](https://cli.vuejs.org/config/#chainwebpack)で行います。
`chainWebpack`については、適宜[公式ドキュメントのサンプル](https://cli.vuejs.org/guide/webpack.html#chaining-advanced)や[webpack-chainのREADME](https://github.com/neutrinojs/webpack-chain/tree/v4)を参照してください。正直とっつきにくいです。もう少しシンプルに書ける方法等あれば教えてください。

# vue.config.jsサンプル

```vue.config.js
module.exports = {
  chainWebpack: config => {
    // disable prefetch and preload
    config.plugins.delete('prefetch')
    config.plugins.delete('preload')

    // Make js and css files inline into index.html
    config
      .plugin('html-inline-source')
      .use(require('html-webpack-inline-source-plugin'))
    config.plugin('html').tap(args => {
      args[0].inlineSource = '(/css/.+\\.css|/js/.+\\.js)'
      return args
    })

    // make inline images
    config.module
      .rule('images')
      .use('url-loader')
      .options({})

    // make inline media
    config.module
      .rule('media')
      .use('url-loader')
      .options({})

    // make inline fonts
    config.module
      .rule('fonts')
      .use('url-loader')
      .options({})

    // make inline svg
    config.module
      .rule('svg')
      .uses.delete('file-loader')
      .end()
      .use('url-loader')
      .loader('url-loader')
      .options({})

    // Get npm modules from CDN
    config.plugin('webpack-cdn').use(require('webpack-cdn-plugin'), [
      {
        modules: [
          {
            name: 'vue',
            var: 'Vue',
            path: 'dist/vue.runtime.min.js'
          },
          {
            name: 'vue-router',
            var: 'VueRouter',
            path: 'dist/vue-router.min.js'
          }
        ]
      }
    ])

    if (process.env.NODE_ENV === 'production') {
      // html minify settings for GAS
      config.plugin('html').tap(args => {
        args[0].minify.removeAttributeQuotes = false
        args[0].minify.removeScriptTypeAttributes = false
        return args
      })
    }
  }
}
```

下記のnpmモジュールのインストールが必要です。

```console
$ npm i -D html-webpack-inline-source-plugin webpack-cdn-plugin
```

少し解説します。

## prefetch, preloadの無効化

デフォルトで有効になっていますが、GASではインライン化する都合上無効化します。

```js:vue.config.js
module.exports = {
  chainWebpack: config => {
    config.plugins.delete('prefetch')
    config.plugins.delete('preload')
  }
}
```

## JS,CSSのインライン化

[html-webpack-inline-source-plugin](https://github.com/DustinJackson/html-webpack-inline-source-plugin/)を導入しましょう。

html-webpack-pluginに`inlineSource`プロパティを設定するのを忘れてハマりがちなので注意。後述するCDN化されたファイルに反応しないよう、パスで指定されたファイルのみ対象にします。

```vue.config.js
module.exports = {
  chainWebpack: config => {

    config
      .plugin('html-inline-source')
      .use(require('html-webpack-inline-source-plugin'))

    config.plugin('html').tap(args => {
      args[0].inlineSource = '^(/css/.+\\.css|/js/.+\\.js)'
      return args
    })

  }
}
```

## 静的リソースのインライン化

画像などのファイルもすべてData URI Scheme化する必要があります。
しかし、デフォルトのurl-loaderの設定ではlimitが設定されており、4KB以上のファイルは抽出されてファイルパスへの参照に置き換えられてしまいます。

それ以上のサイズのファイルを含めたい場合、下記のようにurl-loaderのoptionをデフォルトに戻し、limitを無効化するなどの対応が必要になります。

```vue.config.js
module.exports = {
  chainWebpack: config => {
    config.module
      .rule('images')
      .use('url-loader')
      .options({})
  }
}
```

## npmモジュールのCDN参照化

[webpack-cdn-plugin](https://github.com/van-nguyen/webpack-cdn-plugin)を導入すると、参照しているnpmモジュールを、CDNを参照するように変換してくれます。
Vuetifyなどcssも一緒にnpmモジュールにバンドルされている場合は[cssの方もCDNを参照するように設定](https://github.com/clomie/gas-vue-typescript/blob/master/vue.config.js#L51)した上で、importされないよう[externalsを設定](https://github.com/clomie/gas-vue-typescript/blob/master/vue.config.js#L78)する必要があります。

```vue.config.js
module.exports = {
  chainWebpack: config => {

    config.plugin('webpack-cdn').use(require('webpack-cdn-plugin'), [
      {
        modules: [
          {
            name: 'vue',
            var: 'Vue',
            path: 'dist/vue.runtime.min.js'
          },
          {
            name: 'vue-router',
            var: 'VueRouter',
            path: 'dist/vue-router.min.js'
          }
        ]
      }
    ])

  }
}
```

## minifyの設定

デフォルトのHtmlWebpackPluginの設定では、productionモードではminifyされすぎてHtmlServiceで読み込む際にエラーになってしまうようです。
いろいろと試したところ、`removeAttributeQuotes`, `removeScriptTypeAttributes`の2つを`false`にすることでGASでも読み込めるようになりました。


```vue.config.js
module.exports = {
  chainWebpack: config => {

    if (process.env.NODE_ENV === 'production') {
      // html minify settings for GAS
      config.plugin('html').tap(args => {
        args[0].minify.removeAttributeQuotes = false
        args[0].minify.removeScriptTypeAttributes = false
        return args
      })
    }

  }
}
```

# おわりに

下記のリポジトリにて、Vue.js+TypeScriptでGASのWebAppsにデプロイ可能な構成を試しています。
上記設定でビルドされるindex.htmlを確認したい場合などに使ってください。

https://github.com/clomie/gas-vue-typescript