LoginSignup
1
1

More than 3 years have passed since last update.

Vue.jsを1から学んでみた〜Vue CLIとVue Component〜

Last updated at Posted at 2020-08-06

Vue.js を1から学んでみた。にて書いてきましたが、
長くなり編集しにくくなってきたので、分割しました(こんなやりかたしないのかな?知りたい。)

8-1. Vue CLIとは

Vueのフレームワーク。
簡単に利用でき、サーバーを立ち上げることができる。

$ vue create vue-cli-sample


Vue CLI v4.4.6
? Please pick a preset: default (babel, eslint)


Vue CLI v4.4.6
✨  Creating project in /Users/shinri/development/study/vuejs/vue-cli-sample.
🗃  Initializing git repository...
⚙️  Installing CLI plugins. This might take a while...

・・・省略

$ cd vue-cli-sample/
$ npm run serve

> vue-cli-sample@0.1.0 serve /Users/shinri/development/study/vuejs/vue-cli-sample
> vue-cli-service serve

 INFO  Starting development server...
98% after emitting CopyPlugin

 DONE  Compiled successfully in 2287ms                                                                                                                                                                                       13:24:23


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

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

http://localhost:8080/ にアクセスするとVueの画面が表示される。
image.png

8-2. Vue CLI構成

image.png

初期ディレクトリ概要

  • node_modules
    • nodeでインストールされた様々なモジュール群
    • 何か便利なライブラリを見つけたらnpmでインストールするが、ここに入る
  • public
    • 静的なファイルを置いておく。webpackの対象にならない。
    • index.html しか初期は入っていない(favicon除く)。
    • あんまり追加しない。
  • src
    • vueファイルを書いてアプリを作る(雑)
    • webpackの対象となる

初期ファイル概要

  • .gitIgnore
    • gitにpushしないファイル/ディレクトリの指定
  • babel.config.js
    • ES5にする時の指定。
    • 初期設定のままで基本はOK。複雑なものを作る場合は別。
  • package.json
    • 使用するライブラリのバージョンを指定する
    • scriptsでは、コマンドのオプションを指定する。$npm run serveは、"serve": "vue-cli-service serve"のように定義されている
  • package-lock.json

srcディレクトリ詳細

assetsディレクトリ

ロゴファイルなど、リソースを入れる。

main.js

render関数で、App.vueオブジェクトを指定しているのみ。
#appにマウントしているが、これはpublic/index.htmlに指定されている。
つまり、App.vueオブジェクトをindex.htmlに表示させていることになる。

public/index.html
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width,initial-scale=1.0">
    <link rel="icon" href="<%= BASE_URL %>favicon.ico">
    <title><%= htmlWebpackPlugin.options.title %></title>
  </head>
  <body>
    <noscript>
      <strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
    </noscript>
    <!-- ↓ここに#appが指定されている -->
    <div id="app"></div>
    <!-- built files will be auto injected -->
  </body>
</html>
src/main.js
import Vue from 'vue'
import App from './App.vue'

Vue.config.productionTip = false

new Vue({
  render: h => h(App),
}).$mount('#app')

App.vue

単一ファイルコンポーネントと呼ばれる。
画面に表示する内容を、このコンポーネントで実装する。

なお、.vueファイルはVue CLIで使われることで、webpackにて単一コンポーネントとして扱われる。
import App from './App.vue'のように定義すると、.vueファイル内で単一コンポーネントとして使用できる。

以下の初期作成の例では、HellowWorld.vueコンポーネントを作成し、
それをApp.Vueでタグで使うことで画面を表示させている。

src/App.Vue
<template>
  <div id="app">
    <img alt="Vue logo" src="./assets/logo.png">
    <HelloWorld msg="Welcome to Your Vue.js App"/>
  </div>
</template>

<script>
import HelloWorld from './components/HelloWorld.vue'

export default {
  name: 'App',
  components: {
    HelloWorld
  }
}
</script>

<style>
#app {
  font-family: Avenir, Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
  margin-top: 60px;
}
</style>
src/components/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>

本番用ビルド

$ npm run build を実行することで、src,publicの内容が最適化ビルドされ、distディレクトリが作成される。
これをそのまんまリリースできる。
image.png

8-3. Vueコンポーネントの基本

Vueコンポーネント(.vueファイル)は、以下で構成される。

  • template
    • DOMとして展開される内容を記述する箇所
    • scriptにrender関数があれば、不要。
    • 注意点:ルート要素は必ず1つであること。エラーになる
  • script
    • JavaScriptで記載する箇所
    • 特に必要なければ、不要。
  • style
    • CSSを記載する箇所
    • CSSが不要であれば、不要。

templateとscriptだけを書いて簡単なvueにて実行してみる

App.vue
<template>
  <p>こんにちは{{name}}さん</p>
</template>

<script>
export default {
  data: function() {
    return {
      name: "太郎"
    }
  }
}
</script>

<!-- styleは消しました -->

http://localhost:8080/ にアクセスするとVueの画面が表示される。
image.png

補足:ルート要素は1つ

以下のように、template配下にルート要素を2つ以上持つと、コンパイルエラーとなる。

hoge.vue
<template>
  <p>こんにちは{{name}}さん</p>
  <p>こんにちは{{name}}さん</p>
</template>
 error  in ./src/FirstName.vue?vue&type=template&id=50ec6832&

Module Error (from ./node_modules/vue-loader/lib/loaders/templateLoader.js):
(Emitted value instead of an instance of Error) 

  Errors compiling template:

  Component template should contain exactly one root element. If you are using v-if on multiple elements, use v-else-if to chain them instead.

  1  |  
  2  |  <p>こんにちは{{name}}さん</p>
     |                         
  3  |  <p>こんにちは{{name}}さん</p>
     |  ^^^^^^^^^^^^^^^^^^^^^^
  4  |  

Component template should contain exactly one root element. If you are using v-if on multiple elements, use v-else-if to chain them instead.

一つにしなさいと怒られます。ただ、シンプルに機能を分けるサポートをしてくれるという意味では助かることかと。

8-4. Vueコンポーネントのグローバル登録

どこの.vueでも単一コンポーネントを使えるように、グローバル登録を行う。
グローバル登録はmain.jsに対して行う。

以下のように、FirstName.vueを新規作成し、グローバル登録した後、そのコンポーネントを使用する。
image.png

FirstName.vue
<template>
  <p>こんにちは{{name}}さん</p>
</template>

<script>
export default {
  data: function() {
    return {
      name: "次郎"
    }
  }
}
</script>
App.vue
<template>
  <!-- グローバル登録したコンポーネントを使用 -->
  <FirstName></FirstName>
</template>
main.js
import Vue from 'vue'
import App from './App.vue'
// コンポーネント追加
import FirstName from './FirstName.vue'

Vue.config.productionTip = false

// グローバル登録
Vue.component("FirstName", FirstName);

new Vue({
  render: h => h(App),
}).$mount('#app')

結果
image.png

8-5. Vueコンポーネントのローカル登録

グローバル登録の次にローカル登録について。
単一コンポーネントのローカル登録と同じく、Js内(今回で言うと、<script>内)でimportし、vueインスタンスのcompnentに登録する。

以下のように、LastName.vueを新規作成し、ローカル登録した後、そのコンポーネントを使用する。
main.jsFirstName.vueは変わらないので割愛。

image.png

LastName.vue
<template>
  <div>
    <p>私は{{name}}です</p>
  </div>
</template>

<script>
export default {
  data: function() {
    return {
      name: "山田"
    }
  }
}
</script>
App.vue
<template>
  <!-- ルートは一つ -->
  <div>
    <!-- グローバル登録したコンポーネントを使用 -->
    <FirstName></FirstName>
    <!-- ローカル登録したコンポーネントを使用 -->
    <LastName></LastName>
  </div>
</template>

<script>
import LastName from "./LastName.vue";

export default {
  components: {
    LastName: LastName
  }
}
</script>

結果
image.png

8-6. VueコンポーネントのCSS

<style>タグにCSSを書けば良いが、必ずscopedをつける。
そうすることで、同じvue内のtemplateにのみ適用させることができる。

FirstName.vue
<template>
  <div>
    <p>こんにちは{{name}}さん</p>
  </div>
</template>

<script>
export default {
  data: function() {
    return {
      name: "次郎"
    }
  }
}
</script>

<!-- `scoped`をつける -->
<style scoped>
div {
  border: 1px blue solid;
}
</style>

image.png

内部的にみると、該当タグに識別子をつけて判断している感じ。
image.png

scopedを付けないと、全divに反映されてしまう

<!-- `scoped`をつけない -->
<style>
div {
  border: 1px blue solid;
}
</style>

image.png

8-7. コンポーネント間のデータの受け渡し(親→子:props)

基本:プリミティブ/配列/オブジェクト

親→子の場合、propsを使います。
例として、App.vueから数値と文字列を受け渡すやり方を載せます。
注意:propsは変更値不可なので、子画面で利用する場合は変数に詰め替えてから使う。

FirstName.vue
<template>
  <div>
    <p>こんにちは{{name}}さん</p>
    <button @click="changeMethod">押した分だけ年齢増えます</button>
    <p>年齢:{{age}}</p>
    <p>性別:{{gender}}</p>
  </div>
</template>

<script>
export default {
  // 親→子の受け渡しに`props`を使う。
  // 子では受け口として定義するイメージ。
  // 書き方は配列かオブジェクトで可能。
  // ただ、詳しく書きたいのであれば、オブジェクト型とする。
  props: {
    // 変数名:キャメルケース
    yourAge: {
      type: Number,  // 型
      required: true // 必須かどうか
    },
    gender: {
      type: String,
      required: true
    }
  },
  data: function() {
    return {
      name: "次郎",
      // propsのデータは変更不可が推奨されているため、
      // このようにdataに設定する
      age: this.yourAge
    }
  },
  methods:{
    changeMethod: function() {
      // propsの値は変更させない
      this.age += 1
    }
  },
}
</script>

<style scoped>
div {
  border: 1px blue solid;
}
</style>
App.vue
<template>
  <div>
    <!-- ケバブケースでOK -->
    <!-- propsの値を設定する。v-bindも勿論OK! -->
    <FirstName :your-age="defaultAge" gender="男性"></FirstName>
    <LastName></LastName>
  </div>
</template>

<script>
import LastName from "./components/LastName.vue";

export default {
  data: function() {
    return {
      defaultAge: 15
    }
  },
  components: {
    LastName: LastName
  }
}
</script>

結果
aaa10.gif

応用:slot

htmlをそのまんま渡す場合、
v-htmlタグを使って頑張ればいけそうだけど、めんどくさい笑
その代わりにVueコンポーネント内に(DOM要素のように)htmlを書けるようにすることができる機能がある。

そのためにslotタグとv-slotディレクティブを使う (ジャグラー打ちてー)

1つslotの例

LastName.vue
<template>
  <div>
    <!-- slotタグを指定 -->
    <slot></slot>
  </div>
</template>
App.vue
<template>
  <div>
    <FirstName :your-age="defaultAge" gender="男性" @my-click="emitData = $event"></FirstName>
    <!-- 要素にHTMLを指定 -->
    <LastName>
      <p>私は山田です</p>
      <p>職業はSEです</p>
    </LastName>
    <p>思っていた年齢{{defaultAge}}歳でしたが、{{emitData}}歳違っていたようでした。すみません。</p>
  </div>
</template>

<script>
import LastName from "./components/LastName.vue";

export default {
  data: function() {
    return {
      defaultAge: 15,
      emitData: 0
    }
  },
  components: {
    LastName: LastName
  }
}
</script>

結果
image.png

複数のslotの例

結論から言うと、templateタグと、v-slotディレクティブを使う。

LastName.vue
<template>
  <div>
    <!-- slotタグのnameにテンプレート名を指定 -->
    <slot name="name"></slot>
    <hr>
    <!-- slotタグのnameにテンプレート名を指定 -->
    <slot name="job"></slot>
  </div>
</template>
App.vue
<template>
  <div>
    <FirstName :your-age="defaultAge" gender="男性" @my-click="emitData = $event"></FirstName>
    <LastName>
      <!--  v-slotでテンプレート名を指定 -->
      <template v-slot:name>
        <p>私は山田です</p>
      </template>
      <!--  v-slotでテンプレート名を指定 -->
      <template v-slot:job>
        <p>職業はSEです</p>
      </template>
    </LastName>
    <p>思っていた年齢{{defaultAge}}歳でしたが、{{emitData}}歳違っていたようでした。すみません。</p>
  </div>
</template>

<script>
import LastName from "./components/LastName.vue";

export default {
  data: function() {
    return {
      defaultAge: 15,
      emitData: 0
    }
  },
  components: {
    LastName: LastName
  }
}
</script>

結果
image.png

デフォルトslotの例

templateがついていないものは、Vue.jsが<template v-slot:defalut>を勝手に作成しまとめる。
あくまでも、slotを使う場合はtemplateが必要ということ。

App.vue
<template>
  <div>
    <FirstName :your-age="defaultAge" gender="男性" @my-click="emitData = $event"></FirstName>
    <LastName>
      <!-- templateがついていないのは、デフォルトスロットとしてまとめられる1 -->
      <p>デフォルトスロット用1</p>

      <!--  v-slotでテンプレート名を指定 -->
      <template v-slot:name>
        <p>私は山田です</p>
      </template>
      <!--  v-slotでテンプレート名を指定 -->
      <template v-slot:job>
        <p>職業はSEです</p>
      </template>

      <!-- templateがついていないのは、デフォルトスロットとしてまとめられる2 -->
      <p>デフォルトスロット用2</p>
    </LastName>
  </div>
</template>
LastName.vue
<template>
  <div>
    <!-- デフォルトスロット表示用 -->
    <slot></slot>
    <hr>
    <!-- slotタグのnameにテンプレート名を指定 -->
    <slot name="name"></slot>
    <hr>
    <!-- slotタグのnameにテンプレート名を指定 -->
    <slot name="job"></slot>
  </div>
</template>

結果
image.png

スロットプロパティ

スロットプロパティを使えば、子のdataを使うことができる。

  • 親の指定
    • <template v-slot:name="slotProps">"slotProps"がスロットプロパティ(の名称)。
    • 好きな命名でOK。
  • 子の指定:<slot name="title" :name="name"> ←v-bindを使って、スロットプロパティを指定する。
  • 子はVueインスタンスのフィールドを使用して、親のスロットプロパティが受け取るイメージ。
LastName.vue
<template>
  <div>
    <!-- デフォルトスロット表示用 -->
    <slot></slot>
    <hr>
    <!-- v-bindでスロットプロパティを定義 -->
    <slot name="name" :sei="sei" :mei="mei"></slot>
    <hr>
    <!-- slotタグのnameにテンプレート名を指定 -->
    <slot name="job" :syokugyou="syokugyou"></slot>
  </div>
</template>

<script>
export default {
  props: {
    job: {
      type: String,
      required: false
    }
  },
  data: function() {
    return {
      sei: "山田",
      mei: "太郎",
      syokugyou: this.job
    }
  }
}
</script>
App.vue
<template>
  <div>
    <FirstName :your-age="defaultAge" gender="男性" @my-click="emitData = $event"></FirstName>
    <LastName :job="job">
      <!--  スロットプロパティ「slotProps」を定義 -->
      <template v-slot:name="slotProps">
        <!-- このタグでのみ「slotProps」使用できる -->
        <p>私は{{slotProps.sei}}{{slotProps.mei}}です</p>
      </template>

      <!--  スロットプロパティ「hoge」を定義 -->
      <template v-slot:job="hoge">
        <!-- このタグでのみ「hoge」を 使用できる -->
        <p>職業は{{hoge.syokugyou}}です</p>
      </template>
    </LastName>
    <p>思っていた年齢{{defaultAge}}歳でしたが、{{emitData}}歳違っていたようでした。すみません。</p>
  </div>
</template>

<script>
import LastName from "./components/LastName.vue";

export default {
  data: function() {
    return {
      defaultAge: 15,
      emitData: 0,
      job: "家庭教師"
    }
  },
  components: {
    LastName: LastName
  }
}
</script>

結果
image.png

スロットプロパティ(デフォルトスロットのみ)

デフォルトスロットのみの場合、templateタグが不要(Vue.jsが勝手にtemplateタグを付与してくれる)。
その時のスロットプロパティは、単一コンポーネントに定義する。

LastName.vue
<template>
  <div>
    <!-- デフォルトスロット表示用 -->
    <slot :sei="sei" :mei="mei" :syokugyou="syokugyou"></slot>
  </div>
</template>

<script>
export default {
  props: {
    job: {
      type: String,
      required: false
    }
  },
  data: function() {
    return {
      sei: "山田",
      mei: "太郎",
      syokugyou: this.job
    }
  }
}
</script>
App.vue
<template>
  <div>
    <FirstName :your-age="defaultAge" gender="男性" @my-click="emitData = $event"></FirstName>

    <!-- デフォルトスロットのときのスロットプロパティの命名 -->
    <LastName :job="job" v-slot:default="slotProps">
    <!-- <LastName :job="job" v-slot="slotProps"> のようにdefalutは省略可-->

      <!-- templateがなくデフォルトスロットのみ -->
      <p>私は{{slotProps.sei}}{{slotProps.mei}}です</p>
      <p>職業は{{slotProps.syokugyou}}です</p>
    </LastName>
    <p>思っていた年齢{{defaultAge}}歳でしたが、{{emitData}}歳違っていたようでした。すみません。</p>
  </div>
</template>

<script>
import LastName from "./components/LastName.vue";

export default {
  data: function() {
    return {
      defaultAge: 15,
      emitData: 0,
      job: "広告業界"
    }
  },
  components: {
    LastName: LastName
  }
}
</script>

結果
image.png

v-slotの省略記法

#に置き換えることができる

<template v-slot:job="hoge">

<template #job="hoge">
このように省略して書ける。

ただし、defaultスロットについては省略はNG。

8-8. コンポーネント間のデータの受け渡し(子→親:$emit)

子→親の場合、$emitを使います。
元々、$emitは、カスタムイベントを作る時に使用される。
任意のタイミングで$emitを実行することで、親のVueコンポーネント(今回はApp.vue)でイベントを発火させる。
その発火する時に、データを付け加えて戻すような仕組み。
重要:子は親のデータを変更することはできない。あくまでも子からデータを返している。

なお、カスタムイベントはJavaScript内では使われない。
よって(htmlは大文字小文字を区別しないので)、ケバブケースで命名するのが良い。

FirstName.vue
<template>
  <div>
    <p>こんにちは{{name}}さん</p>
    <button @click="changeMethod">押した分だけ年齢増えます</button>
    <p>年齢:{{age}}</p>
    <p>性別:{{gender}}</p>
  </div>
</template>

<script>
export default {
  props: {
    yourAge: {
      type: Number,
      required: true
    },
    gender: {
      type: String,
      required: true
    }
  },
  data: function() {
    return {
      name: "次郎",
      age: this.yourAge
    }
  },
  methods:{
    changeMethod: function() {
      this.age += 1

      // 子→親への値受け渡し。
      // $emitで値を返す。正確いうと、親のイベントを発火させて、引数で渡す。
      // 第1引数:イベント名(ケバブケースで指定する)、第2引数:イベントで渡す値(任意)
      // 元々 $emit はカスタムイベントを作る時に使用。
      this.$emit("my-click", this.age - this.yourAge)
    }
  },
}
</script>

<style scoped>
div {
  border: 1px blue solid;
}
</style>
App.vue
<template>
  <div>
    <!-- v-onディレクティブでイベントを受け取る -->
    <FirstName :your-age="defaultAge" gender="男性" @my-click="emitData = $event"></FirstName>
    <LastName></LastName>
    <p>思っていた年齢{{defaultAge}}歳でしたが、{{emitData}}歳違っていたようでした。すみません。</p>
  </div>
</template>

<script>
import LastName from "./components/LastName.vue";

export default {
  data: function() {
    return {
      defaultAge: 15,
      emitData: 0
    }
  },
  components: {
    LastName: LastName
  }
}
</script>

結果
aaa11.gif

8-8. コンポーネントの動的切り替え

同じ場所に表示するコンポーネントを切り替える時には、componentタグを使う。
以下、新たに ParentコンポーネントとChildコンポーネントを定義し、切り替える。

Parent.vue
<template>
  <div>
    <p>親の山田一郎です</p>
  </div>
</template>
Child.vue
<template>
  <div>
    <p>子供の山田一太郎です</p>
  </div>
</template>
App.vue
<template>
  <div>
    <!-- クリックでコンポーネントを切り替える操作 -->
    <button @click="changeComponent('Parent')">押したら親の名前が表示されます</button>
    <br>
    <button @click="changeComponent('Child')">押したら子供の名前が表示されます</button>
    <hr>

    <!-- compenentタグで動的にVueコンポーネントを切り替える -->
    <component :is='currentComponents'></component>
  </div>
</template>

<script>
import Parent from "./components/Parent.vue";
import Child from "./components/Child.vue";

export default {
  data: function() {
    return {
      currentComponents: "Parent"
    }
  },
  components: {
    Parent: Parent,
    Child: Child
  },
  methods:{
    changeComponent: function(compName) {
        this.currentComponents = compName
    }
  },
}
</script>

結果
aaa12.gif

切り替え時のキャッシュ

なお、切り替えの度にVueインスタンスがdestroycreateを繰り返している。
インスタンスが消えてしまうため、dataが毎回削除(=初期化)されるということなので、注意が必要。

もしdestroyさせないためには(=キャッシュさせるためには)、keep-aliveタグを使う。

App.vue
<!-- keep-aliveタグを使ってキャッシュ -->
<keep-alive>
  <component :is='currentComponents'></component>
</keep-alive>

ただし、こうすると従来のライフサイクルメソッドのdestroyed()が使えなくなる。
その代わりに、deactivated()activated()が使えるようになる。

deactivated():該当コンポーネントが表示されなくなった時にコールされる
activated():該当コンポーネントが表示された時にコールされる

Parent.vue
<template>
  <div>
    <p>親の山田一郎です</p>
  </div>
</template>

<script>
export default {
  // keep-alive用 
  deactivated() {
    console.log("deactivated:非表示になりました")
  },
  // keep-alive用
  activated() {
    console.log("activated:表示になりました")
  }
}
</script>

8-9. コンポーネントでのv-model(propsとemit)

親コンポーネントにてv-modelを指定して、子コンポーネントでその値を反映するやり方は、
props$emitをを両方使う。

typeによって、子コンポーネントのinputタグの書き方が異なる。

テキストボックス

App.vue
<template>
  <div>
    <!-- v-modelの指定 -->
    <LastName v-model="defaultMsg"></LastName>
  </div>
</template>

<script>
import LastName from "./components/LastName.vue";

export default {
  data: function() {
    return {
      defaultAge: 15,
      defaultMsg: 'デフォルトメッセージ'
    }
  },
  components: {
    LastName: LastName
  }
}
</script>
LastName.vue
<template>
  <div>
    <p>私は{{name}}です</p>

    <!-- valueにpropsの値を反映させ、 -->
    <!-- $emitにinputメソッドを指定して発火させて親コンポーネントに反映させる -->
    <input
      id="title"
      type="text"
      :value="value"
      @input="$emit('input', $event.target.value)"
    >
    <pre>{{ value }}</pre>
  </div>
</template>

<script>
export default {
  props: {
    // v-modelの受け口として、valueという変数名で、propsを指定する
    value: {
      type: String,
      required: true
    }
  },
  data: function() {
    return {
      name: "山田",
    }
  }
}
</script>

結果
aaa20.gif

チェックボックス

App.vue
<template>
  <div>
    <!-- v-modelの指定 -->
    <LastName v-model="defaultCheck"></LastName>
  </div>
</template>

<script>
import LastName from "./components/LastName.vue";

export default {
  data: function() {
    return {
      defaultAge: 15,
      defaultCheck: true
    }
  },
  components: {
    LastName: LastName
  }
}
</script>
LastName.vue
<template>
  <div>
    <p>私は{{name}}です</p>

    <!-- チェックボックスのchangeイベントを利用する -->
    <!-- $emitにchangeメソッドを指定して発火させて親コンポーネントに反映させる -->
    <input
      type="checkbox"
      :checked="childChecked"
      @change="childChecked=$event.target.checked"
    >
    <pre>{{ childChecked }}</pre>
  </div>
</template>

<script>
export default {
  props: {
    // valueという変数名で、propsを指定する
    // チェックは、boolean
    value: {
      type: Boolean,
      required: true
    }
  },
  data: function() {
    return {
      name: "山田",
      childChecked: this.value
    }
  }
}
</script>

結果
aaa21.gif

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1