Vue.jsを最速でビルドして、無料で公開するぞ！[ハンズオン形式]

会社のハンズオン形式の勉強会資料になります（2/9）

今回のハンズオンの結果はGitHubにあげてます
https://github.com/kurosame/vuejs-parcel-hands-on

---

## このハンズオンで何ができるようになるのか
誰でも簡単に

Vue.jsで書いたコードがビルドできて

無料でWeb公開できるようになります

Firebaseの設定で一部画面操作がありますが、
それ以外はターミナルにコピペでできるようになってます

---

## 前置き
ハンズオンだけだと5分で終わるので、時間稼ぎのため、説明多めに挟んでます
ハンズオンだけ目的の人はMarkdownのコードの部分だけ見て実行みてください

---

## 使う技術
フロントエンドのビルドに
<font color="Red">Parcel</font>

Web公開（無料）に
<font color="Red">Firebase Hosting</font>

を使います

---

## 事前準備
### Node.js（+npm）をインストールする

```sh
$ node -v;npm -v
v8.9.1
5.5.1
```

私は上記のバージョンですが、`v8.x.x`か`v9.x.x`であればたぶん問題無いと思います
npmを使用しますが、Node.jsにバンドルされているのでnodeをインストールすればnpmも使えるようになります

### Googleアカウントを作る
https://accounts.google.com/SignUp?hl=ja

---

## スタート
作業フォルダを決めましょう、どこでも良いです

```sh:例
$ mkdir ~/vuejs-parcel-hands-on
$ cd ~/vuejs-parcel-hands-on
```

---

## パッケージ管理の導入
```sh
$ npm init
```

以下のような質問が聞かれますが

```
package name: (vuejs-parcel-hands-on)
version: (1.0.0)
description:
entry point: (index.js)
test command:
git repository:
keywords:
author:
license: (ISC)
```

今回は全部enterで大丈夫です

今後自作したパッケージを公開する時などは、`description`や`keywords`などちゃんと書いた方が良いです
他の方がパッケージを検索しやすくなったり、パッケージの内容を理解しやすくするためです

上記以外にも色々設定項目があります
http://liberty-technology.biz/PublicItems/npm/package.json.html

---

現在の作業中ディレクトリ直下に`package.json`が出来上がります
先程の質問によって中身が変わってきますが、全部enterを押した人は以下のようになっています

```json:package.json
{
  "name": "vuejs-parcel-hands-on",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "",
  "license": "ISC"
}
```

`package.json`は使用するパッケージ（ライブラリ）やシェルスクリプトなどを記載したJSONファイルになります　

---

## Vue.jsをインストール
さっそくVue.jsをインストールしてみましょう！

```sh
$ npm install vue
```

---

package.jsonを見ると以下のように依存パッケージとしてvueが追加されています

```json:package.json
{
...
  "dependencies": {
    "vue": "^2.5.13"
  }
}
```

`package.json`と同階層に`node_modules`が出来ており、その中にインストールしたvueが入っています
今後パッケージをインストールしたら全て`node_modules`内に格納されることになります

またnpm v5を使っている人は`package-lock.json`というファイルが作られていると思います
npm v5はNode.js v8からバンドルされたので、もし古いNode.jsを使っていたらnpmも古いのでこのファイルは作られません

このファイルを置いておくとパッケージのバージョンを固定することができます
`package-lock.json`を開くと、以下のようにパッケージの依存関係が定義されており、
`npm install`を実行した際に`package-lock.json`の依存関係を基にパッケージをインストールします
`package-lock.json`はGitHub等で作ったプロジェクトで管理した方が良いでしょう

```js:package-lock.json
{
  "name": "vuejs-parcel-hands-on",
  "version": "1.0.0",
  "lockfileVersion": 1,
  "requires": true,
  "dependencies": {
    "vue": {
      "version": "2.5.13",
      "resolved": "https://registry.npmjs.org/vue/-/vue-2.5.13.tgz",
      "integrity": "sha512-3D+lY7HTkKbtswDM4BBHgqyq+qo8IAEE8lz8va1dz3LLmttjgo0FxairO4r1iN2OBqk8o1FyL4hvzzTFEdQSEw=="
    }
  }
}
```

このファイルが無い場合、`package.json`からパッケージをインストールするので
`"vue": "^2.5.13"`のように`^`を使った指定をしている場合に
環境ごとにインストールされるバージョンが変わる可能性があります

`^`はメジャーバージョンアップ以外のバージョンアップ（マイナーとパッチ）の際は、
バージョンを書き換えなくても最新をインストールできるようにする指定の仕方です

また`npm update`を実行すれば、`package.json`を基にパッケージを更新するので
バージョンを`^`を使って指定している場合で
そのパッケージのマイナーバージョンやパッチバージョンが上がっている場合は
`package-lock.json`も最新に書き換わります

---

## Vue.jsを作成
次に以下の3ファイルを作成してください
簡単なVue.jsを記述しましょう

```sh
$ touch index.html index.js App.vue
```

---

`index.html`を開いて、以下を記述してください

```html:index.html
<html>

<body>
  <div id="app">
    <script src="./index.js"></script>
  </div>
</body>

</html>
```

次のページで作成する`index.js`を呼んでるだけです

---

`index.js`を開いて、以下を記述してください

```js:index.js
import Vue from 'vue'
import App from './App.vue'

new Vue({
  render: h => h(App)
}).$mount('#app')
```

Vueのインスタンスを作成して、先程の`index.html`で記述した`id="app"`の要素をマウント（$mount）します

renderオプションでは、次ページで作成する`App.vue`をレンダリング（描画）しています

```js
render: h => h(App)
```

上記は、以下の短縮形となります

```js
render: function (createElement) {
  return createElement(App)
}
```

`createElement`はVNodeを返却します
VNodeで形成されるツリー構造がVDOMであり、VDOMから実DOMが作られることになります
`h`は`createElement`のエイリアスです

---

`App.vue`を開いて、以下を記述してください

```html:App.vue
<template>
  <div>
    {{ message }}
  </div>
</template>
<style></style>
<script>
export default {
  name: 'App',
  data() {
    return {
      message: 'Hello Vue!'
    }
  }
}
</script>
```

templateタグにHTML
scriptタグにJavaScript
styleタグにCSS
を記述します

---

## Vue.jsをバンドル（ビルド）
Vue.jsをバンドル（ビルド）しましょう
Parcelというモジュールバンドラーを使って、Vue.jsをバンドルします
以下３つのパッケージをインストールしてください

```sh
$ npm install --save-dev parcel-bundler parcel-plugin-vue babel-preset-env
```

■ parcel-bundler
Parcel本体

■ parcel-plugin-vue
ParcelのVue用プラグイン

■ babel-preset-env
ES201Xの新しい文法で記述したコードをブラウザで使えるようにトランスパイル（ブラウザが理解できる古いコードに変換）してくれるもの

vueをインストールした時と違い、`--save-dev`というオプションを付けてインストールしました
`package.json`を開くと以下のように記述されています

```json:package.json
...
"dependencies": {
  "vue": "^2.5.13"
},
"devDependencies": {
  "babel-preset-env": "^1.6.1",
  "parcel-bundler": "^1.5.1",
  "parcel-plugin-vue": "^1.5.0"
}
```

今回のハンズオンで皆さんがこれらのパッケージを使う分には、`dependencies`に書いても、`devDependencies`に書いても特に違いはありません
`vue`も`devDependencies`に書いたパッケージも全て`node_modules`にインストールされます

dependenciesとdevDependenciesを意識しないといけないのは、今後自作したライブラリをNPMパッケージとして公開した場合です
その公開した自作パッケージを他の人が`npm install 自作パッケージ`した場合、dependenciesのパッケージのみインストールされます
上記の`package.json`だと`vue`だけインストールされて、ParcelとBabelのパッケージはインストールされません

例えば`parcel-bundler`を`dependencies`に書いてしまうと、他の人がParcelを使ってるか分からないのに、
強制的にインストールされてしまうので気を付けましょう
ちなみにESLintを使っていれば、「import/no-extraneous-dependencies」というルールで防ぐこともできます

---

Babelの設定ファイルを追加しましょう

```sh
$ touch .babelrc
```

`.babelrc`を開いて、以下を記述

```json:.babelrc
{
  "presets": ["env"]
}
```

Parcelは勝手に`.babelrc`等の設定ファイルを読み込んで処理してくれます
webpackのように`babel-loader`を設定ファイルに書いたりする必要はありません

本当は対象ブラウザのバージョン等をこのファイルに記載するのですが、
今回は省略します
ここで対象ブラウザを絞ることで、無駄なトランスパイルを無くすことができます
（既に最新ブラウザで動くES文法は変換する必要がなくなる為）

---

最後に`npm-scripts`にparcelコマンドを記載しましょう
`package.json`を開いて、scriptsに以下を記載してください

```json
...
"scripts": {
  "start": "parcel index.html -o"
},
...
```

`-o`オプションはバンドル後にブラウザを起動するオプションです
ParcelはHMR（ブラウザをリロードせずに更新する）がデフォルトオンでビルトインされています

---

Vue.jsをバンドルしましょう！

```sh
$ npm start
```

---

ブラウザが開いて、画面に「Hello Vue!」が表示されたら成功です

ハンズオンの１つ目の目標が達成できました

---

## Parcelについて
フロントエンドのビルドと聞くと、なんとなく環境構築や設定が大変でとっつきにくいイメージがあったけど、
簡単にビルドできたので、ビックリしませんでしたか？

Parcelは簡単に言うと、設定ファイルが不要のモジュールバンドラーです
https://github.com/parcel-bundler/parcel

去年末の１２月にリリースされ、まだ2ヶ月程ですが、既にGitHubスターが18,000を超えています

特徴は以下です
■ 設定ファイルが不要
webpackやFuseboxなどのモジュールバンドラーと違い、設定ファイルが不要

■ バンドルスピードが速い
`.cache`というディレクトリが出来ていると思いますが、デフォルトでキャッシュを利用しています
（もちろんコマンドオプションでキャッシュをオフにすることも出来ます）
webpackでも`hard-source-webpack-plugin`や`cache-loader`で重いloaderをキャッシュすれば速くなりますが、
設定不要でデフォルトで使えるのは嬉しいかもしれない

■ 必要な機能が予めビルトインされている
webpackが設定ファイルにloaderとかPluginとかを書いて、サードパーティーライブラリを使って処理するのに対して、
Parcelでは、Parcel本体にビルトインされている機能と`parcel-plugin-xxx`というプラグインを使ってバンドルします

設定ファイルが無く、自分でスクリプトを書けない為、
Parcelで出来ないことは、諦めるしかありません
さらに、新しいモジュールバンドラーということで、ちゃんとバンドルできないことが多々あります
前にAngular5で簡単なコードをParcelでバンドルした時は、糖衣構文が動かなかったりしました

なので、実用で使うにはまだ厳しいイメージですが、
将来が期待できるモジュールバンドラーだと思います

---

## Firebase Hostingの設定
Googleアカウントが用意できたら、以下にアクセスしてください

https://console.firebase.google.com

---

プロジェクトを追加をクリック

<img width="543" alt=" 2018-01-30 18.44.29.png" src="https://qiita-image-store.s3.amazonaws.com/0/168197/b257ae67-f339-5a07-ca07-8b93a47607ca.png">

---

プロジェクト名と国/地域を入力して、プロジェクトを作成をクリック

<img width="379" alt=" 2018-01-30 18.47.54.png" src="https://qiita-image-store.s3.amazonaws.com/0/168197/058a0479-f1e0-ea3d-ebf3-47117210fb01.png">

少し待つとプロジェクトが作成され、ようこそページが表示されます
また、GmailにWelcomeメールが来るかと思います

---

ここからは`firebase-tools`をインストールして、ターミナル上で操作します
`firebase-tools`をグローバルインストールしてください

```sh
$ npm install -g firebase-tools
```

---

Firebaseにログインします

```sh
$ firebase login
```

最初の質問にYかnで答えると、Googleアカウント選択画面がブラウザで開くので、アカウントを選択してください

---

以下を許可してください

<img width="316" alt=" 2018-01-30 19.03.09.png" src="https://qiita-image-store.s3.amazonaws.com/0/168197/2cad624c-b47b-9065-c1d0-b29b4b683e3d.png">

---

再度以下を実行し、自分のGmailアドレスが表示されたらログイン成功です

```sh
$ firebase login
```

---

Firebase Hostingの初期設定をしましょう
作業ディレクトリは変えないで以下を実行してください

```sh
$ firebase init
```

---

Hostingを選択

<img width="687" alt=" 2018-01-30 19.09.55.png" src="https://qiita-image-store.s3.amazonaws.com/0/168197/da92329e-7aa1-cb9b-08b1-54f60995f640.png">

---

先程作成したプロジェクトを選択

<img width="280" alt=" 2018-01-30 19.10.19.png" src="https://qiita-image-store.s3.amazonaws.com/0/168197/2312f052-95ac-ad27-753f-fdb810d18e51.png">

作業ディレクトリ内に`.firebaserc`と`firebase.json`が作られたら成功です

今回は触りませんが、`.firebaserc`を開いてみると以下のようになってます

```json:.firebaserc
{
  "projects": {
    "default": "vuejs-parcel-hands-on"
  }
}
```

このファイルでプロジェクトのエイリアスを管理することができます
今はdefault projectとして、先程作成したプロジェクトが指定されているだけですが、
新たにFirebaseプロジェクトを作成して、以下のように実行すると

```sh:例
$ firebase use --add
? Which project do you want to add? test-f01a2
? What alias do you want to use for this project? (e.g. staging) test
```

以下の`test`のように新しく作成したプロジェクトとそのエイリアスが書き加えられます

```json:.firebaserc
{
  "projects": {
    "default": "vuejs-parcel-hands-on",
    "test": "test-f01a2"
  }
}
```

---

## Firebase デプロイ
Firebaseの準備ができたので、デプロイしましょう
`firebase.json`を開いて、以下を記述してください

```json:firebase.json
{
  "hosting": {
    "public": "dist"
  }
}
```

Firebase Hostingへデプロイするディレクトリを指定します
デフォルト値は`public`なのですが、今回Parcelでバンドルしたファイルは`dist`ディレクトリに置かれるため、
`dist`を指定します

---

`npm-scripts`に本番環境向けのParcelコマンドとデプロイコマンドを記載しましょう
`package.json`を開いて、scriptsに以下の`build`と`deploy`を追記してください

```json
...
"scripts": {
  "start": "parcel index.html -o",
  "build": "parcel build index.html --public-url .",
  "deploy": "firebase deploy"
},
...
```

■ parcel build
本番向けのビルドでminifyしてdist配下にbundleファイルを出力します
`--public-url .`のオプションは`index.html`で`index.js`を読み込む際にパスが`/dist/index.js`になってしまうのを防ぐために入れてます

■ firebase deploy
`firebase.json`の情報を基にFirebaseへデプロイします

---

ビルド！

```sh
$ npm run build
```

---

デプロイ！

```sh
$ npm run deploy
```

---

完了！

---

ターミナル上で、Hosting URLが出ているので、確認してみましょう
以下のようなURLになっていると思います

https://[自分のFirebaseのプロジェクトID].firebaseapp.com

画面に「Hello Vue!」が表示されたら成功です

個人サイトくらいであれば、Firebaseの無料枠で十分だし、
SSL+HTTP/2に対応したサイトを簡単に作れるので、とても便利です

---

お疲れ様でした:tada: