これから始めるVue.js 2.0
第3回 コンポーネントの作成と連携
上記の記事で紹介されているメモ帳をさらにシンプルにしたものを作成しました。以下メモなので、日本語が怪しいです。
http://better-than-i-was-yesterday.com/test-app/vue-memo/index.html
まずは vue-cli をインストールしておくこと。npm install -g vue-cli
以下で、今いるディレクトリに開発に必要なものを一式用意してくれる。vue-router はなしにしたほうがシンプルな構成になるので、さしあたってテストをする上ではナシをおすすめ。
$ vue init webpack
その後 $ npm i で必要な npm を一式インストール。$ npm run dev で開発開始。(yarn を使っていれば yarn で OK)
App.vue が階層的に一番上にあるものなので、ここにコンポーネントを読み込んでいく。さしあたって必要のない Vue のロゴの画像を削除する。Sassを書きたい場合は、次のものをインストールする。
npm install sass-loader node-sass --save-dev
or
yarn add sass-loader node-sass -D
Contents
コンポーネントのロードと配置
必要なコンポーネントは次のようにロードした上で、
import ListView from './components/ListView' import Editor from './components/Editor'
コンポーネントとして使えるように名前を指定する。
components: { ListView, Editor },
<list-view>,<editor>のように template 内で指定することで使用する。キャメルケースで指定した名前は、template 内ではケバブケースで使用する。(例:ListView→list-view)
//App.vue <template> <div id="app"> <h1>Memos</h1> <!--propsであるmemosにthis.data.memosを渡す--> <list-view :memos="memos"></list-view> <!--editorコンポーネントからのaddイベントを監視して、あればaddメソッドを実行する--> <editor @add="add"></editor> </div> </template> <script> import ListView from './components/ListView' import Editor from './components/Editor' export default { name: 'app', data() { return { memos: [] } }, components: { ListView, Editor }, methods: { add(data){ this.memos.push(data) } } } </script> <style lang="sass"> </style>
name につけた名前は、開発ツールから見たときに表示される。
データは app だけが持つ
App コンポーネントが持つデータ (memos : []) に全メモデータを editor コンポーネントで記録し、このデータをもとに描画だけを list-view コンポーネントが担う。
子コンポーネントから親コンポーネントにデータを渡す
<template> <div class="editor"> <input v-model="text"> <button @click="save">save</button> </div> </template> <script> export default { name: 'Editor', props: { }, data () { return { text:'' } }, methods: { save() { const data = this.text this.text ="" // 'add'イベントを自身にトリガーする this.$emit('add', data) } } } </script> <style lang="sass" scoped> </style>
editor コンポーネントは this.$emit によって親コンポーネントである app コンポーネントにイベント(add)と情報を送る。($emit の詳細は後述する)
this.$emit('add', data)
app コンポーネントは add イベントを受け取って add メソッドを実行する。
<editor @add="add"></editor>
add メソッドは app コンポーネントの memos に受け取った情報をためていく。
methods: { add(data){ this.memos.push(data) } }
this.$emit(‘イベント名’, 送るデータ) について
$emit によって、一個上の階層にイベントを発火させることができる。2つの目の引数に渡したデータは、そのイベントをもとにトリガーするファンクションが受け取る。
this.$emit(‘add’,data) をすると、一個上の階層にイベントがいくので、@add=”doFunction” で add イベントをうけて doFunction メソッドを実行する。methods:[ doFunction(data){console.log(data)}] 引数としてdataを受け取ることができる。
データを表示する役割だけを担う list-view コンポーネント
list-view コンポーネントは app コンポーネントがもつデータを描画する役割だけを担う。次のようにデータを props として渡す。
<list-view :memos="memos"></list-view>
list-view は memos という名前で props を受け取るように指定する。受け取った props は data と同じようなやり方でアクセスできる。
v-for によって、受け取った memos の配列の数だけ、list-item コンポーネントを作成する。data in memos によって、memos の配列の数だけ、ということを指定する。data に、各配列内の値が取得される。この data をさらに list-item コンポーネントに memos という名前の props として渡す。(なお、詳細はわからないが、v-for 使用する場合、:key が必須の模様。エラーが出る)
<template> <div class="list-view"> <list-item v-for="data in memos" :memos="data" :key="data"></list-item> </div> </template> <script> import ListItem from './ListItem' export default { name: 'ListView', components: { ListItem }, props: { memos: Array //バリデーションで配列だけに限定 }, data () { return {} } } </script> <style lang="sass" scoped> </style>
list-item コンポーネントでデータを描画
list-item コンポーネントは memos という名前の props を受け取る。これを{{ memos }} で表示する。
<template> <div class="list-item"> {{ memos }} </div> </template> <script> export default { name: 'ListItem', props: { memos: String //バリデーションで文字列だけに限定 }, data () { return { } } } </script> <style lang="sass" scoped> </style>
Tips
- input された値を data と結びつけるには、<input v-model=”プロパティ名”>とする
- 子コンポーネントに props を渡す際に、v-bind:props =”データのプロパティ名”とするが、略記法として:props = “データのプロパティ名”とすることができる
- v-on:click = “メソッド名”の略記法は、@click=”メソッド名”
- template 内等 HTML 関係の場所で data にアクセスするには :props = “memos” のように、直接プロパティ名を指定できるが、script 内で指定する場合には、this.memos
- data にアクセスして v-for するには <コンポーネント名 v-for=”data in プロパティ名” :memos=”data” :key=”data”></コンポーネント名> :memos で props として data を渡している。
- Build してできたものは、初期設定だとトップディレクトリに配置される前提でパスが指定されているので、それ以外の場所におくと動作しない。簡易的な対策として、index.html で読み込む各ファイルのパス指定を、/ディレクトリ名ではなく、./ディレクトリ名と相対パスで指定する。