Vue.js vue-routerのページに対してコンポーネントを大量に読み込むのがつらい問題を解消する

# はじめに

結論のコードだけチャッチャと見たい人は[こちら](https://github.com/potato4d/vuetiful-vue-router/)

Vue.jsで開発をする際、`webpack`+`vue-loader`で開発することが多いのですが、`.vue`コンポーネントが多くなりすぎて流石に手動で扱うのが辛くなってきた現状があります。

特に`vue-router`を触っているプロジェクトでは、ルーティングのViewにあたるコンポーネントに、諸々の小さいコンポーネントを渡してやるのがつらい問題があります。
しっくりこない方も多いと思いますので、下にサンプルを記しますが、今回はこのつらい問題を解消していきたいと思います。

# つらい状況

ある程度本質でない部分は省きますが、大体こんな感じのプロジェクト構成があるとします。

```
/index.js

/components/
 |
 +--- CompA.vue
 |
 +--- CompB.vue

/pages/
 |
 +--- PageA.vue
 |
 +--- PageB.vue // 今回は使いません
```

まずは各ファイルの構成を紹介します。

```js:index.js
const Vue = require("vue");
const VueRouter = require("vue-router");
Vue.use(VueRouter);

const App = Vue.extend({});
const router = new VueRouter();

// ページ読み込み

const PageA = Vue.extend(require("./pages/PageA.vue"));

// ルーティング

router.map({
	"/b": {
		component: PageA
	},

	"/b": {
		component: PageB
	}
});

router.start(App, "#app");
```

```html:CompA.vue
<template lang="html">
	<p>Hello.</p>
</template>

<style lang="css"></style>

<script>
module.exports =  {};
</script>
```

```html:PageA.vue

<template lang="html">
	<comp-a />
</template>

<style lang="css"></style>

<script>
const CompA = require("../components/CompA.vue");
module.exports =  {
	components: {
		CompA
	}
};
</script>
```

これだけ見ているとあまりつらそうには見えませんが、例えば`CompA`に相当するコンポーネントとして、以下のようなものがでてくるとどうでしょう。  
いずれにしても、Webアプリケーション開発にて、作成するコンポーネントかと思います。

* Btn
* Flash
* Modal

これらを追加していくと、PageA.vueは以下のようになります。

```html:PageA.vue

<template lang="html">
	<comp-a />
</template>

<style lang="css"></style>

<script>
const CompA = require("../components/CompA.vue");
const Btn   = require("../components/Btn.vue");
const Flash = require("../components/Flash.vue");
const Modal = require("../components/Modal.vue");

module.exports =  {
	components: {
		CompA,
		Btn,
		Flash,
		Modal
	}
};
</script>
```

割りとコードとしてつらくなってきました。そして、この管理方法の場合は**Page(.+).vue**系のコンポーネント全てに置いて、このコードが追加されることとなります。

流石に画面ごとに一々requireして設定していると非常に非効率なので、今回は次の方法を使って解決してみます。

# つらくない方法

まず、index.jsを以下のように変更します。

```js:index.js
const Vue = require("vue");
const VueRouter = require("vue-router");
Vue.use(VueRouter);

const App = Vue.extend(require("./App.vue"));
const router = new VueRouter();
const Components = {};
const getComponents = c => Object.assign(c, {"components": Components});

// コンポーネント読み込み

Components["CompA"] = require("./components/CompA.vue");
Components["Btn"]   = require("./components/Btn.vue");
Components["Flash"] = require("./components/Flash.vue");
Components["Modal"] = require("./components/Modal.vue");

// ページ読み込み


const PageA = Vue.extend(getComponents(require("./pages/Page.vue")));

// ルーティング

router.map({
	"/b": {
		component: PageA
	},

	"/b": {
		component: PageB
	}
});

router.start(App, "#app");
```

これでどうでしょう。
この場合、Object.assign(PHPでいうarray_mergeのような機能)を用いる関数`getComponents`を用意し、
`Vue.extend`でVueコンポーネントとして初期化する前に噛ませていることで、全てのコンポーネントを自動的に子コンポーネントとして追加してやっています。

そのため、手動で追加する部分は全く無く、新しくコンポーネントが増えた際、コンポーネント読み込みのところで`require()`してやるだけで自動的に全ページでの利用が可能となります。

したがって、`PageA.vue`も以下のような形となり、コンパクトになります。

```html:PageA.vue
<template lang="html">
	<comp-a />
	<btn />
	<flash />
	<modal />
	<!-- すべて使える！ -->
</template>

<style lang="css">
</style>

<script>
module.exports = {};
</script>
```

これでコンポーネント読み込みつらい問題が無事解消されました。

# Q&A

Q. 全部に読み込ませるとパフォーマンス悪くならない？

A. まだ検証中で不確定なのですが、webpackでビルド時によしなに最適化されてくれているフシがありますので、そこまで大きな問題とはならないと思っています。

Q. それ◯◯で出来るよ

A. 教えてください

# おまけ

サンプルプロジェクト用意しました。

[potato4d/vuetiful-vue-router](https://github.com/potato4d/vuetiful-vue-router/)