コンポーネントの(初期)値設定: data

dataプロパティに定義します。そしてこのデータプロパティはオブジェクトを返す関数である必要があります。

export default {
  data() {
    return {
      value: "foo"
    };
  }
};

親から子へ値を渡す: props

コンポーネントオブジェクト(VueConstructor)にpropsを置くと、親に対して「このコンポーネントは A という値が必要だから渡してね」のように伝えることができます。

一番簡単にてっとり早くそれを実装するには単にpropsに渡せるprops名を配列で羅列するだけです。

{
  props: [
    'foo',
    'barBaz'
  ]
}

これでfoobarBazというプロパティを受け付けるようになりました。親からは、

<Child foo="a" bar-baz="b" />

のように値を渡すことができます。ご覧の通りbarBazのように設定したプロパティ名はテンプレート上ではfoo-bazのようなケバブケースで使えます。

もし「fooは数値がいい」「bar-bazは関数がいい」という注文がある場合、propsをこのように書き換えます。

{
  props: {
    foo: Number,
    barBaz: Function
  } 
}

使えるコンストラクタには他に、

  1. String,
  2. Boolean,
  3. Array
  4. Object,
  5. Promise
  6. class で定義したもの何でも

が使えます。つまりコンストラクタなら何でもです。

これで先程のままabという値を渡してしまった場合、「Invalid prop: type check failed for prop "<prop名>"」というエラーが出ます。

しかしこれでもまだ不十分なケースがあります。それは値を絶対に渡してほしい場合です。つまり先程までのは、どちらかまたはどちらも渡さなくても通ってしまいます。

<Child :foo="1" />

必須プロパティ

bar-bazこそ必要なのに」という場合これでは困ってしまいます。Vue はあるプロパティについては必須にするというやり方も提供しています。それには先程のpropsをまた書き換える必要があります。

{
  props: {
    foo: Number,
    barBaz: {
      type: Function,
      required: true
    }
  } 
}

barBazだけが必須なので、それだけ書き換えてます。barBazをオブジェクトに変えコンストラクタをtypeに、そしてrequired: trueを増やします。
これでbar-bazを渡さなかった時は「Missing required prop: "barBaz"」というエラーがでるようになりました。

デフォルト値

このpropオブジェクトはdefaultというキーを持つことができます。これはprop値が渡されなかった時代わりにそのプロパティ値に設定される値になります。

のようなものを作りました。分かりづらい人はフォークして実際に触ってみてください。

条件分岐: v-if, v-else-if, v-else

テンプレートの中で条件分岐したい場合v-ifv-else-ifv-elseが使えます。これらは必ず同じ階層にある必要があります。またv-else-ifv-elseは単体では使えず同階層にv-ifが必要です。

です。

<template>
  <span v-if="num === 1">one {{num}}</span>
  <span v-else-if="num === 2">two {{num}}</span>
  <span v-else>three {{num}}</span>
</template>

<script>
export default {
  data() {
    return {
      num: 1
    };
  }
};
</script>

ループ: v-for

テンプレートでデータの数だけ繰り返し表示したい場合はv-for属性を使います。この属性は配列とオブジェクトどちらでも回すことができます。この属性はループをラップする要素ではなく、ループする(繰り返し出現するタグ)に付けなければいけません。

また値を回す時、そのルートタグにはv-bind:key:key)値の設定が必要です。

配列

値だけを回す、値とインデックスを回すの2つの方法があります。

配列の要素だけを回したい時はitem in arrayと置きます。inの右側に配列値を置き、左側に各値が入る変数名を置きます。

<ul>
  <li v-for="item in arr" :key="item">{{item}}</li>
</ul>

インデックスも一緒に回したい場合は左側を括弧で囲み第2引数のような形でインデックス値を受け取れます。

<ul>
  <li v-for="(item, index) in arr" :key="item">{{item}}</li>
</ul>

オブジェクト

こちらは

  • 値だけ
  • 値とキー
  • 値とキーとインデックス

3つの方法がありますが、大体は配列と同じです。1つの場合は直に、複数の場合は括弧で囲んで値を受け取ります。

値だけ

<ul>
  <li v-for="value in obj" :key="value">{{value}}</li>
</ul>

値とキーとインデックス

<ul>
  <li v-for="(value, key, index) in obj" :key="key">{{key}} {{value}} {{index}}</li>
</ul>

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

グローバルなコンポーネントとは、各ファイルで読み込んでcomponentsセクションに渡さなくとも使えるコンポーネントのことです。

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

Vue.componentという静的メソッドを使います。これは引数を2つ取り、それぞれ

  1. テンプレート上のコンポーネントの名前string
  2. datamountedなどを含む、いわゆる Vue コンポーネントのオブジェクト

を受け取ります。
ちなみに、コンポーネントの名前は-で繋いだ名前にすると良いです。

以下はmy-componentというコンポーネントの登録例です。

Vue.component('my-component', {
  data() {
    return {
      message: 'hello'
    }
  },
  mounted() {
    console.log(this.message);
  }
});

アプリをマウントする前に

new Vueでアプリをマウントする前に上記のコードで登録しておく必要があります。

Vue.component('...', {/* ... */});

new Vue({/* ... */});

Vue コンポーネントを作って npm で公開する

この内容は知見が貯まると共に変更になる可能性が大です。また Vue の最新がvue@2.6.10、Nuxt の最新がnuxt@2.8.0の時に書いています。

Vue コンポーネントを作って公開するおおよその流れは以下のような感じです。

  1. *.vueファイルを実装
  2. *.vueを Rollup を使ってビルド
  3. 生成されたファイルを npm で公開

ちなみに TypeScript とyarnを使っていきます。

*.vue ファイルを実装

まずは僕的に必要なパッケージをインストールします。必要なものは vuevue-property-decorator そして typeScript です。

yarn add -P vue@^2.6.0
yarn add -D \
  vue \
  vue-property-decorator \
  typescript

そしたら軽く*.vueを1つ作ります。src/index.vue辺りに、

<template>
  <div>
    <h1>{{message}}</h1>
  </div>
</template>

<script lang="ts">
import {Component, Vue} from 'vue-property-decorator';
 
@Component
export default class extends Vue {
  public message = 'hello';
}
</script>

<style>
h1 {
  color: orange;
}
</style>

とりあえず現在は対応が不十分のようなのでスタイルのスコープ機能は使わないようにするといいと思います。

*.vueを Rollup を使ってビルド

Webpack よりこちらのほうが出力コードが少ないです。その設定ファイルrollup.config.jsはこんな感じです。

import nodeResolve from 'rollup-plugin-node-resolve';
import commonjs from 'rollup-plugin-commonjs';
import typescript from 'rollup-plugin-typescript2';
import cssOnly from 'rollup-plugin-css-only';
import vue from 'rollup-plugin-vue';

export default {
  input: 'src/index.vue',
  external: ['vue'],
  plugins: [
    nodeResolve(),
    commonjs(),
    typescript({
      clean: true,
      objectHashIgnoreUnknownHack: true,
    }),
    cssOnly(),
    vue({css: false}),
  ],
};

上2つは環境によって変わります。typescriptのオプションはエラーを回避する為にこうなった的な感じです。下2つは今回のやり方では必須です。必要なプラグインはdevDependenciesに追加するかyarn add -Dが必要なので忘れずに。

下2つはvue({css: false})によって「JavaScript で<style>を作り Dom に挿入しなくていい」と設定しています。これはブラウザでのみ実行が想定するファイルを作る事になってしまうため、Nuxt などでエラーを起こしてしまいます。

`vue({css: true})`のエラー

実は今vue({css: false})により「*.cssということで扱う」(ソースコード中にimport '*.css'で使った)という意味にできています。この*.cssを抜き出し.cssファイルを作ってくれるのがrollup-plugin-css-onlyプラグインです。

さて Rollup の設定ファイルができたのであとは以下のようなコマンドで変換します。

rollup -c rollup.config.js --sourcemap --format esm --file dist/module/index.js

これは--format esmなので ES Module でdist/index.jsに吐き出されると思います。その時にindex.cssも同じディレクトリに存在すればうまくいってます。
同じように--format cjs --file dist/main/index.jsなどで CommonJS や他必要なものを生成します。

生成されたファイルを npm で公開

生成ファイルがちゃんとインストール先で読まれるようにpackage.jsonを編集します。

{
  "name": "vue-xxx",
  "main": "dist/main/index.js",
  "types": "typescript の型へのパス",
  "module": "dist/module/index.js",
  "source": "src/index.vue",
  "browser": "ブラウザ用にビルドしたファイルへのパス",
  "style": "dist/module/index.css"
}

nameには好きな名前を置きます。ただvue-とプレフィックスは付けたほうがいいと思います。あとはyarnで上げるだけです。

yarn publish
# 上によってコミットされたものを `git push`

メモ

CSS も JavaScript で挿入する形で最初考えていて、いろいろ試した結果_vm._ssrEscape is not a functionというエラーが出るまでは行きました。恐らく今度はサーバーサイドでは動いていてブラウザサイドで動かなくなった形な気はしましたがそこで力尽きました。

vue-property-decorator の使い方

kaorun343/vue-property-decoratorの中身は公式が提供しているvuejs/vue-class-componentというライブラリにいくつかデコレーターを増やしたような感じです。
このライブラリを使うことでよりクラスらしい形で書くことができます。

ちなみにコードは全て TypeScript を使っています。

プロパティ

Propデコレーターを使います。例です。

@Component
class extends Vue {
  @Prop(String) foo!: string;
}

これは、

  1. fooというプロパティを受け付けます
  2. それは文字列です

という風なことを定義しています。これは以下のような従来の書き方のものと同等になります。

{
  props: {
    foo: String
  }
}

同等に変換されるようにPropに渡せる引数も従来の設定と同じものを渡します。(参照: プロパティのバリデーション

データ

todo