Vue CLIで作成したプロジェクトに、拡張子.vueの単一ファイルコンポーネントを追加して、それをコンポーネントとして呼び出す方法についてまとめています。
単一ファイルコンポーネントとは?
Vue.jsを使っていると、単一ファイルコンポーネントという言葉を頻繁に耳にします。
単一ファイルコンポーネントとは.vueというファイルの名称です。
単一ファイルコンポーネントでは、HTML, Vue.js(JavaScript), CSS(SASSなど)を一つのファイルにまとめて記述することができます。
通常はばらばらと個別に作成しなければいけないファイルを1つのファイル(単一のファイル)にまとめることができます。
作成した.vueファイルは一塊の要素(コンポーネント)として、好きな場所で呼び出すことができます。
このため、単一ファイルコンポーネントと呼びます。
コンポーネントの作成と読み込み手順
コンポーネントの作成と読み込みは、以下の4つのステップになります。
なお、1つのコンポーネントの中で複数のコンポーネントを呼び出したい場合も、同じ手順の繰り返しになります。(例:ヘッダーやフッターをコンポーネントとして作成し、読み込む場合)
componentsフォルダ配下に新規ファイルを作成する
ファイルの作成場所
まず、単一ファイルコンポーネント.vueファイルをsrcディレクトリの中の、componentsディレクトリ配下に作成します。
デフォルト状態では、componentsは以下のようになっています。
このディレクトリにファイルを追加します。
ファイルの命名規則
ファイル名はコンポーネントの命名規則「大文字始まり、キャメルケース(単語の冒頭を大文字でつなぐ)」のが一般的です。
例: User.vue や UserDetail.vueなど
ファイルの中身
ファイルの中には templateタグ、scriptタグ、styleタグで囲まれた場所を作成します。
以下が.vueファイルのデフォルトのテンプレートになります。
<template>
</template>
<script>
export default {
}
</script>
<style>
</style>
templateタグの中に、ブラウザに表示するHTMLのソースコードを記載します。
scriptタグの中がJavaScriptの記述、export defaultの中がVue.jsの記述になります。
styleタグの中がCSSスタイルを記述する場所です。指定方法によってはSASS(SCSS)も使用できます。
実例
例えば「UserDetail.vue」という単一ファイルコンポーネントを作成する場合は以下のようになります。
ここでは読み込みの手順を確認するために超シンプルなコードにします。(VueのコードやCSSを記述したものは別途解説します)
<template>
<div>
<h1 style="color: red">User Detailの単一ファイルコンポーネントです</h1>
</div>
</template>
<script>
export default {
}
</script>
<style>
</style>
.vueのデフォルトのテンプレートのtemplateタグの中に、divとh1を追加しただけです。
呼び出したいコンポーネントの中にファイルをインポート(import)する
続いて作成した.vueファイルを読み込みます。
作成したコンポーネントを呼び出したいファイルの中に、作成したファイルを変数名を指定してインポートします。
記述する領域はJavaScriptの領域です。(※scriptタグの中、export defaultの外)
import 変数名 from 'ファイルパス'
実例
Vue CLIのプロジェクトを生成したときに画面上に読み込まれている、HelloWorld.vueの中で、新たに作成したコンポーネントを読み込んでみます。
HelloWorld.vueを開いて、scriptタグの直下に以下を追記します。
import UserDetail from './UserDetail'
パスの部分は.vueを省略しています。「./UserDetail.vue」と同じです。「./」は現在のファイルと同じ階層であることを示す相対パスです。
全体的なコードは以下のようになります。
<template>
省略(デフォルトのまま)
</template>
<script>
import UserDetail from './UserDetail'
export default {
name: 'HelloWorld',
props: {
msg: String
}
}
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
省略(デフォルトのまま)
</style>
呼び出したいコンポーネントの中でcomponentとして定義する
.vueファイルをインポートしただけではコンポーネントとして使えません。
次に、Vueの領域の中で、読み込んだファイルをコンポーネントとして登録します。
export defaultの中に、componentsオブジェクトを作成し、その中にテンプレート名を記述します。
<script>
import 変数名 from 'ファイルパス'
export default {
components: {
変数名
}
}
</script>
実例
HelloWorld.vueのVue.jsの領域であるexport defaultでコンポーネント「UserDetail」を定義します。
<template>
省略(デフォルトのまま)
</template>
<script>
import UserDetail from './UserDetail'
export default {
name: 'HelloWorld',
props: {
msg: String
},
components: {
UserDetail
}
}
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
省略(デフォルトのまま)
</style>
templateの中でコンポーネントを呼び出す
コンポーネントの呼び出し方
最後の仕上げとして、登録したコンポーネントをtemplateの中で呼び出します。
呼び出す際は、定義したコンポーネント名をタグとして記述します。
<コンポーネント名 />
id, class, v-bind, v-model, v-on などを使う場合
呼び出すテンプレートに対して、id名や、クラス名、v-bindやv-model、クリックイベント(v-onや@)などを記載する場合は、コンポーネント名の後ろに属性として記述します。
<コンポーネント名
id="unique-id"
class="class-name"
:class="プロパティ名"
.... />
id名やclass名、:classの実例は後述しています。
実例
HelloWorld.vueのtemplateタグの上層部で定義したコンポーネント「UserDetail」を呼び出します。
<template>
<div class="hello">
<UserDetail />
省略(デフォルトのまま)
</div>
</template>
<script>
import UserDetail from './UserDetail'
export default {
name: 'HelloWorld',
props: {
msg: String
},
components: {
UserDetail
}
}
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
省略(デフォルトのまま)
</style>
以上でテンプレートの設定は完了です。
ブラウザでの呼び出し
components定義前のデフォルトの状態は以下のようになっています。(Vue CLIでプロジェクトを作成した状態)
↓ コンポーネント「UserDetail」を定義し呼び出した後
指定した場所に定義したコンポーネント「UserDetail」の内容が読み込まれているのがわかります。
単一ファイルコンポーネントの中でdataやCSSスタイルを設定する方法
単一ファイルコンポーネントを追加する方法がわかったところで、次に、.vueファイルを編集してdataやwatch, cssスタイルなどを追加します。
dataなどVue.jsの機能を使う方法
dataやmethodsなどVue.jsの機能を使うには、export defaultの中に記述します。export defaultの中はVue.jsの世界と考えてください。
例えば、上記のUserDetail.vueの中身を編集して、苗字と名前を入力すると、watchオブジェクトにより変更を検知して、フルネームを自動で返す処理は以下のようになります。
<template>
<div id="user-detail">
<h1>ユーザーの詳細ページです</h1>
<ul class="profile-list">
<li>苗字:<input type="text" v-model="lastName" ></li>
<li>名前:<input type="text" v-model="firstName" ></li>
<li>フルネーム:{{ fullName }}</li>
</ul>
</div>
</template>
<script>
export default {
data(){
return{
lastName: '',
firstName: '',
fullName: '',
}
},
watch:{
lastName: function(){
this.fullName = this.lastName + " " + this.firstName
},
firstName: function(){
this.fullName = this.firstName + " " + this.lastName
}
}
}
</script>
<style>
</style>
CSSスタイルを設定する
続いてCSSスタイルを設定します。スタイルはstyleタグの中に記述します。書き方はCSSファイルに記述するのと同じです。
スコープを設定する方法
styleタグの中に記述した場合だと、作成したスタイルがプロジェクト内の全てのテンプレートに反映されてしまいます。
これを、現在記述している.vueファイルのtemplateにのみ適用したい場合は、styleタグの後ろに scoped属性を記述します。
<style scoped>
</style>
SASSやSCSSを使う方法
SASSやSCSSの記述でスタイルを書くこともできます。
vue create
時にsassを使う設定にしていない場合は別途、sass-loaderとsassの2つのパッケージを別途インストールする必要があります。
lang属性を記述してその値を「scss」や「sass」とします。scoped属性と併用することもできます。
<style lang="scss" scoped>
</style>
実例
コンポーネント「UserDetail」のtemplateタグ内のid="user-detail"
とclass="profile-list"
に対してスタイルを設定します。
<template>
<div id="user-detail">
<h1>ユーザーの詳細ページです</h1>
<ul class="profile-list">
<li>苗字:<input type="text" v-model="lastName" ></li>
<li>名前:<input type="text" v-model="firstName" ></li>
<li>フルネーム:{{ fullName }}</li>
</ul>
</div>
</template>
<script>
export default {
data(){
return{
lastName: '',
firstName: '',
fullName: '',
}
},
watch:{
lastName: function(){
this.fullName = this.lastName + " " + this.firstName
},
firstName: function(){
this.fullName = this.firstName + " " + this.lastName
}
}
}
</script>
<style scoped>
#user-detail{
margin: 0 auto;
width: 800px;
border: 10px solid red;
}
.profile-list{
margin: 40px auto;
padding-left: 200px;
text-align: left;
}
</style>
以上で設定は完了です。
ブラウザの表示
ブラウザの表示は以下のようになります。
↓ 苗字と名前を入力
watchが機能してフルネームが自動で返されます。
コンポーネント読み込み時にidやclass、v-bindを指定する
コンポーネント読み込み時にidやclass、v-bindを指定することもできます。
コンポーネント名の後ろに、スペースを開け(あるいは改行して)、それぞれの属性と値を記述します。
<コンポーネント名
id="unique-id"
class="class-name"
:class="プロパティ名"
.... />
同様の手順でv-on(@)やv-modelも指定できます。
実例
例えば、HelloWorld.vueの中で「UserDetail」というコンポーネントを読み込み、コンポーネント読み込み時にidやclassを指定する場合は以下のようになります。
<template>
<div class="hello">
<UserDetail
id="testId"
class="testClass"
:class="originalClass"
/>
省略(デフォルトのまま)
</div>
</template>
<script>
import UserDetail from './UserDetail'
export default {
name: 'HelloWorld',
components: {
UserDetail
},
props: {
msg: String
},
data(){
return{
originalClass: 'my-class'
}
}
}
</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>
ブラウザの表示
コンパイル後のブラウザの表示は以下のようになります。
ソースコードを確認すると、以下のようにテンプレートを囲む最上位のタグに、指定したidやクラス名が入っているのがわかります。
<div id="user-detail" class="testClass my-class" data-v-b7412fa8="" data-v-469af010="">
<h1 data-v-b7412fa8="">ユーザーの詳細ページです</h1>
<ul class="profile-list" data-v-b7412fa8="">
<li data-v-b7412fa8="">苗字:<input type="text" data-v-b7412fa8=""></li>
<li data-v-b7412fa8="">名前:<input type="text" data-v-b7412fa8=""></li>
<li data-v-b7412fa8="">フルネーム:</li>
</ul>
<p data-v-b7412fa8=""></p>
</div>
親コンポーネントのデータを子コンポーネントに渡す方法
idやclass以外にも、親コンポーネントでdataの中に定義してあるプロパティ(変数)を子コンポーネントの中で使いたいときがあります。
親コンポーネントの中の記述
その場合は、親コンポーネントの中の子コンポーネントタグの中に以下を記述します。
<子コンポーネント名 :渡すプロパティ名="プロパティ名" />
改行もできます。複数設置する場合は改行するのが一般的です。
<子コンポーネント名
:渡すプロパティ名1="プロパティ名1"
:渡すプロパティ名2="プロパティ名2"
:渡すプロパティ名3="プロパティ名3"
,,, />
実例
例えば、dataで「xxx」という名前で定義しているデータを子テンプレート「UserDetail」に「xxx」という名前で渡す場合は以下のようになります。
(※データの値と渡すプロパティ名は同じである必要はありません。)
<template>
<div class="hello">
<UserDetail
:xxx="xxx"
/>
<div>xxxの値:{{xxx}}</div>
</div>
</template>
<script>
import UserDetail from './UserDetail'
export default {
name: 'HelloWorld',
components: {
UserDetail
},
data(){
return{
xxx: "secret"
}
}
</script>
<style scoped>
省略
</style>
子コンポーネントの中の記述
子コンポーネントでは渡されたデータを使えるようにするためにpropsオブジェクトの中でそのデータを指定する必要があります。
<script>
export default {
props:{
渡されたプロパティ名: 型
},
(省略)
}
</script>
実例
親コンポーネントから「:xxx=”プロパティ名”」として渡されたデータを、子コンポーネントで受け取り、型で文字列を指定する場合は以下のようになります。
<template>
<div id="user-detail">
<h1>ユーザーの詳細ページです</h1>
<ul class="profile-list">
<li>親コンポーネントから受け取ったデータ: {{ xxx }} </li>
</ul>
<p></p>
</div>
</template>
<script>
export default {
props:{
xxx: {type: String}
}
}
</script>
<style scoped>
省略
</style>
propsで定義したことにより、templateタグの中でプロパティとして呼び出すことができるようになります。
<li>親コンポーネントから受け取ったデータ: {{ xxx }} </li>
親子間のデータの受け渡し
上記は親コンポーネントのデータを子コンポーネントに渡すだけでしたが、状況によっては、コンポーネントの中で、親コンポーネントから受け取ったデータを変更したい場合があります。
そのときは、propsで定義したプロパティを、v-modelで編集したくなりますが、これをするとエラーが発生します。
親子間でのデータの受け渡しには、computedプロパティを使ってゲッターとセッターをセットするといった処理が必要になります。
詳細については下記をご参考ください。
【Vue】propsのデータを変更する方法|Avoid mutating a prop directlyエラーの発生原因と対処法