「基礎から学ぶVue.js」の感想・備忘録2

スポンサーリンク
「基礎から学ぶVue.js」の感想・忘備録1の続き

イベント

イベントハンドラ

メソッド名、または式を記述する。

<button v-on:click="handleClick">メソッド名を指定</button>
<button v-on:click="handleClick()">メソッドコールを記述</button>

メソッド名を指定した場合、引数でイベントオブジェクトを受け取ることができる。(JavaScriptの通常の動き)

handleInput(e) {console.log(e);}

メソッドコールを記述した場合、$eventという変数名でイベントオブジェクトにアクセスすることができる。

<button v-on:click="eventTest($event, 100)">$eventテスト</button>
eventTest(e,num) {
    console.log(e);   console.log(num);
}

イベント修飾子

  • .prevent
    event.preventDefault()
  • .stop
    event.stopPropagation()
  • .self
    自分自身のときだけハンドラ実行
    モーダルの背景をクリックして閉じる場合に便利
  • .right
    クリックイベントで右ボタンのときだけハンドラ実行

キー修飾子

キーコードまたはエイリアスで指定可能。
v-on:keydown.13
v-on:keydown.enter

複数指定するとORになる。
v-on:keydown.up.down

システム修飾子

対応するキーが押されている場合のみイベントハンドラが呼び出される。

  • .ctrl
  • .alt
  • .shift
<button v-on:click.shift="doDelete">削除</button>

inputイベント

v-modelディレクティブは、日本語入力の場合は変換確定するまでデータを更新しない。確定前に更新したい場合はinputイベントを使うとよい。

<input v-on:input="handleInput">
methods: {
    handleInput: function (event) {
        console.log(event.target.value);
    }
}

テキストエリア

テキストエリアはMustacheを使ったバインディングはできない。v-modelを使用する。

<textarea>{{ msg }}</textarea><!--とはできない。-->
<textarea v-model="msg"></textarea><!--とする。-->

単一チェックボックス

デフォルトでは値はBoolean型となる。

<input type="checkbox" v-model="val">{{ val }}

値を自分で設定したい場合は、true-value, false-value属性を使う。

<input type="checkbox" v-model="val" true-value="OK" false-value="NG">{{ val }}

複数チェックボックス

配列型となる。v-modelディレクティブに同じ配列変数を指定し、value属性に値を設定する。

data: {
    val: []
}
<input type="checkbox" v-model="val" value="A">A
<input type="checkbox" v-model="val" value="B">B
<input type="checkbox" v-model="val" value="C">C
<p>{{ val }}</p>

ファイルアップローダ

ファイルアップローダ(type=”file)はv-modelディレクティブを使用できない。
リアクティブにするにはv-on:change=”xxx”としてchangeイベントをハンドルする必要がある。


<input type="file" name="file" v-on:change="uploadFile">
<img v-bind:src="imgBinary">
uploadFile(e) {
    this.imgBinary = window.URL.createObjectURL(e.target.files[0]);
},

v-modelの修飾子

  • .number
    数値に変換
  • .trim
    前後の改行・スペースを削除(全角スペースも)
  • .lazy
  • inputイベントではなくchangeイベントをハンドルする

マウント要素外のイベントハンドリング

windowやbodyではv-onを使用できないので、普通にaddEventListener(またはjQueryなど)を使う。v-onのように自動で解除されないため、フックを使って解除する必要がある。 例えば、createdでwindow.addEventListener()とし、beforeDestroyでwindow.removeEventListener()とする。

応用

算出プロパティ

Mustacheやディレクティブの式を算出プロパティにすると可読性が上がる。 computedオプションに関数として定義するが、プロパティとして使用できる(内部でdefinePropertyが呼ばれているため)

computed: {
  halfWidth: function() {
    return this.width / 2;
  }
}

算出プロパティのキャッシュ機能

算出プロパティは、参照しているリアクティブデータが更新されたときだけ再評価される。

methods: {
    methodsRandom: function () {
        return Math.random();
    },
},
computed: {
  computedRandom: function () {
        return Math.random();
      },
}
// computedRandomは毎回同じ値を返却する。

ウォッチャ

変数を監視して変化があったときに処理を実行することができる。ただし、多くの場合は算出プロパティを使った方がよい(https://jp.vuejs.org/v2/guide/computed.html)
データが変わるのに応じて、非同期やコストの高い処理を実行したいときに最も便利。

watch: {
  question: function (newQuestion, oldQuestion) {
    this.doSomthing();
  }
},

フィルタ

文字数を丸めたり、数字にカンマを入れたりできる。コンポーネントのfilterオプションに算出プロパティのように定義し、Mustacheまたはv-bindディレクティブの値にパイプでフィルタ名をつなぐ。
※thisへのアクセスはできない
thisへのアクセス可否以外はcomputedと同じだが、テキストの変換はfiltersに定義した方が管理しやすくなる。

filters: {
  localNum: function(val) {
    return val.toLocaleString();
  }
}
// として
// {{ price | localNum }} 円
// で表示。
localNum: function(val、name, num) {
  return `${val}:${name}:${num}`;
}
// のように第2引数以降に引数を持たせることもできる。
// {{ price | localNum('Taro', 100) }}

グローバルフィルタ

Vue.filterメソッドを使って登録すると、すべてのコンポーネントから利用できる。

Vue.filter('localNum',  function(val) {
  return val.toLocaleString();
});

コンポーネント

コンポーネントのグローバル登録

Vue.componentメソッドを使う。
※templateのルート要素は1つでなければならない

Vue.component('my-component', {
    template: '<p>MyComponent</p>'
});
<!--使用する際はタグとして記述する。-->
<my-component></my-component>

コンポーネントのローカル登録

new Vue()する際のcomponentsオプションに登録することで、スコープを制限できる。通常はローカル登録を使うべき。

new Vue({
  el: '#app',
  components: {
    'my-component': {template: '<p>My Component</p>'}
  }
});

コンポーネントのオプション

new Vue()と同じように、data, methods, computed, watchなどを定義できる。ただし、dataはオブジェクトではなくオブジェクトを返す関数にする必要がある。

components: {
  'my-component': {
    template: '<div><p>Option test</p><button v-on:click="doComponentMethod"></button>{{ msg }}</div>',
    data: function() {
      return {
        msg: 'hoge'
      }
    },
    methods: {
      doComponentMethod: function() {
        this.msg = 'doComponentMethod called';
      }
    },
  },
}

コンポーネント間の通信

方法は3つ。

  1. popsとカスタムイベントを使った親子間通信
  2. イベントバスを使った非親子間通信
  3. Vuexを使った状態管理

親子コンポーネント

テンプレートで他のコンポーネントを使用すると親子関係になる。

Vue.component('parent-child', {
  template: '<div><global-component></global-component></div>'
  }
);

グローバル登録だと読み込むコンポーネントが多くなってしまうため、ローカル登録するべき。ただし、ローカル登録されたコンポーネントは、他のコンポーネント内では使用できない。 以下のように、componentsに子テンプレートをセットする必要がある。

let childComponent = {template: '<p>childComponent</p>'};
let parentComponent = {
  template: '<p><child-component></child-component></p>',
  components: {
     'child-component': childComponent
  },
};

として、new Vueの中で以下のようにする。

components: {
  'parent-component': parentComponent
},

※コンポーネントをJavaScriptオブジェクトとして定義し、componentsに使うものをセットしてあげる

propsオプション(親から子へ)

propsオプションにより、属性としてコンポーネントに値を渡すことができる。

let parentComponent = {
  template: '<div><child-component v-bind:val="val"></div>',
  components: {
    'child-component': childComponent,
  },
  props: ['val'],
};
<parent-component val="abc"></parent-component>
<parent-component v-bind:val="msg"></parent-component>

※propsで受け取ったデータは上書きできない。データを変更したい場合はcomputedで新しいデータを作成するか$emitで親のアクションを実行する。

コンポーネントをリストレンダリング

let listComponent = {
  template: '<li>{{ name }}</li>',
  props: ['name'],
};
<ul>
  <list-component v-for="item in items" v-bind:name="item"></list-component>
</ul>

カスタムイベント(子から親へ)

親のテンプレートで以下のように記述する。

<comp-child v-on:childs-event="parentsMethod"></comp-child>

以下のようにイベントハンドラを定義。

methods: {
  parentsMethod: function(msg) {
    alert(msg);
  }
}


子では下記のようにthis.$emit('childs-event')でイベント発火できる。
※第2引数以降でデータを渡すことができる

method: {
  handleClick: function() {
    this.$emit('childs-event', 'hogehoge');
  }
}

その他

トランジション

transitionタグで囲むと、要素の追加・削除のタイミングでクラスを動的に付けてくれる。

<transition><p v-show="show">トランシジョンテスト</p></transition>

以下のようなCSSを定義しておけばよい。

.v-enter-active, .v-leave-active {
    transition: opacity 1s;
}
.v-enter, .v-leave-to {
    opacity: 0;
}

※初期描画時にも付与したい場合は<transition appear>とする
<transition>は単一要素の場合のみ使える。複数要素の場合は<transition-group>

アプリケーションの拡張に必要な技術

  • Vuex
    複数のコンポーネントを管理するためのライブラリ
  • Vue Router
    SPA用に画面とURLを紐付けるためのライブラリ。
    VuexとVue Routerは独立しているが、組み合わせることで効率的な管理ができるため同時に導入することが多い。
  • Vue CLI
    Vue.jsのためのコマンドラインインターフェース。
    開発環境にツールやライブラリを簡単に導入できる。
    雛形の作成やビルドもしてくれる。
    モジュール化されたJavaScriptファイル(Vue.jsの場合は単一ファイルコンポーネント)は単純に結合するだけでは動かない。
    webpackのようなバンドルツールを使って依存関係を解決しながら結合する必要がある。webpackの環境構築はとても複雑で、webpack以外のツールの知識も必要になってしまう。そこで、設定ファイルを自動作成し、環境構築してくれるのがVue CLI。 Vue CLIを使えば専門的な知識がなくても単一ファイルコンポーネントを使用するための環境を作ることができる。

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

HTML, JavaScript, CSSを.vueファイルにまとめて管理することができる。中規模以上の開発では単一ファイルコンポーネントがメインになる。独自フォーマットのため、コンパイルしなければならない。templateタグはコンポーネントオプションのtemplateを抜き出したもの。scriptタグはそれ以外のオプション。

<template>
  <p class="title">{{ msg }}</p>
</template>
<script>
  export default {
    name: 'Example',
    data() {
      return {
        msg: 'hoge',
      };
    },
  };
</script>
<style scoped>
.title {
  color: red
}
</style>

ES2015モジュールの書き方

export default 'Yamada';
export const name1 = 'Tanaka';

のように定義し、以下のようにして使う。

import User, { name1 } from './User';
console.log(User); // Yamada
console.log(name1); // Tanaka
// <script type="module">としないと動かない
  • export defaultで公開した値
    中括弧なしでimportすることができる。
    どんな名前でimportしてもよい。
    ※export defaultで公開できるのは1ファイル1つ
  • exportで公開した値
    中括弧をつけてimportしなければならない。
    export時の変数名でimportする。
    ※{ xxx as yyy }とすることで任意の変数名に変えることができる

Babel

ES6以降の記法を、対応ブラウザの多いES5準拠へ変換するトランスパイラ。あくまでBabel自体(babel-core)は構文変換しか行わない。Babelはcore-jsというブラウザ標準APIのPolyfillと組み合わせることでES2015(ES6)の構文への対応を行っている。つまりBabelによるトランスパイルの動作は、「構文変換+Polyfill」によって実現されている。 最新のAPIなどを使用したいというよりは、開発時に新しい記法を使って書けるようにするのが目的。Vue CLIはBabelの基本設定を自動的に行ってくれる。

Vue CLIの導入

  • sudo npm install -g vue-cliでインストール
  • vue init テンプレート名 プロジェクト名でプロジェクト作成

テンプレート名はwebpackとwebpack-simpleがよく使われる。
例)vue init webpack sample-app
(Vue CLI3ではvue create sample-app
いくつか質問されるが、すべてEnterとnでOK。
ディレクトリがいくつか作成されるが、触れるのはsrcだけ。
.vueファイルはsrc/components/にまとめる。
cd sample-appで移動しnpm run devで開発サーバを起動
(Vue CLI3ではnpm run serve)
http://localhost:8080/で確認可能。
※webpack-simpleの場合は起動前にnpm installが必要だった
ホットリロード機能により、修正はリロードしなくてもブラウザを反映される。 npm run buildでdistディレクトリにWEBサーバへのアップ用のファイルが生成される。

ES2015で書いてみよう

  • 変数はconst、上書きが必要なものだけlet、varは使わない
    constで宣言した配列でもpushなどは使える。
    初期化は.length=0とする、空配列[]の再代入はできない。
  • thisが必要な場面以外ではfunctionキーワードは使わない
    hoge: function(){}は短縮記法でhoge(){}と書く。
    function name(引数) {処理}const name = (引数) => {処理};と書く。

コメント