【Vue.js】Vue CLIでVue.jsを動かす〜ビルドまでの流れ、生成されたファイルの構造について

前回記事の続きです。
[【Vue.js】Vue CLIでVue.jsを動かす〜プロジェクト作成まで](https://qiita.com/yukibe/items/fac6d6c45dd308e3d7dc)

今回は、vue initで生成されたプロジェクトの中を確認してみます。

以下の項目を中心に、順番にたどりながら進めたいと思います。
作成したファイルがどういう流れでビルドされて、そのためにはどういった設定が必要か、概要を確認します。

- 生成されたディレクトリ・ファイルの概要
- エントリポイントとbundleファイルの出力先
- ビルドとローカルサーバ（開発用サーバ）の起動について
- 描画されるファイルのつながり

# ディレクトリ構造

```
$ cd my-app
$ tree -L 1
.
├── README.md 
├── build             // ビルド用スクリプトを格納
├── config            // ビルド用設定ファイルを格納
├── index.html        // インデックス、最終的にこのファイルに描画される
├── node_modules      // パッケージを格納
├── package-lock.json // パッケージのバージョン情報
├── package.json      // スクリプトやインストールされたパッケージの情報
├── src               // ビルド対象のファイルを格納
└── static            // 静的ファイルを格納
```

実際に描画されるファイル（ビルド対象のファイル）は``srcディレクトリ``に格納されています。

```
$ cd src
$ tree -L 2
.
├── App.vue            // 単一ファイルコンポーネント
├── assets             // 画像やフォントなどを格納
│   └── logo.png       // 画像ファイル
├── components         // コンポーネントを格納
│   └── HelloWorld.vue // 単一ファイルコンポーネント
└── main.js            // エントリーポイントとなるファイル
```

さらに``npm run build``を行うと、``distディレクトリ``が生成されます。
ビルド後アプリケーションを公開した時、実際に表示されるファイル（bundleファイル）が格納されています。

```
$ cd dist
$ tree -L 2
.
├── index.html
└── static
    ├── css
    └── js
```

ただし、ビルド後に以下のメッセージが表示されます。
``HTTP server``で公開されることを前提としているため、``file://``でindex.htmlにアクセスしても表示確認ができないようです。

```
Tip: built files are meant to be served over an HTTP server.
Opening index.html over file:// won't work.
```

## 参考記事
[vue-cliでVue.jsをインストールしたときのファイルについて](https://qiita.com/magaya0403/items/2b5d9641592df0c7cba2)
[vue-cliでwebアプリケーションを作って、GitHubPagesで無料で爆速でリリースした話](https://qiita.com/tiwu_official/items/43dc554ec43dd951812a)

# webpackの設定

## devとbuildの違い
両方とも``my-app/package.json``で定義されています。

### dev
webpackのプラグイン``webpack-dev-server``により、ローカルサーバが起動します。
編集したファイルはブラウザをリロードすれば即反映されるので、動作確認に便利です。

ビルドではありません。

### build
ビルドです。

エントリーポイントを起点として読み込まれたファイル群が、公開用のbundleファイルにまとめられます。

## ビルド/ローカルサーバ起動までの道のり

``npm run build``を実行すると``build/build.js``が実行されます。

``webpack-dev-server``でローカルサーバを起動する場合は、build.jsは呼ばれません。（ビルドではありません。）
設定ファイルも``webpack.prod.conf.js``ではなく``webpack.dev.conf.js``が呼ばれます。webpack.dev.conf.jsには各種設定に加え、ローカルサーバ起動の処理も記述されています。

```package.json
"scripts": {
  // ローカルサーバ起動
  "dev": "webpack-dev-server --inline --progress --config build/webpack.dev.conf.js",
  "start": "npm run dev",
  // ビルド
  "build": "node build/build.js"
},
```

``build.js``から設定ファイルが読み込まれ、必要な値がセットされます。

```build/build.js
const config = require('../config')
const webpackConfig = require('./webpack.prod.conf')
```

エントリポイントの場所やbundleファイルの出力先などを指定する``webpack.base.conf.js``は、共通で読み込まれます。

```build/webpack.prod.conf.js
// webpack.dev.conf.jsも同じ記述あり
const baseWebpackConfig = require('./webpack.base.conf')
```

抜粋して確認してみます。

```build/webpack.base.conf.js
const config = require('../config')
```

```build/webpack.base.conf.js
module.exports = {
  // ファイルパスの起点を指定
  // my-appプロジェクトの場合はmy-appディレクトリが指定される
  context: path.resolve(__dirname, '../'),
  // エントリーポイント
  entry: {
    app: './src/main.js'
  },
  // bundleファイルの出力先
  output: {
    path: config.build.assetsRoot,
    // nameやprocessはpackage-lock.jsonより
    filename: '[name].js',
    publicPath: process.env.NODE_ENV === 'production'
      // ビルド用
      ? config.build.assetsPublicPath
      // ローカルサーバ起動用
      : config.dev.assetsPublicPath
  },
```

webpack.base.conf.jsでは``config/index.js``を読み込んでいます。つまり、index.jsはビルド/ローカルサーバ起動を問わず呼び出されるファイルということです。
こちらは、1ファイルの中にそれぞれの設定が記述がされています。

```config/index.js
module.exports = {
  dev: {
    // ローカルサーバ起動の設定
  },

  build: {
    // ビルドの設定
  }
}
```

そのほかに``package-lock.json``も読み込まれているようです。（``name``や``process``の設定が記述されています。）

``webpackConfig``がセットされたら、ビルドが実行されます。

```build/build.js
webpack(webpackConfig, (err, stats) => {
  // ビルド処理
})
```

これらの設定は、webpackの``webpack.config.jsファイル``に該当する部分です。
以前に記事の中で紹介しました。
[【JavaScript】ビルドとは何か〜webpackを使ってビルドする](https://qiita.com/yukibe/items/453d874eb7ce929363d6)

開発用にビルドを行う（本番用とは別のサーバにビルドする）場合は、別途設定が必要です。
[vue-cliのwebpackテンプレートで環境別にnpm run buildが出来るようにする](https://qiita.com/irohaPh/items/05ca74b9566058542e5e)

## 参考記事
[webpackのメモ](https://qiita.com/hrloca/items/61ced4d16d6c257ada02)
[webpack.config.jsで思ったpath.resolveって何のためにあるの？](https://koukitips.net/post1825/)

# ファイルの構造
デモ画面は以下のファイルから構成されています。
どこから何が読み込まれて何が表示されているのか、順番に確認していきます。

- index.html
- src/main.js
- src/App.vue
- src/components/HelloWorld.vue

## コンポーネントとは
先にコンポーネントについて簡単に説明します。

コンポーネントとは``再利用可能な名前付きの部品(Vueインスタンス)``のことです。
複数の画面で、共通して利用したい機能がある時に使います。

他の部品とは切り離されているため、メンテナンスも簡単になります。

コンポーネントの設定については、公式サイトに詳しく説明されています。
[公式サイト>コンポーネントオプション](https://012-jp.vuejs.org/api/options.html)

### 単一ファイルコンポーネント
これから紹介する``App.vue``と``HelloWorld.vue``は、``単一ファイルコンポーネント``と呼ばれています。

1つのコンポーネント内で``HTML, JavaScript, CSSがセット``になっており、ファイルの拡張子が``.vue``になります。

中規模以上の開発でよく使われるそうです。

[公式サイト>単一ファイルコンポーネント](https://jp.vuejs.org/v2/guide/single-file-components.html)

## index.html
``npm run dev``実行時メッセージの通り、``http://localhost:8080``にアクセスすると表示されるインデックスです。

```
Your application is running here: http://localhost:8080
```

普通のhtmlファイルですが、``<div id="app"></div>``の部分に注目です。
コメントの通り、ここにビルドされたファイルが挿入されます。

```index.html
<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width,initial-scale=1.0">
    <title>my-app</title>
  </head>
  <body>
    <div id="app"></div>
    <!-- built files will be auto injected -->
  </body>
</html>
```

## main.js
エントリーポイントとなるjsファイルです。

index.htmlの``id="app"``要素に結びついています（マウント）。main.jsの内容が``id="app"``要素の中に挿入されます。

``template: '<App/>'``により、main.jsのtemplateとして、Appコンポーネントのtemplateがそのまま反映されるようです。

```src/main.js
// The Vue build version to load with the `import` command
// (runtime-only or standalone) has been set in webpack.base.conf with an alias.
// 具体的に何をしているのかわかりません・・・おまじない?
import Vue from 'vue'
// Appコンポーネントをインポート
import App from './App'

// Vue.js起動時に、コンソールにヒントが表示されなくなる
Vue.config.productionTip = false

/* eslint-disable no-new */
new Vue({
  // elオプション
  // アプリケーション（この場合はindex.html）に紐づける要素を指定する
  // #appにマウントする、と言うらしい
  el: '#app',
  // componentsオプション
  // Appコンポーネントを利用する
  components: { App },
  // templateオプション
  // Appコンポーネントのタグ名を設定する
  // <App/>としてAppコンポーネントのtemplateを呼び出せる
  template: '<App/>'
})
```

## App.vue
main.jsから呼び出されている単一ファイルコンポーネントです。

``templateタグ``で囲われている部分が、実際に表示されます。
templateタグ内では、さらに``HelloWorldコンポーネント``が呼び出されています。

```src/App.vue
<template>
  <div id="app">
    <img src="./assets/logo.png">
    <!-- ここにHelloWorldコンポーネントが挿入される -->
    <HelloWorld/>
  </div>
</template>

<script>
// HelloWorldコンポーネントをインポート
import HelloWorld from './components/HelloWorld'

export default {
  // nameオプション
  // コンポーネントコンストラクタ名をAppに設定する
  name: 'App',
  // componentsオプション
  // HelloWorldコンポーネントを利用する
  components: {
    HelloWorld
  }
}
</script>

<style>
  /* CSSを記述 */
</style>
```

## HelloWorld.vue
App.vueから呼び出されている単一ファイルコンポーネントです。

``templateタグ``で囲われている部分が、実際に表示されます。
``dataオプション``のmsgプロパティを設定しているので、その値も反映されます。

```src/components/HelloWorld.vue
<template>
  <div class="hello">
　<!-- 設定したmsgプロパティの値が入る -->
  <h1>{{ msg }}</h1>
  <!-- 省略 -->
  </div>
</template>

<script>
export default {
  // nameオプション
  // コンポーネントコンストラクタ名をHelloWorldに設定する
  name: 'HelloWorld',
  // dataオプション
  data () {
    return {
      // msgプロパティの値を設定する
      msg: 'Welcome to Your Vue.js App'
    }
  }
}
</script>

<!-- Add "scoped" attribute to limit CSS to this component only -->
<!-- HelloWorldコンポーネント限定のCSSを設定 -->
<style scoped>
  /* CSSを記述 */
</style>
```

## 参考記事
[VueCLIからVue.js入門①【VueCLIで出てくるファイルを概要図で理解】](https://qiita.com/po3rin/items/3968f825f3c86f9c4e21)
[vue-cliで始めるVue.jsチュートリアル (1)](https://qiita.com/sin_tanaka/items/29769266b3b078ea0f7c)