Vueインスタンスとコンポーネントの関係

# はじめに
オブジェクトリテラルの観点でVueインスタンス、コンポーネントを整理してみる。
これによって、

- ルートVueインスタンス
- グローバルコンポーネント
- ローカルコンポーネント

３つのVueインスタンスがそれぞれ何故そういう書き方になるのかがクリアになる。また、`単一ファイルコンポーネント`等のより発展的な内容についても理解の助けになる。本記事では、「ルートVueインスタンスとは？」「コンポーネントとは？」といった、そもそものところについては触れないけれども、それらについてなんかしっくりこない感じがあるひとに対しては何かしら役に立つかもしれない。

- 本記事では、Vueインスタンス、コンポーネントの生成時に記述されるオブジェクトリテラルを便宜上**Vueオブジェクト**と呼ぶことにしています。  
- 記事内のサンプルコードはCDNでVueを読み込めば動く内容です。  
- サンプルコードの結果はルートVueインスタンス、グローバルコンポーネント、ローカルコンポーネントで、ほぼ同じです。

# Vueオブジェクトによる整理
まずは、ルートVueインスタンス、グローバルコンポーネント、ローカルコンポーネントについて、それぞれ導入的な書き方から**Vueオブジェクト**の観点で整理した書き方に変更する。
## ルートVueインスタンス
よくある導入的な書き方。

```HTML
<div id="container">
    <p>{{ comments }}.</p>
</div>
```

```js
// ルートVueインスタンス
new Vue({
    el: '#container',
    data: {
        comments: "hello in root"
    }
});
```

```
# ブラウザ出力
hello in root.
```
ルートVueインスタンスを生成する`new Vue()`の引数はオブジェクトリテラル。これを**Vueオブジェクト**として外に出す。

```js
// ルートVueオブジェクト
// new Vue()の引数を外に出した
let rootVueObj = {
    el: '#container',
    data: {
        comments: "hello in root"
    }
};
// ルートVueインスタンス
// rootVueObjを登録
new Vue(rootVueObj);
```

## グローバルコンポーネント
よくある導入的な書き方。

```HTML
<div id="container">
    <g-component></g-component>
</div>
```
```js
// グローバルコンポーネント
Vue.component('g-component', {
    data: function () {
        return {
            comments: "hello in global component"
        }
    }
    , template: '<p>{{ comments }}.</p>'
});

// ルートVueインスタンス
new Vue({ el: '#container' });
```
```
# ブラウザ出力
hello in global component.
```

**ルートVueオブジェクト**については前章同様。  
グローバルコンポーネントを生成する`Vue.component()`の第二引数はオブジェクトリテラル。これも**コンポーネントVueオブジェクト**として外に出す。

```js
// コンポーネントVueオブジェクト
// Vue.component()の第二引数の内容を外に出した
let componentVueObj = {
    data: function () {
        return model
    }
    , template: '<p>{{ comments }}.</p>'
};

// ルートVueオブジェクト
// new Vue()の引数を外に出した
let rootVueObj = {
    el: '#container'
};

// グローバルコンポーネント
// componentVueObjを登録
Vue.component('g-component', componentVueObj);

// ルートVueインスタンス
// rootVueObjを登録
new Vue({rootVueObj);
```

## ローカルコンポーネント
よくある導入的な書き方。

```HTML
<div id="container">
    <l-component></l-component>
</div>
```
```js
// コンポーネントVueオブジェクト
let componentVueObj = {
    data: function () {
        return {
            comments: "hello in local component"
        }
    }
    , template: '<p>{{ comments }}.</p>'
};

// ルートVueインスタンス
new Vue( {
    el: '#container', 
    components: {
        'l-component': componentVueObj
    }
});    
```
```
# ブラウザ出力
hello in local component.
```
**ルートVueオブジェクト**についてこれまで同様外に出す。  

```js
let componentVueObj = {
    data: function () {
        return {
            comments: "hello in local component"
        }
    }
    , template: '<p>{{ comments }}.</p>'
};

// ルートVueオブジェクト
// new Vue()の引数を外に出した
let rootVueObj = {
    el: '#container', 
    components: {
        'l-component': componentVueObj
    }
};

// ルートVueインスタンス
// rootVueObjを登録
new Vue(rootVueObj);    
```

# Vueオブジェクトの定義と登録
ここまでの整理で、**Vueオブジェクト**が**定義と登録によって成立している**ことがはっきりしたので、今度はルートVueインスタンス、グローバルコンポーネント、ローカルコンポーネント、それぞれの定義と登録を比較してみる。
## 定義
```js
// ルートVueインスタンスでのVueオブジェクト記述形式
let vueObj = {
    el: 'DOMセレクタ',
    data: {}
};
```
```js
// グローバルコンポーネント／ローカルコンポーネントでのVueオブジェクト記述形式
let vueObj = {
    data: function () {
        return {}
    }
    // , template: 'テンプレート'
```
まず、グローバルコンポーネントとローカルコンポーネントの**Vueオブジェクト**は全く同じである。ルートVueインスタンスは全く同じでは無いが、かなり似ている（templateオプションについては、後で触れます）。

## 登録
```js
// ルートVueインスタンスでのVueオブジェクトの登録
new Vue(rootVueObj);
```
```js
// グローバルコンポーネントでのVueオブジェクトの登録
Vue.component('g-component', componentVueObj);
// ルートVueインスタンス
new Vue({rootVueObj);
```
```js
// ローカルコンポーネントでのVueオブジェクトの登録
let rootVueObj = {
    el: '#container', 
    components: {
        'l-component': componentVueObj
    }
};
// ルートVueインスタンス
new Vue(rootVueObj);    
```
**Vueオブジェクト**という観点から整理すると、
- 定義の書き方は２種類
- 登録方法はルートVueインスタンス、グローバルコンポーネント、ローカルコンポーネントによってそれぞれ異なる

これだけでもかなり見通しがよくなりませんか？

# 考察
## ルートVueインスタンスとコンポーネントの違い
### elオプション
ルートVueインスタンスとコンポーネントの大きな違いはHTML要素へ紐付けする際の対象の違い。ルートVueインスタンスは`'#container'`という**既存のHTML要素**に`id属性`で紐付けしている。それに対して、グローバルコンポーネントの`'g-component'`もローカルコンポーネントの`'l-component'`も**カスタムタグ**への紐付けである。Vue独自ルールである**カスタムタグ**は素のHTMLのままでは使用することはできない。そのため**既存のHTML要素の接点としてルートVueインスタンスが存在している**。そう考えると、ルートVueインスタンスにだけ`'elオプション'`がある理由が理解できる（[唯一の例外は el のようなルート固有のオプションです](https://jp.vuejs.org/v2/guide/components.html#%E5%9F%BA%E6%9C%AC%E4%BE%8B)）。
### dataオプション
ルートVueインスタンスでは直接文字列や数値が入るのに対し、コンポーネントでは関数経由でそれらを指定する。その理由はインスタンスごとの値を保持するためということだが（[data は関数でなければなりません](https://jp.vuejs.org/v2/guide/components.html#data-%E3%81%AF%E9%96%A2%E6%95%B0%E3%81%A7%E3%81%AA%E3%81%91%E3%82%8C%E3%81%B0%E3%81%AA%E3%82%8A%E3%81%BE%E3%81%9B%E3%82%93)）、関数にするとなぜそれが可能なのかはわからない（何となくはわかる気もするが、、）。誰か教えていただけると嬉しい。
### templateオプション
`templateオプション`はルートVueインスタンスに登録する**Vueオブジェクト**にも記述できる（あまりそういう書き方をは目にしないが、、）。なので、実はこの点に関しては両者に違いはない。
逆に、本記事のようような書き方をする場合、HTML側ににテンプレートの内容を書くことはできない。`X-テンプレート`を使うとできるようなので、気になる人はそちらを試してみると良いかもしれない（[X- テンプレート](https://jp.vuejs.org/v2/guide/components-edge-cases.html#X-%E3%83%86%E3%83%B3%E3%83%97%E3%83%AC%E3%83%BC%E3%83%88)）。
`X-テンプレート`の他には、`単一ファイルコンポーネント`という形式で**Vueオブジェクト**からテンプレートの記述を分割できる。プロジェクトがある程度大きくなれば`単一ファイルコンポーネント`を使うことが必須になるはず。本記事では触れないけれども、本記事の内容が理解の助けになると思う（そもそも、それが本記事の目的の一つ）。
## グローバルコンポーネントとローカルコンポーネントの違い
両者の違いは名前通りグローバルとローカルの違いだが、それは記述にも表れている。グローバルコンポーネントの登録は直接`カスタムタグ`の名前が紐付けられている。それに対し、ローカルコンポーネントの登録はルートVueインスタンス（親）の`componentsオブション`に対してである（親**Vueオブジェクト**はルートVueインスタンスである必要はない）。これによってローカル化を実現している。 
違いではなく共通点に目を向けると、どちらもルートVueインスタンスで紐付けされた`HTML要素`（`<div id='container'>`）のなかで、コンポーネントの`カスタムDOM`が記述されている。繰り返しになるが、ルートVueインスタンスは**既存のHTML要素の接点**なので、グローバルコンポーネントであってもその外には出られない。