もがき系プログラマの日常

もがき系エンジニアの勉強したこと、日常のこと、気になっている技術、備忘録などを紹介するブログです。

Vue.js入門 基礎から実践アプリケーション開発までを読んだ vol.6

はじめに

こんにちは。

Kojirockの1人アドベントカレンダー Advent Calendar 2018の6日目の記事です。

以下の本を読んで勉強できたことを記載していきます。

今回は第6章です。

Vue.js入門 基礎から実践アプリケーション開発まで

Vue.js入門 基礎から実践アプリケーション開発まで

6章 単一ファイルコンポーネントによる開発

目次

6.1 ツールのインストール

6.2 単一ファイルコンポーネントとは

6.3 単一ファイルコンポーネントの仕様

6.4 単一ファイルコンポーネントのビルド

6.5 単一ファイルコンポーネントの動作を体験する

6.6 単一ファイルコンポーネントの機能





「単一ファイルコンポーネント」という単語がとにかく多いw

6.1 ツールのインストール

Vue CLIを利便性について述べられています。

実際にVue CLIを使用してのインストールは、ここでは述べられていないので、実践してみます。

$ npm install -g @vue/cli
+ @vue/cli@3.2.1

# デフォルト設定か、手動インストールかを選択します
$ vue create test
Vue CLI v3.2.1
? Please pick a preset: (Use arrow keys)
  default (babel, eslint)     ← デフォルト設定
❯ Manually select features    ← 手動設定(一旦こっち選んで見る)

# 同時にインストールしたい機能を選択します
? Check the features needed for your project: (Press <space> to select, <a> to toggle all, <i> to invert selection)
❯◉ Babel
 ◯ TypeScript
 ◯ Progressive Web App (PWA) Support
 ◉ Router ← この2つにチェックを入れます 
 ◉ Vuex   ← この2つにチェックを入れます
 ◯ CSS Pre-processors
 ◉ Linter / Formatter
 ◯ Unit Testing
 ◯ E2E Testing
 
# Vue Routerのヒストリーモードを使用するかどうかを選択します
? Use history mode for router? (Requires proper server setup for index fallback in production) (Y/n) Yes

# LinterとFormatterの設定を選択します
? Pick a linter / formatter config: (Use arrow keys)
  ESLint with error prevention only
  ESLint + Airbnb config
❯ ESLint + Standard config  ← この辺はよくわからなかったので、Standardにしておきます
  ESLint + Prettier
  
# 追加のLint機能を選択します(Lintの実行タイミング)
? Pick additional lint features: (Press <space> to select, <a> to toggle all, <i> to invert selection)
❯◉ Lint on save
 ◯ Lint and fix on commit
 
# Babel、PostCSS、ESLint等の設定をどこで行うかを選択します
? Where do you prefer placing config for Babel, PostCSS, ESLint, etc.? (Use arrow keys)
❯ In dedicated config files ← それぞれの設定ファイルで行う
  In package.json

# 次回からもこの設定を使うために、設定を保存するかどうかを選択します
? Save this as a preset for future projects? (y/N) Yes

# どこに保存するかを入力します
? Save preset as:
./setteing



Vue CLI v3.2.1
✨  Creating project in /path/to/test.
🗃  Initializing git repository...
⚙  Installing CLI plugins. This might take a while...

yarn install v1.12.3
info No lockfile found.
[1/4] 🔍  Resolving packages...
[2/4] 🚚  Fetching packages...
[3/4] 🔗  Linking dependencies...

success Saved lockfile.
✨  Done in 135.72s.
🚀  Invoking generators...
📦  Installing additional dependencies...

yarn install v1.12.3
[1/4] 🔍  Resolving packages...
[2/4] 🚚  Fetching packages...
[3/4] 🔗  Linking dependencies...
[4/4] 📃  Building fresh packages...

success Saved lockfile.
✨  Done in 42.73s.
⚓  Running completion hooks...

📄  Generating README.md...

🎉  Successfully created project test.
👉  Get started with the following commands:

 $ cd test
 $ yarn serve

$ cd test
$ yarn serve
yarn run v1.12.3
$ vue-cli-service serve
 INFO  Starting development server...
 98% after emitting CopyPlugin

 DONE  Compiled successfully in 3536ms                                                                                                                                                                              17:23:20


  App running at:
  - Local:   http://localhost:8080/
  - Network: http://192.168.7.6:8080/

  Note that the development build is not optimized.
  To create a production build, run yarn build.

f:id:kojirooooocks:20181206223015p:plain

OKです。簡単ですね。

6.2 単一ファイルコンポーネントとは

単一ファイルコンポーネントの簡単な説明が述べられています。 本書の文を引用します。

Vue.jsのコンポーネントを単独のファイルとして作成する機能です。

単一ファイルコンポーネントという呼び方の他に、 SFC(Single File Component)や、 Vueコンポーネントと呼ぶこともあるようです。

Vue CLIで作成されたHelloWorld.vueの中身がわかりやすいと思いますので、記載しておきます。

<template>
  <div class="hello">
    <h1>{{ msg }}</h1>
    <p>
      For a guide and recipes on how to configure / customize this project,<br>
      check out the
      <a href="https://cli.vuejs.org" target="_blank" rel="noopener">vue-cli documentation</a>.
    </p>
    <h3>Installed CLI Plugins</h3>
    <ul>
      <li><a href="https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-babel" target="_blank" rel="noopener">babel</a></li>
      <li><a href="https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-eslint" target="_blank" rel="noopener">eslint</a></li>
    </ul>
    <h3>Essential Links</h3>
    <ul>
      <li><a href="https://vuejs.org" target="_blank" rel="noopener">Core Docs</a></li>
      <li><a href="https://forum.vuejs.org" target="_blank" rel="noopener">Forum</a></li>
      <li><a href="https://chat.vuejs.org" target="_blank" rel="noopener">Community Chat</a></li>
      <li><a href="https://twitter.com/vuejs" target="_blank" rel="noopener">Twitter</a></li>
      <li><a href="https://news.vuejs.org" target="_blank" rel="noopener">News</a></li>
    </ul>
    <h3>Ecosystem</h3>
    <ul>
      <li><a href="https://router.vuejs.org" target="_blank" rel="noopener">vue-router</a></li>
      <li><a href="https://vuex.vuejs.org" target="_blank" rel="noopener">vuex</a></li>
      <li><a href="https://github.com/vuejs/vue-devtools#vue-devtools" target="_blank" rel="noopener">vue-devtools</a></li>
      <li><a href="https://vue-loader.vuejs.org" target="_blank" rel="noopener">vue-loader</a></li>
      <li><a href="https://github.com/vuejs/awesome-vue" target="_blank" rel="noopener">awesome-vue</a></li>
    </ul>
  </div>
</template>

<script>
export default {
  name: 'HelloWorld',
  props: {
    msg: String
  }
}
</script>

<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
h3 {
  margin: 40px 0 0;
}
ul {
  list-style-type: none;
  padding: 0;
}
li {
  display: inline-block;
  margin: 0 10px;
}
a {
  color: #42b983;
}
</style>

SFCは、HTML構成(template)と、ロジック(script)と、更にスタイル(style)がそれぞれのブロックに分かれて、一つのファイルの中に閉じ込めたものになります。

設計を気をつける必要がありますが、作り方によっては、使用方法が確立した、再利用性の高いコンポーネントが完成すると思います。

6.3 単一ファイルコンポーネントの仕様

SFCのそれぞれのブロックの説明が述べられています。

基本的に名前通りです。

  • <template>

    テンプレート(HTML要素)を記載します。

    コンポーネントのtemplateオプションと同じく、各種ディレクティブなどを記載できます。

  • <script>

    javascriptを記載します。

    つまりこのブロックがSFCのキモの部分可と思います。

    利用するためにはエクスポートが必要です。以下のようなコードです。

    html <script> export default { // 処理 } </script>

  • <style>

    対象のSFC内のコンポーネントに対してのスタイル(CSS)を記載します。

    lang指定をすることで、書くaltCSSを使用することもできるようです。

    <style lang="stylus">など。公式にも記載されいます。

6.4 単一ファイルコンポーネントのビルド

作成したSFCはvueファイルなので、ブラウザは解析できません。

ブラウザで確認可能にするため変換する必要があります。

このブロックでは、webpackやvue-loaderの説明が述べられています。

6.5 単一ファイルコンポーネントの動作を体験する

実際にSFCを作成してみて、動作を確認するブロックです。

router.jsにコードを追加します。

    {
      path: '/profile',
      name: 'profile',
      component: Profile
    }

src/views/Profile.vueを作成します。

<template>
  <div class="profile">
    <ProfileField name="kojirock" age="34" address="埼玉県"/>
  </div>
</template>

<script>
import ProfileField from '@/components/ProfileField.vue'
export default {
  name: 'profile',
  components: {
    ProfileField
  }
}
</script>

src/components/ProfileField.vueを作成します。

<template>
<table>
  <thead>
    <tr>
      <th>名前</th>
      <th>年齢</th>
      <th>住所</th>
    </tr>
  </thead>
  <tbody>
    <tr>{{ name }}</tr>
    <tr>{{ age }}</tr>
    <tr>{{ address }}</tr>
  </tbody>
</table>
</template>

<script>
export default {
  name: 'ProfileField',
  props: {
    name: String,
    age: Number,
    address: String
  }
}
</script>

<style>
thead tr {
  background-color: aquamarine;
}
</style>

これで、yarn serve を実行して http://192.168.7.6:8080/profile にアクセスします。

f:id:kojirooooocks:20181206223049p:plain

表示されました。

6.6 単一ファイルコンポーネントの機能

SFCのその他いろいろな機能の紹介がされていました。

1. 外部ファイルのインポート

template, script, style全てにおいて、importすることができます。

<template src="./aaa.html"></template>

<script src="./bbb.js"></script>

<style src="./ccc.css"></style>

2. スコープ付きCSSCSSモジュール

<style scoped> とすることで、そのSFC内の要素のみに適用されるstyleとして定義できます。

これは、最終的に排出されるcssファイルが1ファイルになるため、かぶらないための対応なのだと思います。

CSSモジュールは、<style module> という指定になります。

moduleの場合はスコープの指定の仕方が違うようです。

:class="$style.style_name" という感じで指定する形です。

終わりに

簡単な感じで終わっちゃいました。。。

次は自分的鬼門のVuexのところです。

がんばります。

Vue.js入門 基礎から実践アプリケーション開発までを読んだ vol.5

はじめに

こんにちは。

Kojirockの1人アドベントカレンダー Advent Calendar 2018の5日目の記事です。

以下の本を読んで勉強できたことを記載していきます。

今回は第5章です。

Vue.js入門 基礎から実践アプリケーション開発まで

Vue.js入門 基礎から実践アプリケーション開発まで

5章 Vue.jsの高度な機能

目次

5.1 トランジションアニメーション

5.2 スロット

5.3 カスタムディレクティブ

5.4 描画関数

5.5 ミックスイン

5.1 トランジションアニメーション

Vue.jsでのアニメーション方法が記載されています。

要素の表示・非表示をトリガーにして、CSS3、Web AnimationAPI、各アニメーションライブラリとの連携を行い、トランジションアニメーションを簡単に実装できるということでした。

アニメーション関連は自分は全く知識なかったので、正直この部分に関しては、内容理解しづらかったです。

<transition> タグで囲ったものが、アニメーションの対象となります。

以下の条件の際に、トランジションのクラスが要素に追加されるようです。

  1. v-ifの条件が変わった時
  2. v-showの条件が変わった時
  3. 動的コンポーネントのis属性値が変わった時

追加されるクラスは以下のとおりです。

クラス名 追加されるタイミング 削除されるタイミング
v-enter 要素が挿入される前 挿入のアニメーション開始時
v-enter-to 挿入のアニメーションの開始時 挿入のアニメーション終了後
v-enter-active 要素の挿入前 挿入のアニメーション終了時
v-leave 削除のアニメーションの開始前 削除のアニメーション開始時
v-leave-to 削除のアニメーションの開始時 削除のアニメーション終了後
v-leave-active 削除のアニメーションの開始前 削除のアニメーション終了後

実際に本書で紹介されているコードが以下になります。

<!DOCTYPE html>
<html>
<head>
    <title>Page Title</title>
    <script src="https://unpkg.com/vue@2.5.17"></script>
</head>
<body>
    <div id="app">
        <button @click="isShown = !isShown">表示切り替え</button>
        <transition>
          <p v-show="isShown">Hello World</p>
        </transition>
    </div>
    
    <script>
        new Vue({
            el: '#app',
            data: function() {
              return {
                isShown: false
              }
            }
        })
    </script>

    <style>
      .v-enter-active, .v-leave-active {
        transition: opacity 500ms ease-out;
      }

      .v-enter {
        opacity: 0;
      }

      .v-enter-to {
        opacity: 1;
      }

      .v-leave {
        opacity: 1;
      }

      .v-leave-to {
        opacity: 0;
      }
    </style>
</body>
</html>

f:id:kojirooooocks:20181204224559g:plain

f:id:kojirooooocks:20181204224631g:plain

↑のように、アニメーション時に先ほど紹介したクラスが着脱されてますね。

公式に乗っていた、以下の画像がとてもわかり易かったです。

f:id:kojirooooocks:20181204224704p:plain

その他にも、実践的なアニメーションの方法(javascriptへのフックなど)が紹介されています。

ただ、自分のフィールド的に、「まだ必要ないかな」と判断したので、こちらは読み飛ばしました。

5.2 スロット

以前の記事にも登場していたスロットの話です。

本書ではモーダルを表示するコンポーネントが例として取り上げられています。

モーダル内の内容がテキスト1文だけであれば、プロパティで渡すだけでもよいかもしれませんが、内容が複雑多岐に渡る場合、プロパティで全部渡すのは辛いです。

モーダル内の要素が動的に切り替えられることで、そのモーダルコンポーネントは再利用が効く優秀なコンポーネントになります。

スロットには2種類のタイプが存在します。

名前なしスロット

<!DOCTYPE html>
<html>
<head>
    <title>Page Title</title>
    <script src="https://unpkg.com/vue@2.5.17"></script>
</head>
<body>
  <div id="app">
    <ul>
      <li-item>味噌ラーメン</li-item>
      <li-item>塩ラーメン</li-item>
      <li-item>醤油ラーメン</li-item>
      <li-item>とんこつラーメン</li-item>
      <li-item>餃子</li-item>
      <li-item></li-item>
    </ul>
  </div>
    
  <script>
    new Vue({
      el: '#app',
      components: {
        LiItem: {
          template: `
            <li><slot>売り切れ</slot></li>
          `
        }
      }
    })
  </script>
</body>
</html>

f:id:kojirooooocks:20181204224728p:plain

templateの中にある<slot>タグで囲った中が、コンテンツを差し込まれる場所となっています。

各ラーメンと餃子に関しては、コンテンツ(今回の場合味噌ラーメンなどのテキスト)が指定されているため、HTMLでは、 <li>味噌ラーメン</li>になります。

コンテンツが指定されていない最後の

  • 要素では、で指定している売り切れが差し込まれます。

    イメージ的に、親からデータが渡されなかった場合のデフォルト要素みたいな感じでしょうか。

    名前付きスロット

    前述の名前なしスロットは単一スロットといって、一つのコンテンツしか挿入させることが出来ませんでした。

    それ以外に、コンテンツを挿入したい場合は、この名前付きスロットを使います。

    使い方はそこまで変わらず、<slot> タグにname属性で名前をつけて、呼び出す際もそのnameを指定する形です。

    そうすることで狙ったスロットに挿入できます。

    <!DOCTYPE html>
    <html>
    <head>
        <title>Page Title</title>
        <script src="https://unpkg.com/vue@2.5.17"></script>
    </head>
    <body>
      <div id="app">
        <contents-body>
          <ul slot="pickup_item">
              <li>11111</li>
              <li>22222</li>
              <li>33333</li>
          </ul>
    
          <ul slot="discount_item">
            <li>現在商品はありません。</li>
          </ul>
    
          <ul slot="normal_item">
            <li>AAAAA</li>
            <li>BBBBB</li>
            <li>CCCCC</li>
            <li>DDDDD</li>
            <li>EEEEE</li>
            <li>FFFFF</li>
            <li>GGGGG</li>
            <li>HHHHH</li>
            <li>IIIII</li>
            <li>KKKKK</li>
            <li>LLLLL</li>
          </ul>
    
          <ul slot="soldout_item">
              <li>JJJJJ</li>
          </ul>
        </contents-body>
      </div>
        
      <script>
        new Vue({
          el: '#app',
          components: {
            ContentsBody: {
              template: `
                <div>
                  <h3>ピックアップ商品</h3>
                  <slot name="pickup_item">
                    <ul>
                      <li>AAAAA</li>
                      <li>BBBBB</li>
                      <li>CCCCC</li>
                    </ul>
                  </slot>
    
                  <h3>特別割引商品</h3>
                  <slot name="discount_item">
                    <ul>
                      <li>DDDDD</li>
                      <li>EEEEE</li>
                      <li>FFFFF</li>
                    </ul>
                  </slot>
    
                  <h3>通常商品</h3>
                  <slot name="normal_item">
                    <ul>
                      <li>GGGGG</li>
                      <li>HHHHH</li>
                      <li>IIIII</li>
                    </ul>
                  </slot>
    
                  <h3>売り切れ</h3>
                    <slot name="soldout_item">
                    <ul>
                      <li>JJJJJ</li>
                      <li>KKKKK</li>
                      <li>LLLLL</li>
                    </ul>
                  </slot>
                </div>
              `
            }
          }
        })
      </script>
    </body>
    </html>
    

    f:id:kojirooooocks:20181204224748p:plain

    こんな感じで複雑な差し替えも可能になりました。

    これはかなり使えそうです。

    5.3 カスタムディレクティブ

    v-ifやv-modelといった標準搭載のディレクティブでは扱いきれない複雑なものにたいして、独自のディレクティブを作成できる仕組みです。

    アプリケーション全体で使用できるグローバルディレクトリと、特定のコンポーネントでのみ使用できるカスタムディレクトリがあるようですが、基本的にディレクティブはコンポーネントに依存しないものなので、グローバルディレクトリで作成するのが良さそうです。

    こちらは、本書で紹介されていた例がとてもわかり易かったです。

    <!DOCTYPE html>
    <html>
    <head>
        <title>Page Title</title>
        <script src="https://unpkg.com/vue@2.5.17"></script>
    </head>
    <body>
      <div id="app">
        <img v-fallback-image src="./logo.png">
      </div>
        
      <script>
        Vue.directive('fallback-image', {
          bind: function(el) {
            el.addEventListener('error', () => {
              el.src = 'https://dummyimage.com/400x400/000/ffffff.png&text=no+image'
            })
          }
        })
    
        new Vue({
          el: '#app',
        })
      </script>
    </body>
    </html>
    

    f:id:kojirooooocks:20181204224803p:plain

    画像が読み込めない場合指定の画像をsrcにセットして、代わりに表示するようなロジックです。

    例題では、bindというフックを使用しましたが、その他別のフックもあります。

    • bind
    • inserted
    • update
    • componentUpdated
    • unbind

    本書ではそれぞれの説明とupdateフックによるサンプルコードが記載されてます。

    また、先程のコードの機能拡張版のコードも紹介されています。

    この拡張版のコードはより実践的なコードなので、すぐ使用できそうなものになっていると思いました。

    5.4 描画関数

    これも最初の方に少しだけ触ったものです。

    templateで指定せず、render()を使用するという方式です。

    以前は、どちらも使えます的な紹介でしたが、今回は実際に、描画関数を使用しないと書きづらい例が載せられていました。

    以下が本書で紹介されていた例です。

    template使用時

    <!DOCTYPE html>
    <html>
    <head>
        <title>Page Title</title>
        <script src="https://unpkg.com/vue@2.5.17"></script>
    </head>
    <body>
      <div id="app">
        <my-button href="https://vuejs.org">anchor</my-button>
        <my-button tag="span">span</my-button>
        <my-button>button</my-button>
      </div>
        
      <script>
        new Vue({
          el: '#app',
          components: {
            MyButton: {
              props: [
                'href', 'tag'
              ],
              template: `
                <a v-if="(!tag && href) || tag === 'a'" :href="href || '#'">
                  <slot></slot>
                </a>
                <span v-else-if="tag === 'span'">
                  <slot></slot>
                </span>
                <button v-else>
                  <slot></slot>
                </button>
              `
            }
          }
        })
      </script>
    </body>
    </html>
    

    render()使用時

    <!DOCTYPE html>
    <html>
    <head>
        <title>Page Title</title>
        <script src="https://unpkg.com/vue@2.5.17"></script>
    </head>
    <body>
      <div id="app">
        <my-button href="https://vuejs.org">anchor</my-button>
        <my-button tag="span">span</my-button>
        <my-button>button</my-button>
      </div>
        
      <script>
        new Vue({
          el: '#app',
          components: {
            MyButton: {
              props: [
                'href', 'tag'
              ],
              render: function(h) {
                const tag = this.tag || (this.href ? 'a' : 'button')
                return h(tag, {
                  attrs: {
                    href: this.href || '#'
                  }
                }, this.$slots.default)
              }
            }
          }
        })
      </script>
    </body>
    </html>
    

    tenplate自体にv-ifなどの条件分岐ディレクティブが取り外されたので、確かに見通しは良さそうです。

    ちょっと実際に使ってみないとわからないですが、どの程度使うかは怪しいところです(すっかり忘れてtemplateだけ使っちゃいそう...)

    5.5 ミックスイン

    機能は一緒だけど、見た目が違うような場合にミックスインの機能が使えます。

    <!DOCTYPE html>
    <html>
    <head>
        <title>Page Title</title>
        <script src="https://unpkg.com/vue@2.5.17"></script>
    </head>
    <body>
      <div id="app">
        <red-ranger></red-ranger>
        <blue-ranger></blue-ranger>
        <yellow-ranger></yellow-ranger>
        <green-ranger></green-ranger>
        <pink-ranger></pink-ranger>
      </div>
        
      <script>
        const Attack = {
          methods: {
            attack: function() {
              alert(`必殺!! ${this.special}ーーーー!!`)
            }
          }
        }
    
        const RedRanger = {
          data: function() {
            return {
              special: 'レッドスパーク',
            }
          },
          mixins: [Attack],
          template: '<div><span>アカレンジャー参上!</span><button @click="attack">必殺技</button></div>'
        }
        const BlueRanger = {
          data: function() {
            return {
              special: 'ブルーチェリー',
            }
          },
          mixins: [Attack],
          template: '<div><span>アオレンジャー参上!</span><button @click="attack">必殺技</button></div>'
        }
        const YellowRanger = {
          mixins: [Attack],
          methods: {
            attack: function() {
              alert('カレーが切れて力が出ないー...')
            }
          },
          template: '<div><span>キレンジャー参上!</span><button @click="attack">必殺技</button></div>'
        }
        const GreenRanger = {
          data: function() {
            return {
              special: 'ミドメラン',
            }
          },
          mixins: [Attack],
          template: '<div><span>ミドレンジャー参上!</span><button @click="attack">必殺技</button></div>'
        }
        const PinkRanger = {
          mixins: [Attack],
          methods: {
            attack: function() {
              alert('モモレンジャーは敵に捕まっている!!')
            }
          },
          template: '<div><span>モモレンジャー...?</span><button @click="attack">必殺技</button></div>'
        }
    
        new Vue({
          el: '#app',
          components: {
            RedRanger,
            BlueRanger,
            YellowRanger,
            GreenRanger,
            PinkRanger
          }
        })
      </script>
    </body>
    </html>
    

    ちょっと使い方あってるか微妙でしたが、、、

    mixins で、使用したいミックスインを追加します。

    今回の例だと、attackというメソッドを持つミックスインをそれぞれのコンポーネントに追加してます。

    モモレンジャーキレンジャーにもミックスインを追加してますが、attack()というメソッドを追加して、ミックスインを上書きしています。

    終わりに

    かなり実践的な章でした。

    とくにスロットとミックスインはすごく使えそうです。

    とりあえず、今回はこれで終わり。

  • Vue.js入門 基礎から実践アプリケーション開発までを読んだ vol.4

    はじめに

    こんにちは。

    Kojirockの1人アドベントカレンダー Advent Calendar 2018の4日目の記事です。

    以下の本を読んで勉強できたことを記載していきます。

    今回は第4章です。

    Vue.js入門 基礎から実践アプリケーション開発まで

    Vue.js入門 基礎から実践アプリケーション開発まで

    4章 Vue Routerを活用したアプリケーション開発

    目次

    4.1 Vue Routerによるシングルページアプリケーション

    4.2 ルーティングの基礎

    4.3 実践的なルーティングのための機能

    4.4 サンプルアプリケーションの実装

    4.5 Vue Routerの高度な機能

    4.1 Vue Routerによるシングルページアプリケーション

    こちらでは、Vue Routerの概要を紹介してくれています。

    簡単な使い方としては、以下のような定義です。

    new VueRouter({
        routes: [
            {
                path: '/profile',
                component: Profile
            },
            {
                path: '/about',
                component: About
            }
        ]
    })
    

    該当するpathに応じて指定したコンポーネントを表示するという具合のようです。

    その他の便利な機能があるので、この後出てくることを期待しておきます。

    ちなみに、自分はSPAのwebアプリケーション開発は経験あるのですが、残念ながらバックエンド側だったので、フロントエンド側はノータッチでした。

    この辺は興味津々です。

    4.2 ルーティングの基礎

    こちらでは、前述したRouterの簡単な定義を使用して、実際に動くものの紹介がありました。

    <!DOCTYPE html>
    <html>
    <head>
        <title>Page Title</title>
        <script src="https://unpkg.com/vue@2.5.17"></script>
        <script src="https://unpkg.com/vue-router@3.0.1"></script>
    </head>
    <body>
        <div id="app">
            <router-link to="/">トップページ</router-link>
            <router-link to="/news">ニュース</router-link>
            <router-link to="/profile">プロフィール</router-link>
            <router-view></router-view>
        </div>
    
        <script>
            const router = new VueRouter({
                routes: [
                    {
                        path: '/',
                        component: {
                            template: '<div style="background:red;">トップページ</div>'
                        }
                    },
                    {
                        path: '/news',
                        component: {
                            template: '<div style="background:green;">ニュースページ</div>'
                        }
                    },
                    {
                        path: '/profile',
                        component: {
                            template: '<div style="background:yellow;">プロフィールページ</div>'
                        }
                    }
                ]
            })
    
            new Vue({
                el: '#app',
                router: router
            })
        </script>
    </body>
    </html>
    

    f:id:kojirooooocks:20181203053013g:plain

    <router-link to="xxxx"> でroutesに定義したpathを定義することで、リンクを表示することが出来ます。

    componentが差し込まれる部分は <router-view> です。

    4.3 実践的なルーティングのための機能

    URLパラメータ

    /user/1 のようなURLで user_id=1 のユーザー情報を取得したりするような場合、Vue Routerではどのように実現するかを述べてくれています。

    以下のような感じで対応します。

    <!DOCTYPE html>
    <html>
    <head>
        <title>Page Title</title>
        <script src="https://unpkg.com/vue@2.5.17"></script>
        <script src="https://unpkg.com/vue-router@3.0.1"></script>
    </head>
    <body>
        <div id="app">
            <ul>
                <li><router-link to="/">トップページ</router-link></li>
                <li><router-link to="/news/1">メンテンナスのお知らせ</router-link></li>
                <li><router-link to="/news/2">イベント開始</router-link></li>
                <li><router-link to="/news/3">あけましておめでとうございます</router-link></li>
            </ul>
            <router-view></router-view>
        </div>
    
        <script>
            const newsComponent = Vue.extend({
                template: `
                    <div style="background:skyblue;">
                        <h3>こちらはID: {{ $route.params.newsId }} のニュースです</h3>
                        <p v-if="$route.params.newsId == 1">メンテナンス開始しますよ!アクセスできないですよ</p>
                        <p v-else-if="$route.params.newsId == 2">新イベントが開始されますよ!楽しんでね☆</p>
                        <p v-else-if="$route.params.newsId == 3">あけましたね!正月イベントもうすぐはじまるよ!</p>
                    </div>
                `
            })
    
            const router = new VueRouter({
                routes: [
                    {
                        path: '/',
                        component: {
                            template: '<div style="background:red;">トップページ</div>'
                        }
                    },
                    {
                        path: '/news/:newsId',
                        component: newsComponent
                    },
                ]
            })
    
            new Vue({
                el: '#app',
                router: router
            })
        </script>
    </body>
    </html>
    

    f:id:kojirooooocks:20181203053049g:plain

    対応は簡単で、routesのpathでURLパラメータを渡す値を:で指定します。今回の例だと /news/:newsId です。

    URLから与えられた値は、 $route.params から、pathで指定した名前のまま取得できいます。 今回の例だと $route.params.newsId です。

    名前付きルート

    指定のルートに名前を付けられます。

    前述のコードのいち部を抜粋します。

    {
        path: '/news/:newsId',
        name: 'news',
        component: newsComponent
    },
    
    <ul>
        <li><router-link to="/">トップページ</router-link></li>
        <li><router-link :to="{ name: 'news', params: { newsId: 1 }}">メンテンナスのお知らせ</router-link></li>
        <li><router-link :to="{ name: 'news', params: { newsId: 2 }}">イベント開始</router-link></li>
        <li><router-link :to="{ name: 'news', params: { newsId: 3 }}">あけましておめでとうございます</router-link></li>
    </ul>
    

    ↑のコードは、URLパラメータで記載したコード同じ動きを実現します。

    router.pushでの遷移

    <router-link> を使わずに遷移することも出来ます。

    何らかのアクション後、自動的に遷移されるような場合に使いそうです。

    new Vue({
        el: '#app',
        router: router,
        methods: {
            linkTo: (newsId) => {
                router.push({ name: 'news', params: { newsId: newsId }})
            }
        }
    })
    
    <ul>
        <li><button v-on:click="linkTo(1)">ニュースID_1のページへ</button></li>
        <li><button v-on:click="linkTo(2)">ニュースID_2のページへ</button></li>
        <li><button v-on:click="linkTo(3)">ニュースID_3のページへ</button></li>
    </ul>
    

    f:id:kojirooooocks:20181203053130g:plain

    フック

    ページ遷移前後にフックを差し込めるようです。

    グローバルのフック関数

    全ページ遷移に設定できるフックで、 router.beforeEach でフックを差し込めます。

    router.beforeEach((to, from, next) => {
        if (to.name === 'news') {
            alert('ニュースページへ遷移します')
        }
        next() ← これを実行しなれば、ページ遷移が実行されない
    })
    
    ルート単位のフック関数

    特定のルート単位でフックを差し込む方法です。

    routesで定義する際に指定します。

    {
        path: '/news/:newsId',
        name: 'news',
        component: newsComponent,
        beforeEnter: (to, from, next) => {
            if (to.name === 'news') {
                alert('ニュースページへ遷移します')
            }
            next()                        
        }
    }
    

    4.4 サンプルアプリケーションの実装

    こちらでは、Vue Routerの機能を使って、実際にサンプルのSPAを実装します。

    こちらは写経させてもらいました。

    f:id:kojirooooocks:20181203053208g:plain

    今回、使った新たなものとしては、ライフサイクルの created を使用しています。

    created以前の記事でも紹介している通り、インスタンスが生成され、データが初期化された後に実行されます。

    ユーザー一覧遷移した際のcreated

    created: function() {
        this.fetchData()
    },
    methods: {
        fetchData: function() {
            this.loading = true
            getUsers(((err, users) => {
                this.loading = false
                if (err) {
                    this.error = err.toString()
                } else {
                    this.users = users
                }
            }).bind(this))
        }
    }
    

    また、watch オプションを使用しています。

    watch: {
        '$route': 'fetchData'
    },
    

    '$route' の変更をwatchして、ルーティングの変更に伴いfetchData()が実行されるようになります。

    4.5 Vue Routerの高度な機能

    サンプルアプリケーションで使用されてなかったVue Routerのその他の機能が紹介されています。

    便利そうだったのは、ネストしたルーティングでした。

    <!DOCTYPE html>
    <html>
    <head>
        <title>Page Title</title>
        <script src="https://unpkg.com/vue@2.5.17"></script>
        <script src="https://unpkg.com/vue-router@3.0.1"></script>
    </head>
    <body>
        <div id="app">
            <ul>
                <li><router-link to="/user/1">ユーザーID: 1</router-link></li>
                <li><router-link to="/user/2">ユーザーID: 2</router-link></li>
                <li><router-link to="/user/3">ユーザーID: 3</router-link></li>
            </ul>
            <router-view></router-view>
        </div>
    
        <script>
            const User = {
                template: `
                    <div class="user">
                        <h2>ユーザーIDは{{ $route.params.userId }}です。</h2>
                        <router-link :to="'/user/' + $route.params.userId + '/profile'">ユーザーのプロフィールページを見る</router-link>
                        <router-link :to="'/user/' + $route.params.userId + '/posts'">ユーザーの投稿ページを見る</router-link>
                        <router-view></router-view>
                    </div>
                `
            }
    
            const UserProfile = {
                template: `
                    <div class="user-profile">
                        <h3>こちらはユーザー{{ $route.params.userId }}のプロフィールページです。</h3>
                    </div>
                `
            }
    
            const UserPosts = {
                template: `
                    <div class="user-posts">
                        <h3>こちらはユーザー{{ $route.params.userId }}の投稿ページです。</h3>
                    </div>
                `
            }
    
            const router = new VueRouter({
                routes: [
                    {
                        path: '/user/:userId',
                        name: 'user',
                        component: User,
                        children: [
                            {
                                path: 'profile',
                                component: UserProfile
                            },
                            {
                                path: 'posts',
                                component: UserPosts
                            }
                        ]
                    }
                ]
            })
    
            new Vue({
                el: '#app',
                router: router,
            })
        </script>
    </body>
    </html>
    

    f:id:kojirooooocks:20181203053327g:plain

    終わりに

    サーバーサイド側のプログラムしかやってない自分にとって、とても革新的で、Vue Routerすごいなー!と思いました。

    自分で作るとめっちゃ楽しいです。

    さぁ次は第5章です。

    がんばります。

    Vue.js入門 基礎から実践アプリケーション開発までを読んだ vol.3

    はじめに

    こんにちは。

    Kojirockの1人アドベントカレンダー Advent Calendar 2018の3日目の記事です。

    以下の本を読んで勉強できたことを記載していきます。

    今回は第3章です。

    Vue.js入門 基礎から実践アプリケーション開発まで

    Vue.js入門 基礎から実践アプリケーション開発まで

    3章 コンポーネントの基礎

    目次

    3.1 コンポーネントとは何か

    3.2 Vueコンポーネントの定義

    3.3 コンポーネント間の通信

    3.4 コンポーネントの設計

    3.1 コンポーネントとは何か

    このブロックの序盤では、コンポーネントの重要性などが説明されています。

    以下の一文が重要なところかと思います。

    UIをコンポーネント化する大きなメリットは、その再利用可能性にあります。再利用することを意識してコンポーネントを設計することは、アプリケーションの開発のしやすさでメンテナンス性を高めます。

    再利用できるコンポーネントをうまく設計し作ることが、メリットを最大限受けるコツになってくるのかなと思いました。

    簡単な使用方法

    HTMLタグ名としてコンポーネント名を記載すれば、指定の場所に指定したコンポーネントの内容を流せます。 (カスタムタグ)

    <!DOCTYPE html>
    <html>
    <head>
        <title>Page Title</title>
        <script src="https://unpkg.com/vue@2.5.17"></script>
    </head>
    <body>
        <div id="app">
            <test-cm></test-cm>
        </div>
    
        <script>
            Vue.component('test-cm', {
                template: '<p>おす!おら悟空!{{ skill }}</p>',
                data: function() {
                    return {
                        skill: 'かめはめ波!'
                    }
                }
            })
    
            new Vue({
                el: '#app'
            })
        </script>
    </body>
    </html>
    
    => おす!おら悟空!かめはめ波!
    

    Vueコンポーネントの定義

    Vueコンポーネントの種類として、グローバルコンポーネントとローカルコンポーネントの2種類があり、それぞれ定義方法として、前述したカスタムタグ方式と、Vue.extend()を使用したサブコンストラクタ定義という定義方法があると説明されています。

    グローバルコンポーネント

    カスタムタグ定義

    前回のブロックで記載したコードが、グローバルコンポーネント(カスタムタグ定義)となります。

    サブコンストラクタ定義

    Vue.extend() を使用して、継承により定義できるようになります。

    <!DOCTYPE html>
    <html>
    <head>
        <title>Page Title</title>
        <script src="https://unpkg.com/vue@2.5.17"></script>
    </head>
    <body>
        <div id="app">
            <test-cm></test-cm>
        </div>
        
        <script>
            Vue.component('test-cm', {
                template: '<p>おす!おら悟空!<skill-field></skill-field></p>',
            })
    
            const SkillField = Vue.extend({
                template: '<span>必殺技はかめはめ波だ!</span>'
            })
            Vue.component('skill-field', SkillField)
    
            new Vue({
                el: '#app'
            })
        </script>
    </body>
    </html>
    
    => おす!おら悟空!必殺技はかめはめ波だ!
    

    ローカルコンポーネント

    特定のコンポーネント内でしか使用できないローカルコンポーネントの定義です。

    <!DOCTYPE html>
    <html>
    <head>
        <title>Page Title</title>
        <script src="https://unpkg.com/vue@2.5.17"></script>
    </head>
    <body>
        <div id="app">
            <test-cm></test-cm>
        </div>    
    
        <script>
            new Vue({
                el: '#app',
                components: {
                    'test-cm': {
                        template: '<p>おす!おら悟空!</p>',
                    }
                }
            })
        </script>
    </body>
    </html>
    
    => おす!おら悟空!
    

    グローバルで作るコンポーネントはページの大枠など大きな枠でのコンポーネントが対象になり、それに紐づくコンポーネントはそれぞれのローカルコンポーネントみたいな印象ですが、あってますか。。。?

    ページ全体でつかわれるnotificationとかはグローバルコンポーネントなのかな?

    もう少し実践してから考えてみます。

    また、前述した例では、template の値にhtml要素を書いていましたが、それ以外の定義方法が存在します。

    本書で紹介されている方法が以下でした。

    1. text/x-template
    2. インラインテンプレート
    3. 描画関数
    4. JSX
    5. 単一ファイルコンポーネント

    本ブロックでは、 text/x-templateと描画関数での定義方法について説明がされていました。

    text/x-template

    以下のように、表示するHTMLをtypeとidを指定したscriptタグで囲って定義する方法です。

    <!DOCTYPE html>
    <html>
    <head>
        <title>Page Title</title>
        <script src="https://unpkg.com/vue@2.5.17"></script>
    </head>
    <body>
        <div id="app">
            <test-cm></test-cm>
        </div>    
    
        <script type="text/x-template" id="hello">
            <p>おす!おら悟空!</p>
        </script>
    
        <script>
            new Vue({
                el: '#app',
                components: {
                    'test-cm': {
                        template: '#hello',
                    }
                }
            })
        </script>
    </body>
    </html>
    
    => おす!おら悟空!
    

    描画関数

    render()という関数でtemplateの代替として使えます。メソッドなので、v-ifやv-forなどディレクティブでむやみにコードが複雑化するのを防げる役割があるようです。

    <!DOCTYPE html>
    <html>
    <head>
        <title>Page Title</title>
        <script src="https://unpkg.com/vue@2.5.17"></script>
    </head>
    <body>
        <div id="app">
            <test-cm></test-cm>
        </div>    
    
        <script>
            new Vue({
                el: '#app',
                components: {
                    'test-cm': {
                        render: createElement => {
                            return createElement('h1', 'ベジータだよ!')
                        }
                    }
                }
            })
        </script>
    </body>
    </html>
    
    => ベジータだよ!
    

    コンポーネント命名規則についても触れられていました。

    ハイフン続きのケバブケースを選んでおくほうが無難ということです。

    3.3 コンポーネント間の通信

    親から子へデータを渡す場合は、propsでデータを渡すようです。

    この辺はReactもそうでした。

    <!DOCTYPE html>
    <html>
    <head>
        <title>Page Title</title>
        <script src="https://unpkg.com/vue@2.5.17"></script>
    </head>
    <body>
        <div id="app">
            <profile v-bind:name="name" v-bind:text="text"></profile>
            <profile v-bind:text="text"></profile>
        </div>    
    
        <script type="text/x-template" id="field">
            <div>
                <label>{{ name }}</label>
                <p>{{ text }}</p>
            </div>  
        </script>
    
        <script>
            Vue.component('profile', {
                props: {
                    name: {
                        type: String,
                        default: '名無し',
                        required: false,
                    },
                    text: {
                        type: String,
                        required: true,
                    }
                },
                template: '#field'
            })
    
            new Vue({
                el: '#app',
                data: {
                    name: 'kojirock',
                    text: 'こんにちは'
                }
            })
        </script>
    </body>
    </html>
    
    => kojirock
    こんにちは
    
    名無し
    こんにちは
    

    子 -> 親へのイベントの通知方法はいかが紹介されていました。

    こちらは、本書で紹介されているコードがわかりやすかったので、引用の意味で、記載します。

    <!DOCTYPE html>
    <html>
    <head>
        <title>Page Title</title>
        <script src="https://unpkg.com/vue@2.5.17"></script>
    </head>
    <body>
        <div id="fruits-counter">
            <div v-for="fruit in fruits">
                {{ fruit.name }}: <counter-button v-on:increment="incrementCartStatus()"></counter-button>
            </div>
            <p>合計: {{ total }}</p>
        </div>    
    
        <script>
            const counterButton = Vue.extend({
                template: '<span>{{counter}}個<button v-on:click="addToCart">追加</button></span>',
                data: function() {
                    return {
                        counter: 0
                    }
                },
                methods: {
                    addToCart: function() {
                        this.counter += 1
                        this.$emit('increment')
                    }
                }
            })
    
            new Vue({
                el: '#fruits-counter',
                components: {
                    'counter-button': counterButton
                },
                data: {
                    total: 0,
                    fruits: [
                        {name: '梨'},
                        {name: '苺'}
                    ]
                },
                methods: {
                    incrementCartStatus: function() {
                        this.total += 1
                    }
                }
            })
        </script>
    </body>
    </html>
    

    buttonが押された際のクリックイベントで、addToCartメソッドが実行されて、this.$emit('increment') によって、カスタムイベントが発火します。

    親の方では、v-onでincrementイベントをlistenしてます。

    なのでボタンを押すと親の incrementCartStatus() 実行される形になります。

    非常にわかりやすかったです。

    実際には、$parent や、 $children を使用することで、親や子のデータに直接アクセスることが可能のようです。

    ただ、本書ではこちらの方法は、使うべきではないと書かれています。親子間のやりとりにはpropsを使うことで統一すべきのようです。

    3.4 コンポーネントの設計

    こちらでは、実際にコンポーネントを作成する際の設計方法などが述べられていました。

    前の記事でも紹介したAtmic Design等が挙げられています。

    また、ヘッダーとログインフォームという2種類のコンポーネントの作成の例が挙げられています。

    ヘッダーコンポーネントでは、スロットコンテンツ という仕組みで作成する例が記載されています。

    本ブロックではスロットコンテンツについては、以下のように簡素にまとめられています。

    コンポーネントの中に、親から差し替えやすい部分を残すための仕組み

    実際になにか作ってみます。

    <!DOCTYPE html>
    <html>
    <head>
        <title>Page Title</title>
        <script src="https://unpkg.com/vue@2.5.17"></script>
    </head>
    <body>
        <div id="app">
            <page-header></page-header>
        </div>
    
        <script>
            var headerTemplate = `<span><slot name="header"><h1>AAAAA</h1></slot></span>`
    
            Vue.component('page-header', {
                template: headerTemplate
            })
    
            new Vue({
                el: '#app',
            })
        </script>
    </body>
    </html>
    

    この状態だと、表示は、「AAAAA」となります。

    page-header内の部分を以下のように変更します。

    <page-header>
        <h1 slot="header">BBBBB</h1>
    </page-header>
    

    そうすると、結果は、「BBBBB」となります。

    親からの呼び出し時に子の要素を上書きできるという感じのようです。

    スロットコンテンツについては、後の章で更に詳しく解説されるようなので、とりあえず現段階では上記のような理解でとどめておきます。

    最後に、簡単なログインフォームコンポーネントを作成する流れになります。

    このフォームではバリデートなどが入っていない簡単なものだったので、紹介されているコードにプラスして、バリデーションをいれてみました。

    <!DOCTYPE html>
    <html>
    <head>
        <title>Page Title</title>
        <script src="https://unpkg.com/vue@2.5.17"></script>
    </head>
    <body>
        <div id="login-example">
            <user-login></user-login>
        </div>    
    
        <script type="text/x-template" id="login-template">
            <div>
                <div>
                    <input type="text" name="userId" placeholder="ログインID" v-model="userId" />
                    <span>{{ errors.userId }}</span>
                </div>
                <div>
                    <input type="password" name="password" placeholder="パスワード" v-model="password" />
                    <span>{{ errors.password }}</span>
                </div>
                <button @click="login()">ログイン</button>
            </div>
        </script>
    
        <script>
            Vue.component('user-login', {
                template: '#login-template',
                data: () => {
                    return {
                        userId: '',
                        password: '',
                        errors: {
                            userId: '',
                            password: '', 
                        }
                    }
                },
                methods: {
                    isValid: function() {
                        this.errors.userId = ''
                        this.errors.password = ''
                        if (this.password.length === 0) {
                            this.errors.password = 'パスワードが未入力です。'
                        }
    
                        if (this.userId.length === 0) {
                            this.errors.userId = 'ユーザーIDが未入力です。'
                        }
    
                        return (this.errors.userId.length === 0 && this.errors.password.length === 0)
                    },
    
                    login: function() {
                        if (!this.isValid()) {
                            return false;
                        }
    
                        auth.login(this.userId, this.password)
                    }
                }
            })
    
            var auth = {
                login: (id, pass) => {
                    window.alert(`userId: ${id}\npassword: ${pass}`)
                }
            }
    
            new Vue({
                el: "#login-example"
            })
        </script>
    </body>
    </html>
    

    読み進めると、今後もしかしたらもっとスマートなやり方が出てくるかもしれません。

    終わりに

    とりあえず3章まで終わりました。

    やっとコード出てきて、学んでる感出てきました。

    もっと複数コンポーネントを使うような場面が読み進めると出てくるのかな?

    続けてがんばりますー。

    Vue.js入門 基礎から実践アプリケーション開発までを読んだ vol.2

    はじめに

    こんにちは。

    Kojirockの1人アドベントカレンダー Advent Calendar 2018の2日目の記事です。

    以下の本を読んで勉強できたことを記載していきます。

    今回は第2章です。

    Vue.js入門 基礎から実践アプリケーション開発まで

    Vue.js入門 基礎から実践アプリケーション開発まで

    2章 Vue.jsの基本

    目次

    2.1 Vue.jsでUIを構築する際の考え方

    2.2 Vue.jsの導入

    2.3 Vueオブジェクト

    2.4 Vueインスタンスのマウント

    2.5 UIのデータ定義(data)

    2.6 テンプレート構文

    2.7 フィルタ(filters)

    2.8 算出プロパティ(computed)

    2.9 ディレクティブ

    2.10 ライフサイクルフック

    2.11 メソッド(methods)

    2.1 Vue.jsでUIを構築する際の考え方

    ここでは、旧来のjQueryによるUI構築と、Vue.jsでのUI構築の差が述べられていました。

    実際、イベントや要素が少ない場面では、jQueryとVue.jsに大きな差は生まれないのですが、複数のイベントによる複雑な要素変更があるページでは、jQueryによる構築だと辛い場面が出てきます。

    こちらの要素は非表示にして、こちらの要素は表示して、こちらの要素はこのinputフォームをdisabledにして...と多種多様な要素の変更があった場合、各イベントリスナーからそれらの要素の変更を追加・削除などをしていく必要があります。

    簡単にメンテナンス性が失われてしまうようになります。

    実際昔から動いているjQuery中心のwebアプリケーションとかまさに↑の状態なんじゃないでしょうか。

    「ここのイベントいらなそうだけど、、、でも消したら要素変わっちゃいそう。。。」

    とかおっかなびっくり触りつつみたいなことも経験あると思います(自分はありました)

    Vue.jsによるUI構築は、イベントと要素の間に「状態」が挟まる形になるということです。

    前述したとおり、要素数やイベント数が少ない場合は恩恵が感じられないかもしれないですが、多くなってきたときに効果が実感できるということです。

    実際のコードなどはこのあと出てくると思いますが、現段階での自分の認識では、jQueryでの運用は、各イベントと要素が密接につながっているため、どちらも切り離せず各々を考慮しながら開発しなければいけないが、Vue.jsでの運用は、「状態」が間に挟まることにより、各イベント(アクション)と、要素の各変更を分けて考えることができるというところにとどめておきます。

    2.2 Vue.jsの導入

    このブロックからは実際にコードを打ちつつ進めていく形になります

    一番簡単な導入方法としてscriptタグで読み込む手法が紹介されています。

    以下がその例です。

    <script src="https://unpkg.com/vue@2.5.17"></script>
    

    jsFiddleでやったのはこちら。

    f:id:kojirooooocks:20181201034748p:plain

    上記の例はあくまでテストとして、紹介されています。

    以下のようなscriptタグでの読み込みは、SPAのような複雑なwebアプリケーションを構築する場合では使われず、今回のような学習用や、一部のページのみに適用したい場合に使用されるようです。

    実際にサイト全体に適用する場合は、前回の記事でも紹介した Vue CLIなどを使用して周辺ツールを一緒にインストールしたほうが懸命です。

    2.3 Vueオブジェクト

    前述したscripttタグでの読み込みで、グローバル変数Vueが使用可能になります。

    実際に使用する際は、以下のような形になります。

    var vm = new Vue({
    
    })
    

    コンストラクタの引数には、オブジェクトとしていろいろな設定値を渡します。

    本書で紹介されているものは、主要なものとして以下を紹介してくれています。

    オプション名 内容
    data UIの状態・データ
    el Vueインスタンスをマウントする要素
    filters データを文字列と整形する
    methods イベントが発生したときなどの振る舞い
    computed データから派生して算出される値

    その他にも様々なオプションがあるようなので、公式の情報を載せておきます。

    2.4 Vueインスタンスのマウント

    マウントの説明として本書では以下のように説明されています。

    マウントとは、既存のDOM要素をVue.jsが生成するDOM要素で置き換えることです。

    マウント自体は、前述のオプションオブジェクトで指定することもできるし、あとから指定することも可能とのことです。

    • 先に指定する場合

    f:id:kojirooooocks:20181201034806p:plain

    • あとから指定する場合

    f:id:kojirooooocks:20181201034817p:plain

    この2つの使い分けとしては、マウント対象の要素が非同期通信などであとから追加される場合に、あとから追加する方法をとるようです。

    2.5 UIのデータ定義(data)

    UIの「状態」となるデータのオブジェクトを指定します。

    dataプロパティの値が変更されるたびにVue.jsがそれを自動で検知して表示を切り替えるようになります。

    例でもよく使われている一方向バインディングでそれはわかるかなと思います。

    <div id="app">
      <p>
        {{ message }}
      </p>
    </div>
    <script>
    new Vue({
      el: '#app',
      data: {
        message: 'こんにちは' ←ここの値を変えれば表示も変わる
      }
    })
    </script>
    

    dataはあくまでデータそのものを置いて、関数などはmethodsやcomputedの仕事として分離すべきということのようです。

    次に本書では、consoleでのVueインスタンスの状態確認方法が記載されています。

    Chrome DevToolでの確認方法なので、このあたりは流し読みしました。

    またVue.jsはデータの代入と参照はデータバインディングのため監視を行っています。

    なので、console上でdataを修正するとデータが変わり、表示が変わります。

    自分が書いた以前の記事でも試しています。

    2.6 テンプレート構文

    今までも使用してきた {{ }} を使ったものや、後述するその他の機能を総じてテンプレート構文と呼ぶようです。

    Vue.jsのテンプレート構文で重要なのは次の2つとなります。

    1. Mustache記法によるデータの展開
    2. ディレクティブによるHTML要素の拡張

    ディレクティブについては、後述されるようなので、このブロックではMustache記法によるデータ展開について中心に書かれています。

    テキストへの展開

    今まで使用してきた {{ }} のことを指しています。

    属性値の展開

    自分が書いた以前の記事の属性をバインディングするという項目が該当します。

    ↑で紹介している方法以外にも本書では、以下のように便利に使える方法も紹介されています。

    <script src="https://unpkg.com/vue@2.5.17"></script>
    <button id="b-button" v-bind:disabled=!canBuy">購入</button>
    <script>
    const vm = new Vue({
      el: '#b-button',
      data: {
        canBuy: false
      }
    })
    </script>
    

    単にテキストとして出す以外にも、disabledなどの条件としても扱えるようになるという感じです。 こちらの例は汎用性の高い使用方法だなと思いました。

    javascript式も展開できるようですが、おすすめはされていませんでした。

    複雑になるということもあるし、その仕事を行うのはmethodsやcomputedになるからだと思いました。

    2.7 フィルタ(filters)

    実際に保持しているdataとページに表示したいテキストは違う場合があると思います。

    例えば電話番号をdataで保存していて、表示する場合は-(ハイフン)をつけて表示したいときなどです。

    そういったときにこのfilterが使用されます。

    以下は本書で紹介されているfilterのメソッドを少し改良しました。

      <script src="https://unpkg.com/vue@2.5.17"></script>
      <div id="app">
        <label>電話番号</label>
        <span>{{ tel|telDelimiter }}</span>
      </div>
    
      <script>
      new Vue({
        el: '#app',
        data: {
          tel: '09012345678'
        },
        filters: {
          telDelimiter: function(value) {
            return value.replace(/(\d)(?=(\d{4})+$)/g, '$1-')
          }
        }
      })
      </script>
      => 電話番号 090-1234-5678
    

    フィルタは複数セットすることが出来ます。 {{ value | filter1 | filter2 }} といった感じです。

    表示するデータが何らかの金額だとすると、金額フォーマット(3桁区切りにカンマ)にしたあと、最終的に をつけるとかですかね?

    2.8 算出プロパティ(computed)

    今までも何回か登場していた算出プロパティです。

    こちらは以前の自分の記事でも紹介しています。

    内容的にもそこまで難しいものではないです。

    2.9 ディレクティブ

    テンプレート構文のところで紹介されたディレクティブです。

    こちらも以前の自分の記事でほぼほぼ、紹介していました。

    2.10 ライフサイクルフック

    reactでもあったのですが、vueでも各タイミングで処理を差し込めるフック機能があるようです。

    本書で紹介されていたタイミングとフック名の表を記載しておきます。

    フックの名前 フックが呼ばれるタイミング
    beforeCreate インスタンスが生成され、データが初期化される前
    created インスタンスが生成され、データが初期化された後
    beforeMount インスタンスがDOM要素にマウントされる前
    mounted インスタンスがDOM要素にマウントされた後
    beforeUpdate データが変更され、DOMに適用される前
    updated データが変更され、DOMに適用された後
    beforeDestory Vueインスタンスが破棄される前
    destroyed Vueインスタンスが破棄された後

    2.11 メソッド(methods)

    メソッドは一番わかりやすく、インスタンスにmethodを追加できます。

    よくあるajax等によるサーバとの通信などで使用するのが一般的のようです。

    ここでも、特に、難しいところはありませんでした。

    一点算出プロパティとメソッドどちらも同じような使い方できるんじゃないの?と思ったりしたところがあったのですが、以前自分の記事でも書いていました。

    算出プロパティは依存しているデータが変更されない限りは、結果をキャッシュしてくれるようです。

    終わりに

    今回は以前自分がやったことのある内容がほぼでした。

    次章から少し複雑なものになっていくようです。

    がんばるぞー!

    Vue.js入門 基礎から実践アプリケーション開発までを読んだ vol.1

    はじめに

    こんにちは。

    Kojirockの1人アドベントカレンダー Advent Calendar 2018の1日目の記事です。

    今回からおそらく連投で、vue.js入門 基礎から実践アプリケーション開発まで を読んで勉強した備忘録を載せていきます。

    Vue.js入門 基礎から実践アプリケーション開発まで

    Vue.js入門 基礎から実践アプリケーション開発まで

    今までいくつか、vueの勉強したものをブログにしたのですが、あまりに身になっていると言い難いので、一章ずつじっくりとやっていければと思います。

    過去記事はこちら

    1章 プログレッシブフレームワークVue.js

    目次

    1.1 現代のWebフロントエンド開発の複雑化

    1.2 Vue.jsの特徴

    1.3 Vue.jsの設計思想

    1.4 プログレッシブフレームワークの解決する段階的な領域

    1.5 Vue.jsを支える技術

    1.6 Vue.jsのエコシステム

    1.7 Vue.jsのはじめの一歩

    1.1 現代のWebフロントエンド開発の複雑化

    過去から現在までのjsの進化などについて記載されていました。

    cssなどと同じく装飾目的で使用されていたjavascriptajaxの登場で飛躍的に存在が強くなり、nodejsやES6などによりさらに注目されるようになったという過程がわかりやすく紹介されています。

    このあたりはサラッと流し読みしております。

    実際に自分がjsを触り始めたのは、backbone.jsが騒がれた頃でした。

    それまではほぼほぼ触っていませんでした。

    理由はガラケー向けサイトの開発だったからです。

    その際は完全にバックエンドとして働いており、jsを使う場面は管理画面くらいで、そのときもalert出すとかその程度でした。

    そんな僕でも、jQueryを駆使して諸々作れる様になり、今vueやReactなどをつまみ食いする用になったので、自分でも少しは成長しているんだなと実感してますw

    1.2 Vue.jsの特徴

    この項目では、Vue.jsの特徴を紹介されてました。

    Vue.js自体は表示部分を取り扱うライブラリとして提供されていて、その他の関連するライブラリと組み合わせて使うことでフレームワークのように扱えることができるとのことです。

    特徴1. 学習コストが低い

    この辺は以前の投稿したブログでも書いたりしてたのですが、Reactより確かに最初はとっつきやすいと感じました。

    本でも紹介されている通り、HTMLを記述するブロック、Javascriptを記述するブロックが明確に分かれているところが、なんと言ってもわかりやすいと思いました。

    <div id="app">
        <p>{{ message }}</p>
    </div>
    
    
    <script type="text/javascript">
      var vm = new Vue({
          el: '#app',
          data: {
              message: 'vueだよ!'
          }
      })
    </script>
    

    ReactだとJSXで完全にロジック内に記述するようになると思うので、マークアップするコーダーさんと分業をしているようなチームでは、コーダーさんにも少し負担がある(覚えればいいんですが・・・)のですが、vueの場合は前述したとおり、ブロックが分かれてるので、入りやすいのかなと。

    特徴2. コンポーネント指向によるUIの構造化

    UIを構造化して、それぞれをコンポーネントとして使用できるようです。

    コンポーネントを組み合わせてページを構成するという形で、その設計はAtomic Designが有名らしいです。

    DeNAさんのAtomic Designの記事がとても理解しやすかったです。

    原子のレベルでコンポーネント化していき、どんどん組み合わせてページとするようなかんじかと理解しました。

    公式サイトに載せてくれている、以下の画像がとてもわかり易かったです。

    f:id:kojirooooocks:20181130224631p:plain

    特徴3. リアクティブなデータバインディング

    この辺は、以前自分が書いた記事でも試してますね。

    双方向バインディングという形です。

    <div id="app">
        <p>{{ message }}</p>
        <input v-model="message">
    
        <p>{{ message_2 }}</p>
        <select v-model="message_2">
            <option value="test_select_1">テストセレクト1</option>
            <option value="test_select_2">テストセレクト2</option>
        </select>
    
        <p>{{ message_3 }}</p>
        <input type="radio" v-model="message_3" value="test_radio_1">テストラジオ1<br />
        <input type="radio" v-model="message_3" value="test_radio_2">テストラジオ2<br />
    
        <p>{{ message_4 }}</p>
        <input type="checkbox" v-on:click="changeMessage" value="test_check_1">テストチェックボックス1<br />
        <input type="checkbox" v-on:click="changeMessage" value="test_check_2">テストチェックボックス2<br />
        <input type="checkbox" v-on:click="changeMessage" value="test_check_3">テストチェックボックス3<br />
    </div>
    
    
    <script type="text/javascript">
        var app = new Vue({
            el: "#app",
            data: {
                message: "テストだよ",
                message_2: "テストだよ2",
                message_3: "テストだよ3",
                message_4: "テストだよ4",
                message_4_origin: "テストだよ4",
            },
            methods: {
                changeMessage: function(e) {
                    if (e.target.checked === true) {
                        this.message_4 = e.target.value;
                    } else {
                        this.message_4 = this.message_4_origin;
                    }
                }
            }
        });
    </script>
    

    ちなみに、先ほど載せた以下のようなコードは、一方向バインディングという形のようです。

    <div id="app">
        <p>{{ message }}</p>
    </div>
    
    
    <script type="text/javascript">
      var vm = new Vue({
          el: '#app',
          data: {
              message: 'vueだよ!'
          }
      })
    </script>
    

    1.3 Vue.jsの設計思想

    vueの設計思想は、どんな規模でも対応できる、「プログレッシブフレームワーク」というもので考えられているようです。

    本で紹介されていた一文です。

    フレームワークはどんなときにでも、どんな規模でも、段階的に柔軟に使えるべきである。

    プログレッシブとは、進歩的な、革新的な、という意味の他にも、(段階的に)前進する、という意味もあるようで、まさに言葉通りの思想という感じです。

    この章ではプログレッシブフレームワークではない、フレームワークで、各段階においての適用の辛さなどが紹介されています。

    1.4 プログレッシブフレームワークの解決する段階的な領域

    以下の公開されているスライドに説明があるので、引用させてもらいます。

    https://docs.google.com/presentation/d/1zQ3Frm3DxSw_qY-KEuykkIUREO-ueFbOyMd1Kd8nqKE/edit#slide=id.g5fc474681_041

    f:id:kojirooooocks:20181130224650p:plain

    1. Declarative Rendering(宣言的レンダリング)

    LPなどのシンプルなwebページなど簡単なページでも、Vue本体がサポートしていることを伝えています。

    2. Component System(コンポーネントシステム)

    前述した、コンポーネント化の機能がVue本体にあるため、こちらもサポートしています。

    LPなどの簡単なwebページよりも、やや複雑なページの際に使用するようなるイメージです。

    3. Client-Side Routing(クライアントサイドルーティング)

    SPAのような動きのwebページも、vue.jsが公式で提供しているライブラリのVue Routerを使用することで、こちらもサポートできるようになります。

    4. Large Scale State Management(大規模向け状態管理)

    コンポーネント間の状態管理ですが、こちらはFluxライクなライブラリであるVuexを使用することで可能になるようです。

    Reactでもコンポーネント間でのデータの受け渡しは、Reduxを使用しないとどんどん数珠つなぎにつなげるような辛い作業をしてたので、vueもvuexを使用することで、そのような辛みから開放されるのだと想像してます。

    5. Build System(ビルドシステム)

    デプロイなどに実行するようなビルド機能ですが、こちらもvue.jsが公式で提供している開発サポートツールを使用することで、その辺のツールを意識せず用意してくれるのかなと思います。

    6. Client-Server Data Persistence(クライアントサーバーデータ永続化)

    クライアントとサーバーサイドのやり取りにおける、複雑なデータ構造の永続化ですが、現状では公式で該当になるようなライブラリは提供されていないようです。

    axiosなどを使用することが一般的のようです。

    ただ、この周りも今後公式でサポートできるようになるようなライブラリが提供される予定のようです。

    1.5 Vue.jsを支える技術

    ここでは大きく3つの技術が紹介されています。

    1. コンポーネントシステム

    前述でも紹介しているコンポーネントを扱うためのシステムです。

    htmlもjsもstyleも、全てをコンポーネントとして閉じ込めることができるので、無駄に被ったり、足りなかったりということがなくなるイメージだと思います。

    そのためには、細かく再利用できるようコンポーネントを区切っていく必要がありますが、きれいに決まれば気持ちいいなと思いました。

    自分的にしっくりきたのは以下の一文です。

    コンポーネントで大事なのはそれを機能や関心事という言語の役割とは別の粒度で切り出せることです。

    2. リアクティブシステム

    状態の変化をVue.jsが検知して、その状態変化をもとにDOM側に自動的に反映できるようにする仕組みです。

    算出プロパティがリアクティブシステムの恩恵を最も受けている例ということのようです。

    算出プロパティ

    自分が書いた以前のブログにも記載してました。

    3. レンダリングシステム

    Vue.jsはReactでも採用している仮想DOMを使用しています。

    仮想DOMについては、こちらの記事がとてもわかり易かったです。

    実際のDOMを扱う場合は、いちから再構築するところを、仮想DOMの場合は、実際のDOMを構築する前に仮想DOMから構築し、その後実DOMを、差分(変更された部分)のみを構築するという形のようです。

    1.6 Vue.jsのエコシステム

    前述したとおり、Vue.js自体はあくまで表示周りのライブラリで、それ単体では、フレームワークというわけではありません。

    Vue.js自体は表示部分を取り扱うライブラリとして提供されていて、その他の関連するライブラリと組み合わせて使うことでフレームワークのように扱えることができるとのことです。

    ここでは、その他関連のライブラリが紹介されていました。

    1. Vue Router

    SPAを実現するための、ルーティング用ライブラリ

    2. Vuex

    大規模なwebアプリケーションで必要になる様々な状態を一括管理するプラグイン

    3. Vue Loader

    更に高度なコンポーネント機能を利用するためのwebpack向けライブラリ。

    4. Vue CLI

    プロジェクトの雛形の生成や必要なライブラリを対話形式で選択して導入できる開発向けライブラリ。

    5. Vue DevTools

    vue.jsのブラウザの開発ツールで、デバッグ時に重宝するライブラリ。

    1.7 Vue.jsのはじめの一歩

    ローカルでの試し方や、jsFiddleでの試し方が紹介されています。

    参考までにjsFiddleでの試し方で紹介されていたものをそのまま試しました。

    f:id:kojirooooocks:20181130224707p:plain

    終わりに

    とりあえず1章終わりました。

    次章からは実際に手を動かす周りだと思うので、早速取り掛かっていこうと思います。

    Kojirockの1人アドベントカレンダー 2018 はーじまーるよー!

    こんばんは。

    ついに始まってしまいました。

    1人アドベントカレンダー

    ノリと勢いだけで始めてしまったんですが、やり始めたからには、やり終えなければ。

    adventar.org

    今回はこの一ヶ月で積みに積んでる本の消化を目的にしています。

    その本はこちら

    Vue.js入門 基礎から実践アプリケーション開発まで

    Vue.js入門 基礎から実践アプリケーション開発まで

    Nuxt.jsビギナーズガイド―Vue.js ベースのフレームワークによるシングルページアプリケーション開発

    Nuxt.jsビギナーズガイド―Vue.js ベースのフレームワークによるシングルページアプリケーション開発

    みんなのGo言語【現場で使える実践テクニック】

    みんなのGo言語【現場で使える実践テクニック】

    Goプログラミング実践入門 標準ライブラリでゼロからWebアプリを作る (impress top gear)

    Goプログラミング実践入門 標準ライブラリでゼロからWebアプリを作る (impress top gear)

    はい。そうです。

    自分が今伸ばしたい分野である、フロントエンド(vue + nuxt)と、Go言語です。

    消化しないと消化しないとと思いつつなかなか消化できずにいたので、これを気にやり終えたいなと思います。

    実際にすべて読み終えたあとは、バックエンドGo、フロント(vue + nuxt)の簡単なアプリケーションを作っておしまいにしようかなと思っています。

    来年もやろう!と思えるように、頑張ってやり続けます!