Nuxt.jsのビルド時間を短縮する為に頑張った話

# やりたい事
- Nuxt.jsのビルド時間を短縮したい
- JSのファイルサイズを軽くしたい

# やった事
- 1.Webpack Bundle Analyzerを使って、現状のファイルサイズを把握。
- 2.その中でどのファイルがネックになっているか調査する。
- 3.リファクタリングする。
※今回は一部のみ

# 0.各キーワード説明
## Webpack Bundle Analyzerとは？
下記を可視化してくれる。
- webpackの速度改善の為、遅いloaderやpluginを知りたい
- バンドルファイルのサイズを減らしたいので、バンドルファイル内の各パッケージがどのくらいの容量を占めているか知りたい。

参考：https://github.com/webpack-contrib/webpack-bundle-analyzer

# 1.Webpack Bundle Analyzerを使って、現状のファイルサイズを把握。
```
$ yarn build --analyze
```
<img width="479" alt="スクリーンショット 2019-12-16 23.47.31.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/85181/d3ee19b9-ebfd-4cda-c350-fc7bdb9ae1b7.png">



# 2.その中でどのファイルがネックになっているか調査する。
<img width="717" alt="スクリーンショット 2019-12-16 23.52.50.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/85181/32df1c66-4f11-61ee-3e78-ce6f42d04940.png">

- ①Chart.jsが重い
- ②moment.jsが重い
   - localeが無駄に多い
- ③lodash.jsが重い

# 3.リファクタリングする。
- ①Chart.jsが重い
  - vue-chart.jsの中で使われているので、外すのは難しそうだった。  
  

```json

  "dependencies": {
    "bootstrap": "^3.4.1",
    "bootstrap-social": "^5.1.1",
    "bootstrap-vue": "^2.0.0-rc.24",
    "chart.js": "^2.8.0",
    "chosen-js": "^1.8.5",
    "ckeditor": "^4.11.4",
    "clipboard": "^2.0.4",
    "datatables": "^1.10.13",
    "datatables-all": "^1.10.13",
    "firebase": "^6.0.1",
    "font-awesome": "^4.7.0",
    "hchs-vue-charts": "^1.2.8",
    "icheck": "^1.0.2",
    "ionicons": "^4.5.8",
    "jquery-datetimepicker": "^2.5.21",
    "jquery-ui-dist": "^1.12.1",
    "jquery.flot": "^0.8.3",
    "luxon": "^1.15.0",
    "moment": "^2.24.0",
    "sass": "^1.20.1",
    "sass-loader": "^7.0.1",
    "select2": "^4.0.7",
    "sweetalert2": "^8.11.6",
    "turbolinks": "^5.0.3",
    "vue-chartjs": "^3.4.2",
    "vue-datetime": "^1.0.0-beta.10",
    "vue-js-modal": "^1.3.31",
    "vue-nl2br": "^0.1.1",
    "vue-resource": "^1.3.4",
    "vue-spinner": "^1.0.3",
    "vue-spinner-component": "^1.0.5",
    "vuex": "^3.1.1",
    "weekstart": "^1.0.0",
    "yubinbango": "yubinbango/yubinbango"
  },

```   


- ②moment.jsが重い
    - nuxt.config.jsにmomentの設定を追加  

```js


module.exports = {
  mode: 'spa',

// 省略

  build: {
    parallel: true,

    /*
     * Run ESLint on save
     */
    extend (config, { isDev, isClient }) {
      // クライアントのバンドルの Webpack 設定のみを拡張する
      if (isClient) {
        config.devtool = '#source-map'
      }

      if (isDev && isClient) {
        config.module.rules.push({
          enforce: 'pre',
          test: /\.(js|vue)$/,
          loader: 'eslint-loader',
          exclude: /(node_modules)/,
        })

        const vueLoader = config.module.rules.find(
          ({ loader }) => loader === 'vue-loader')
        const { options: { loaders } } = vueLoader || { options: {} }

        if (loaders) {
          for (const loader of Object.values(loaders)) {
            changeLoaderOptions(Array.isArray(loader) ? loader : [loader])
          }
        }

        config.module.rules.forEach(rule => changeLoaderOptions(rule.use))

        // For faster build. Use hard-source-webpack-plugin.
        const HardSourceWebpackPlugin = require('hard-source-webpack-plugin')
        config.plugins.push(new HardSourceWebpackPlugin())
      }
    },
      // ここを変更
      vendeer: ['moment'],
      plugins: [
          new MomentLocalesPlugin({
              localesToKeep: ['es-us', 'ja']
          })
      ]
  },
}
```

- ③lodash.jsが重い
    - 現状、ディープコピーの箇所にしかlodash.jsが使われていなかった

```js
<script>
import { mapActions, mapGetters } from 'vuex'
import cloneDeep from 'lodash'

export default {
  name: 'agent',
  layout: 'administrator/default',
  data () {
    return {
     // 省略
      },
    }
  },
  async created () {
    // 省略
  },
  computed: {
    // 省略
  },
  methods: {
    async setProgrammingCategory () {
      
      let programmingCategory = this.programmingCategories.find(programmingCategory => Number(programmingCategory.id) === Number(this.programmingCategoryId))

      
      if (!programmingCategory) {
        this.$nextTick(() => this.$nuxt.$loading.start())
        try {
          await this.fetchProgrammingCategories()
        } catch (e) {
          console.log(e.message)
          return
        } finally {
          this.$nuxt.$loading.finish()
        }
        programmingCategory = this.programmingCategories.find(programmingCategory => Number(programmingCategory.id) === Number(this.programmingCategoryId))
        if (!programmingCategory) {
          this.$router.push('/errors/404')
        }
      }

      this.formData = cloneDeep(programmingCategory)
    },
    async update () {
      // 省略
    },
    ...mapActions('administrator', ['fetchProgrammingCategories', 'updateProgrammingCategory']),
  },
}
</script>
```

- 修正点   
lodashをimport _ from 'lodash'のように読み込んでしまうと、全てのlodash関数などがbundleされてしまいます。
それを回避するため、使用する関数のみをimportするように変更します。

```js
<script>
import cloneDeep from 'lodash/cloneDeep'


</script>
```

## 改善結果
### 改善前
<img width="560" alt="スクリーンショット 2019-12-17 0.15.06.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/85181/56d880b5-17c3-a669-5559-31174e5fba63.png">
↓
↓
### 改善後
lodashとmoment.jsはほぼ消えた！
<img width="585" alt="スクリーンショット 2019-12-17 0.15.15.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/85181/89b10d33-2bb2-d09f-195d-955023f76cf3.png">

### 改善前
<img width="472" alt="スクリーンショット 2019-12-17 0.16.05.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/85181/b5d03f8f-e97b-6fbc-3709-2f541aa606fb.png">
↓
↓
### 改善後
約1MB軽量化！！
<img width="471" alt="スクリーンショット 2019-12-17 0.16.15.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/85181/1d81b918-61b5-f4dc-0b46-86eb97018a52.png">


# まとめ
- ビルドの高速化するにあたって、moment.jsやlodash.jsが重くなるのはよくありそう
- 補足としては、そもそもmoment.jsよりday.jsの方が軽いので、そっちを使うと良いらしい！

# 参考記事
- https://speakerdeck.com/bumptakayuki/nuxt-dot-jsfalsehirutowogao-su-hua-suruwei-niwan-zhang-tutahua 
自分の発表資料も参考により具体的に書いてみました。