2019年1月2日水曜日

Vue.jsについて その2

前回に引き続きVue.jsの公式ガイドを見ていく。

Vueコンポーネント


Vueを使うとhtml内で自作したタグを使うことができる。関数を定義して使う感じ。
<div id="components-demo">
  <button-counter text="これは自作タグです"> {{ text }} </button-counter>
</div>

<script>
//自作したタグの定義(グローバル登録)
Vue.component('button-counter', {
  props: ['text'], //関数の引数的なもの。タグの属性として値を指定する。
  data: function () { //関数として初期化することで、複数使ったときに値が独立してくれる。(公式で関数でないといけないと書いてある)
    return { count: 0 }
  },
  template: '<button v-on:click="count++">You clicked me {{ count }} times.</button>'
});
varapp = new Vue({el: '#components-demo'});
</script>
Vue.component関数で自作タグを定義できる。Vue.jsではこのコンポーネントを組み合わせてWebページをつくることを推奨している。 定義するときに指定できるパラメータは以下のものになる
  • template: 自作タグの本体.ここに書かれたものがHTML内で展開される。
  • props: 引数的なもの。HTML内で属性として値を渡せる。HTMLでは大文字は小文字として扱われるのでハイフン(-)で区切った名前を付けた方がいい

    ちなみに型を指定することもできる。

  • data: Vueインスタンスと同じ感じ。ただし関数として定義しないといけない。

    ※そのまま値を設定してもいいが、使われている全てのコンポーネントで共通な値として扱われるので管理が難しくなるので、公式では推奨していない。

  • computed. watch, methods: これらもVueインスタンスと同じ感じで使える。ただしelはない
Vue.componentでコンポーネントを定義する。第一引数にその名前を付けるが、ケバブケース(my-component-name)パスカルケース(MyComponentName)のどちらかで名付けないといけない。
ケバブケースはHTML内で使うときもケバブでないといけないが、パスカルケースの場合はケバブ・パスカルのどちらでもいい。 ただ特別な理由がない限り、ケバブケースで名前を付けた方がいい。

グローバル定義とローカル定義

上のサンプルコードではグローバルとしてコンポーネントを定義している。グローバルは名前の通りでどこからでも参照できるコンポーネントを表す。 プログラミングにおいてグローバルというものは避けるべきという認識があって、Vueコンポーネントにおいてもそれと変わらず、乱用すると不必要なリソースを食う。

そのため、ローカル登録というものを用意しており、基本こちらを使うべきである。

//自作したタグの定義(グローバル登録)
var app = new Vue({
  components: { //ローカルコンポーネントの登録
    'component-A': {...},
    'component-B': {...},
  }
});

モジュールシステムとの連携

javascriptのモジュールには詳しくないのだが(他もあまり詳しくないのだが…)、そのような機能を使うにはBabelやらWebpackといったものを利用するそうだ。

Vue.jsでもそのようなものを使うことを想定しており、自作のコンポーネントについてはファイルに分けて管理することを推奨している。

プロパティバリデーション

Vueコンポーネントの定義の歳にpropsでプロパティを定義できるが、その際にバリデーションを指定できる。

公式ガイドが読みやすくてそちらを見た方が早い気がだんだんしてきているが、一応コードを下に書く。

function Person(firstName, lastName) {
  this.firstName = firstName;
  this.lastName = lastName;
}
Vue.component('my-component', {
  props: {
    // 基本的な型の検査 (`null` は全ての型にマッチします)
    propNumber: Number,
    propBoolean: Boolean,
    propString: String,
    propArray: Array,
    propObject: Object,
    propPerson: Person, //instanceof()で検証しているので自作の型でもOK
    // 複数の型の許容
    propB: [String, Number],
    // 文字列型を必須で要求する
    propC: {
      type: String,
      required: true
      default: 100 // デフォルト値の指定
    },
    // デフォルト値つきのオブジェクト型
    propE: {
      type: Object,
      // オブジェクトもしくは配列のデフォルト値は
      // 必ずそれを生み出すための関数を返す必要があります。
      default: function () {
        return { message: 'hello' }
      }
    },
    // カスタマイズしたバリデーション関数
    propF: {
      validator: function (value) {
        // プロパティの値は、必ずいずれかの文字列でなければならない
        return ['success', 'warning', 'danger'].indexOf(value) !== -1
      }
    }
  }
})

スロット

スロットは Web Components spec draft にある<slot>要素にそったものになる。 スロットは以下のようなものになる。'navigation-linkコンポーネント内の<slot>の部分が"Your Profile"に置き換わり、より自由なコードが書けるようになる。

<div id="app">
    <navigation-link url="/profile">
    Your Profile
    </navigation-link>
</div>

<script>
Vue.component('navigation-link', {
    props: {
        url: String
    },
    template: `
        <a
            v-bind:href="url"
            class="nav-link"
        >
            <slot></slot>
        </a>`
})
var app = new Vue({
    el: "#app",
});
</script>

<slot>は複数書くことができる、以下のVueコンポーネントは3か所置き換えることができる。

<div class="container">
  <header>
    <slot name="header"></slot> //ここと
  </header>
  <div>
    <slot></slot> //ここと
  </div>
  <footer>
    <slot name="footer"></slot> //ここと
  </footer>
  <div>
    <slot></slot> //ここ。
    //同名、または無名なslotは一つにすること。
    //動作はするが、各々の所で記述された内容が統合されて、各場所に展開される。
  </div>
</div>
実際に使うときは以下のようになる。slot名を指定するときはnameではなくてslot属性。うっかり間違えたので気を付けたい。
<base-layout>
    <template slot="header"> <-- ヘッダーに変換される -->
        ヘッダー
    </template>

    <p>メイン</p> <-- メイン だぶり に変換される -->

    <p slot="footer"> <-- フッターに変換される -->
      フッター
    </p>

    <p> <-- メイン だぶり に変換される -->
      だぶり。
    </p>

</base-layout>

ちなみにVueコンポーネント内のslotタグ内に何か記述したら、HTMLで何も記述しなかった時それが代わりに使われる。 いわゆるデフォルト値というものだ。

タブインターフェイスとkeep-alive

Vue.jsを使うとタブインターフェイスも簡単に作れる。componentタグv-bind:isがタブ切り替えの重要点だ。
<div id="app">
    <button
        v-for="tab in tabs"
        v-bind:key="tab"
        v-bind:class="['tab-button', {active: currentTab === tab}]"
        v-on:click="currentTab = tab">
        {{ tab }}
    </button>

    <component
//新しく定義した'tab-part1' か 'tab-part2'コンポーネントが実際に設定される
        v-bind:is="currrentTabComponent"
        class="tab">
    </component>
</div>

<script>
    Vue.component('tab-part1', {
        template: `
            <div>
                タブその1
            </div>
        `,
    })
    Vue.component('tab-part2', {
        template: `
            <div>
                タブその2
            </div>
        `,
    })
    var app = new Vue({
        el: "#app",
        data: {
            currentTab: 'part1',
            tabs: ['part1', 'part2']
        },
        computed: {
            currrentTabComponent: function() {
                return 'tab-' + this.currentTab.toLowerCase();
            }
        }
    });
</script>
しかしこのままだと、あるタブで何か操作した状態はタブを切り替えたらリセットされる。 それを防ぎたいときはkeep-aliveを使うといい。
<keep-alive>
    <component
        v-bind:is="currrentTabComponent"
        class="tab">
    </component>
</keep-alive>
ここまででVue.jsの機能を大体みてきた。こちらのページにエッジケースについて解説しているので、より詳しく知りたい/困ったときに参照するといい。

要素 & コンポーネントへのアクセス

プログラム的なイベントリスナー

循環参照

代替テンプレート定義

強制更新

まだまだあるVue.jsの機能。次はトランジションやミックスインとか見ていきたい。

0 件のコメント:

コメントを投稿